[C#] TextBox im UserPaint Stil, weis beim hineinklicken

FBIagent

Erfahrenes Mitglied
Guten Tag,

ich habe mir eine Erweiterung für die normale TextBox aus .NET geschrieben, die es mir erlaubt
einen Text in der Mitte des Controls zu rendern, falls die TextBox keinen Text enthält.

Es funktioniert auch alles prima, nur hat die Erweiterung einen kleinen Schönheitsfehler:
Sobalt in der TextBox kein Text mehr enthalten ist und in den UserPaint style gewechselt wird,
wird der Hintergrund in weis gerendert wenn ich auf die Textfläche klicke. Sobald ich den bereich
des Textes mit dem Mauszeiger wieder verlasse ist wieder alles in Ordnung.

Dabei ist zu sagen, das nicht der gedammte Hintergrund in weis gerendert wird(siehe Anhang).

C#:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;

namespace TS3QueryClient
{
	public partial class TextBoxEx : TextBox
	{
		[Localizable(true)]
		[Bindable(true)]
		[Description("Text displayed when the text box is empty")]
		[Category("Appearance")]
		[Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
		public String HintText
		{
			get { return _hintText; }
			set { _hintText = value; Invalidate(); }
		}

		[Description("Color of HintText")]
		[Category("Appearance")]
		public Color HintTextColor
		{
			get { return _hintTextBrush.Color; }
			set { _hintTextBrush = new SolidBrush(value); Invalidate(); }
		}

		private String _hintText = "";
		private SolidBrush _hintTextBrush = new SolidBrush(Color.Silver);
		private SolidBrush _backBrush = null;
		private Font _oldFont = null;

		public TextBoxEx()
		{
			TextChanged += TextChangedFunc;
		}

		protected override void OnCreateControl()
		{
			base.OnCreateControl();
			_backBrush = new SolidBrush(BackColor);
			_oldFont = Font;
			TextChangedFunc(null, null);
		}

		protected override void OnFontChanged(EventArgs e)
		{
			base.OnFontChanged(e);
			_oldFont = Font;
		}

		protected override void OnPaintBackground(PaintEventArgs pevent)
		{
			if (_backBrush.Color != BackColor)
				_backBrush.Color = BackColor;

			pevent.Graphics.FillRectangle(_backBrush, ClientRectangle);
		}

		protected override void OnPaint(PaintEventArgs pevent)
		{
			// draw hint for the textbox if it is empty (center/center)
			SizeF backSize = pevent.Graphics.MeasureString(_hintText, Font);
			PointF drawPt = new PointF(ClientSize.Width / 2 - backSize.Width / 2, ClientSize.Height / 2 - backSize.Height / 2);
			pevent.Graphics.DrawString(_hintText, Font, _hintTextBrush, drawPt);
		}

		private void TextChangedFunc(object sender, EventArgs e)
		{
			if (TextLength == 0)
			{
				SetStyle(ControlStyles.UserPaint, true);
			}
			else
			{
				SetStyle(ControlStyles.UserPaint, false);
				// in some cases without this the text will be rendered in system default font
				Font = _oldFont;
			}

			Invalidate();
		}
	}
}

Könnt ihr mir einen Tipp geben wie ich das unterbinden könnte?

Grüße,
FBIagent
 

Anhänge

  • textboxex.png
    textboxex.png
    24,3 KB · Aufrufe: 68
Hallo,

wenn du den Text in der Mitte haben willst, warum nicht:
C#:
textBox1.TextAlign = HorizontalAlignment.Center;
 
Hallo,

wenn du den Text in der Mitte haben willst, warum nicht:
C#:
textBox1.TextAlign = HorizontalAlignment.Center;

Wenn ich dem Control nun den Focus gebe, dann ist der Cursor auch zwingend in der Mitte.

1. Ich klicke rein
entwerder ich lasse nun den text align auf center, unschön: cursor kann nicht links und rechts sein wie vom designer vieleicht verlangt
ODER
ich ändere nun den text align auf dem vom designer gewünschten wert, unschön: hintergrundtext wird ggf. nicht in
der mitte angezeigt, da text align vom designer auch links bzw. rechts angegeben sein kann.

Sprich: Ich brauche das eigene Rendering um dies unabhängig laufen zu lassen, damit die kleinen Schönheitsfehler
nicht auftreten.

Desweiteren müsste ich, wenn ich die Text Eigenschaft benutzen würde dafür sorgen das ich den Text leere sobald etwas eingetippt wird und auch wieder den hintergrundtext wiederherstelle wenn die box leer ist.
 
Zuletzt bearbeitet:
Bei mir tritt der Fehler genau dann auf, wenn
  • die TextBoxEx bereits den Fokus hat
  • die linke Maustaste runtergedrükt wird
  • die Text-Eigenschaft leer ist
Sobald die Maus aus der TextBoxEx hinaus bewegt wird, wird die Textbox wieder korrekt gezeichnet.
Ist das bei dir auch so FBIagent? D.h. wenn die TextBoxEx keinen Fokus besitzt und dann mit links auf sie geklickt wird (und sie dadurch den Fokus erhält), dann ist das Rendering wie gewünscht. Hm seltsam. *grübel*
EDIT:
Nach ein bisschen Herumprobieren kann ich nur Folgendes anbieten:
C#:
protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    Invalidate();
}

protected override void OnMouseMove(MouseEventArgs e)
{
    base.OnMouseMove(e);
    Invalidate();
}
Das flackert aber manchmal.
EDIT 2:
Es reicht das Invalidate beim MouseMove nur dann aufzurufen, wenn die linke Maustaste auch tatsächlich gedrückt ist:
C#:
protected override void OnMouseMove(MouseEventArgs e)
{
    base.OnMouseMove(e);
    if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left) Invalidate();
}
 
