Workshop UserControls Part 3: Events im Griff – Mouse- und Focus-Events sinnvoll implementiert
28. August 2009 von Werner Mager · Gelesen: 3906 · heute: 1Im dritten Teil werde ich noch tiefer auf Events eingehen. Dabei stehen die Mouse- und Focus-Events im Vordergrund, da diese Events sich zunächst nicht wie erwartet verhalten. Dabei können die hier vorgestellten Verfahren aber auch auf andere Events übertragen werden. Als praktisches Beispiel wird das UserControl “SomeButton” aus den vorherigen Artikeln um ein Highlighting bei Mausberührung und Fokuserhalt erweitert.
Ein echtes Highlight…
Das Highlighting das in diesem Artikel realisiert werden soll, hebt unser UserControl bei Mausberührung und Fokuserhalt hervor:

Um dies zu realisieren werden im folgenden zunächst die Mouse-Events behandelt.
Mouse-Events
Unser Usercontrol hat bereits Mouse-Events, die es von der Klasse ContainerControl geerbt hat. Diese beziehen sich jedoch nur auf die Fläche des Controls, die nicht durch untergeordnete Controls belegt ist. In unserem Beispiel erhalten wir kein MouseMove-Event, wenn sich die Maus über den farbigen Panels befindet. Um dieses Verhalten zu erreichen, müssen von allen untergeordneten Controls die gewünschten Events eingesammelt und auf unser Control übertragen werden. Dies geschieht, in dem im Eventhandler die zugehörige Funktion (gleicher Name wie Event, nur mit „On“) aufgerufen wird:
Public panel1_MouseMove(Object sender, EventArgs e){ OnMouseMove(e); }
Sonderfall MouseLeave und MouseEnter
Nicht ganz unproblematisch sind hierbei jedoch die MouseLeave und MouseEnter Events. Dadurch dass sich die untergeordneten Controls innerhalb unseres UserControls befinden, führt eine Mausbewegung von z.B. dem Panel ausgehend hin zu unbelegtem Bereich im Control zu einer Folge von MouseLeave- und MouseEnter-Events, obwohl die Maus nie unser UserControl verlassen hat.
Focus-Behandlung
Auch bei der Behandlung der Focus-Events kann es zu unerwünschten Effekten kommen. Ein nicht ganz einfacher Fall ist z.B. wenn das Contrrol untergeordnete Controls besitzt, die selbst einen Focus erhalten können, wie z.B. Textboxen. Erhält diese den Focus, so verliert das UserControl selbst den Fokus, obwohl sich dieser ja noch innerhalb des Controls befindet (nur eben auf einem untergeordneten Control). Um dieses Problem zu vermeiden, sollte für alle untergeordneten UserControls die Eigenschaft „enabled“ auf false gesetzt werden, wenn diese keine Interaktion mit dem User durchführen müssen.
Praktisches Beispiel
An Hand von dem schon in den vorherigen Artikeln vorgestelltem UserControl soll nun die vorgestellte Problematik veranschaulicht werden. Damit man auch was sieht, wird dazu zunächst eine Eigenschaft „Highlighted“ zugefügt, die die Schrift auf „Fett“ stellt. Zusätzlich erhält die Property ein Browsable-Attribut mit dem Parameter false, so dass die Property nicht im Visual Studio Designer angezeigt wird.
bool highlight = false; [Browsable(false)] public bool Highlight { get { return highlight; } set { highlight = value; this.Font = new Font(this.Font,highlight?FontStyle.Bold:FontStyle.Regular); } }
Diese Eigenschaft soll nun automatisch gesetzt werden, wenn die Maus über unser UserControl bewegt wird oder wenn das Control den Fokus erhält. Dazu werden zunächst im Designer die Events MouseEnter, MouseLeave, Enter und Leave aboniert und wie folgt implementiert:
void RegisterEvents(Control parent) { foreach (Control child in parent.Controls) { child.Click += new System.EventHandler(child_Click); child.MouseMove += new MouseEventHandler(child_MouseMove); child.MouseEnter += new EventHandler(child_MouseEnter); child.MouseLeave += new EventHandler(child_MouseLeave); RegisterEvents(child); } } void child_MouseLeave(object sender, EventArgs e) { OnMouseLeave(e); } void child_MouseEnter(object sender, EventArgs e) { OnMouseEnter(e); } void child_MouseMove(object sender, MouseEventArgs e) { OnMouseMove(e); } void child_Click(object sender, System.EventArgs e) { OnClick(e); }
Beim Ausführen des Codes wird deutlich, dass die Events nur auf die Hintergrundfläche des Controls reagieren. Bewegt man die Maus in den Grenzbereich zum farbigen Rechteck, kommt es sogar zu einem sehr unschönen Flimmern.
MouseEnter und MouseLeave: Auf richtige Verschaltung kommt es an
Wie schon erwähnt wird für die MouseEnter und MouseLeave Events eine Sonderbehandlung benötigt. Diese wird durch ein Überschreiben der OnMouseEnter und OnMouseLeave Methoden realisiert. In diesen muss eine Zustandsvariable eingeführt und die Mausposition muss ausgewertet werden. In der Zustandsvariablen wird gespeichert, ob sich die Maus bereits innerhalb des Controls befindet. In diesem Fall wird kein neuer MouseEnter-Event ausgelöst. Beim MouseLeave wird die Mausposition überprüft. Nur wenn sich die Maus tatsächlich außerhalb des Controls befindet, wird der MouseLeave Event ausgeführt:
// This flag indicates if the mouse was inside the control bool inside = false; protected override void OnMouseEnter(EventArgs e) { // Check if mouse was already in control. if (!inside) { inside = true; base.OnMouseEnter(e); } } protected override void OnMouseLeave(EventArgs e) { // Check mouse coordinates Point mouseCoordinates = this.Parent.PointToClient(MousePosition); // Call base method only if mouse really left control if (!this.Bounds.Contains(mouseCoordinates)) { inside = false; base.OnMouseLeave(e); } }
Zu guter Letzt
Mit dem dritten Teil endet mein Workshop über Usercontrols zunächst. Das heißt aber nicht, dass ich nicht noch mehr coole Ideen und Tricks zum Thema UserControls habe. Die verrate ich aber nur, wenn ich auch mal nen Kommentar zu meinen Workshops bekomme
. Also haut in die Tasten und schreibt was Ihr denkt oder zu was Ihr gerne noch mehr lesen würdet.
So, und wie immer gibt es an dieser Stelle das komplette Projekt zum Download:
04. September 2009 um 22:19
[…] guter Letzt Hier endet nun auch der zweite Teil des UserControl-Workshops. Im dritten Teil werde ich noch tiefer auf Events eingehen, insbesondere werde ich die MouseEnter, MouseLeave und […]
13. Januar 2010 um 16:00
Frage:
Wie kann man auf einfache Weise UCs lokalisieren?
Geht das nur indem man “per Hand” den einzelnen Controls den entsprechenden lokalisierten Content zur Laufzeit zuweist, oder habe ich u.U. einen viel einfacheren Weg übersehen - in der Art, wie man z.B. eine Form und ihre Controls im Designer lokalisiert.
13. Januar 2010 um 18:11
Hallo Bernd,
das Lokalisieren eines UserControls ist eigentlich ganz einfach. Zunächst muß die Eigenschaft “Localizable” im Designer auf true gesetzt werden. Nun wähle die gewünschte Sprache aus und ändere die Eigenschaften wie gewünscht. Z.B. Ändere den Text eines Buttons oder sogar seine Position und Größe. Properties, die du selbst zufügst, mußt du mit [Localizable(true)] kennzeichnen, wenn der Benutzer deines UserControls die Property später lokaliseren können soll (siehe Text-Property aus dem Artikel).
Nun kompiliere das ganze und füge dein UserControl einem Form zu. Dieses muß natürlich auch “Localizable” auf true haben, damit es von den Sprachen in deinem Control gebrauch machen kann. Leider siehst du die Lokaliserung nicht im Designer, sondern erst wenn du die Anwendung ausführst. Du kannst z.B. in der “Program.cs” die Sprache umschalten.
Ich habe dir mal ein kleines Beispiel geschrieben:
http://blog.sternico.de/wp-content/uploads/2010/01/uc_local.zip
Weitere Fragen beantworte ich gerne
Schöne Grüße
Werner