Zuletzt bearbeitet:
Bei mir tritt der Fehler genau dann auf, wenn
  • die TextBoxEx bereits den Fokus hat
  • die linke Maustaste runtergedrükt wird
  • die Text-Eigenschaft leer ist
Sobald die Maus aus der TextBoxEx hinaus bewegt wird, wird die Textbox wieder korrekt gezeichnet.
Ist das bei dir auch so FBIagent? D.h. wenn die TextBoxEx keinen Fokus besitzt und dann mit links auf sie geklickt wird (und sie dadurch den Fokus erhält), dann ist das Rendering wie gewünscht. Hm seltsam. *grübel*
EDIT:
Nach ein bisschen Herumprobieren kann ich nur Folgendes anbieten:
C#:
protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    Invalidate();
}

protected override void OnMouseMove(MouseEventArgs e)
{
    base.OnMouseMove(e);
    Invalidate();
}
Das flackert aber manchmal.
EDIT 2:
Es reicht das Invalidate beim MouseMove nur dann aufzurufen, wenn die linke Maustaste auch tatsächlich gedrückt ist:
C#:
protected override void OnMouseMove(MouseEventArgs e)
{
    base.OnMouseMove(e);
    if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left) Invalidate();
}

Hm... in diese Richtung bin ich noch garnicht gegangen.

Ich habe gleich mal geschaut welche Window Messages das Control bekommt wenn ich darauf klicke.
Bei bereits vorhandenem Fokus wäre das OnMouseDown(WM_LBUTTONDOWN). Wenn ich nun die linke Maustaste unten lasse, dann bekommt das Control geschätzt alle Sekunde ein
OnMouseMove(WM_MOUSEMOVE). Das dekt sich damit wenn ich nur MouseDown überschreibe und dort
ein Refresh mache nach geschätzter Sekunde der Hintergrund weis wird.

Das löst das Problem auf jeden fall erstmal für weiteres.

Danke für deine Hilfe, nun muss ich mal in Erfahrung bringen was die Mausmeldungen genau machen,
wird bestimmt was mit dem TextBox Control selbst zusammen hängen, irgendein spieziellen Fall den man
beim UserPaint berücksichtigen muss.

EDIT:
Es scheint so zu sein das sich eine TextBox unter bestimmten Umständen nicht über die Paint-Methoden neuzeichnet.
Das besagt zumindest die angenommene Antwort in diesem Beitrag:
http://stackoverflow.com/questions/4360301/setting-background-image-on-textbox-in-c
 
Zuletzt bearbeitet:
Es scheint so zu sein das sich eine TextBox unter bestimmten Umständen nicht über die Paint-Methoden neuzeichnet.
Hm, das ist ja blöd.
Bist du mit deiner Anwendung schon weit fortgeschritten oder steckt sie noch im Anfangsstadium? Mit WPF wäre es sehr einfach so eine Textbox zu bauen, wie du sie dir wünscht. (Ich will hiermit ein bisschen Werbung für WPF machen.) Hier gibt es ein Beispiel für eine ComboBox.
Du könntest theoretisch auch dein TextBoxEx-Control mit WPF erstellen und in einer WinForms-Anwendung verwenden, wie in diesem Artikel beschrieben. Allerdings würde ich mir dann überlegen eventuell die gesamte Anwendung mit WPF zu realisieren.
Wenn du aber noch nie mit WPF gearbeitet hast (und du deine Anwendung zügig fertigstellen willst), dann lohnt sich die Einarbeitung wahrscheinlich nicht und ich würde mit dem Flackern leben.
 
Zuletzt bearbeitet:
Hm, das ist ja blöd.
Bist du mit deiner Anwendung schon weit fortgeschritten oder steckt sie noch im Anfangsstadium? Mit WPF wäre es sehr einfach so eine Textbox zu bauen, wie du sie dir wünscht. (Ich will hiermit ein bisschen Werbung für WPF machen.) Hier gibt es ein Beispiel für eine ComboBox.
Du könntest theoretisch auch dein TextBoxEx-Control mit WPF erstellen und in einer WinForms-Anwendung verwenden, wie in diesem Artikel beschrieben. Allerdings würde ich mir dann überlegen eventuell die gesamte Anwendung mit WPF zu realisieren.
Wenn du aber noch nie mit WPF gearbeitet hast (und du deine Anwendung zügig fertigstellen willst), dann lohnt sich die Einarbeitung wahrscheinlich nicht und ich würde mit dem Flackern leben.

In WPF wollte ich mich nun nicht einarbeiten. Vieleicht bekomme ich das Flackern ja noch irgendwie in
den Griff.

Die TextBoxEx möchte ich vor allendingen benutzen, da ich meine erste Lokalisierte Anwendung in .NET
und C# erstelle. Da die Textlänge je nach Sprache ja verschieden lang sein kann wollte ich die Bedeutung
der einzelnen Textboxen nicht davor schreiben. Und die Bedeutung über der Textbox sieht auch nicht sehr
schön aus, da ist das mit dem Text in der Textbox schon viel kompakter und übersichtlicher in der UI.
 
Zuletzt bearbeitet:
Hoi FBIagent

Flackern kommt ja in der Regel davon, dass du das Aufbauen des Bildes mit dem Auge mitverfolgen kannst. In der Regel verhinder Doublebuffering dies. Folgede Änderungen erzeugen bei mir eine flackerfreie Textbox mit dem entsprechenden Overlay:
C#:
...
        private void TextChangedFunc(object sender, EventArgs e)
        {
            if (TextLength == 0)
            {
                if (!GetStyle(ControlStyles.UserPaint))
                {
                    SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
                    Invalidate();
                }
            }
            else
            {
                if (GetStyle(ControlStyles.UserPaint))
                {
                    SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, false);
                    // in some cases without this the text will be rendered in system default font
                    Font = _oldFont;
                    Invalidate();
                }
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left && TextLength == 0) Invalidate();
        }
 

Neue Beiträge

Zurück