Der wichtigste Kniff, der in diesem Artikel eingesetzt wird, lässt sich mit einem Stichwort zusammenfassen: selbstzeichnend. Die Steuerelemente, die erstellt werden, sind einfache Ableitungen von UserControl. Das Ereignis Paint wird abgefangen und durch eine eigene Zeichenmethode ersetzt. Das Control zeichnet sich also im wahrsten Sinne des Wortes selbst.
Projekt 1: Schaltfläche
Genug der Vorrede, es geht direkt an die Implementierung. Erstellen Sie zunächst ein neues Projekt in Visual Studio .NET (egal ob 2002 oder 2003). Als Projekttyp wird Windows-Steuerelementbibliothek verwendet. Als Programmiersprache kam diesmal C# zum Zuge (wie üblich als Folge eines Münzwurfs). Mit Visual Basic .NET lässt sich das Beispiel selbstredend analog realisieren.Als erstes sollte die standardmäßig angelegte Klasse umbenannt werden: UserControl1 ist wahrlich kein selbsterklärender Name. Stattdessen verwenden wir SelbstzeichnendButton. Die Umbenennung können Sie in der Klassenansicht des Projekts vornehmen.
Dann kommen wir aber auch schon zum Hauptpunkt der Anwendung: Das Ereignis Paint muss abgefangen werden. Dazu wird die Methode OnPaint entweder direkt überschrieben oder eine neue Behandlungsmethode für das Paint-Ereignis angelegt. Der zweite Weg wird im Folgenden umgesetzt. Wählen Sie dazu das Control in Visual Studio .NET aus, schalten Sie im Eigenschaftenfenster auf die Ereignis-Ansicht und klicken Sie doppelt auf das leere, weiße Feld neben Paint. Visual Studio springt automatisch in die Code-Ansicht und hat eine entsprechende Methode angelegt:
private void SelbstzeichnendButton_Paint(object sender, System.Windows.Forms.PaintEventArgs e){}
this.Paint += new System.Windows.Forms.PaintEventHandler(this.UserControl1_Paint);
int breite = this.Width;int hoehe = this.Height;
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();gp.AddLine(5, 0, breite - 6, 0);gp.AddLine(breite - 1, 5, breite - 1, hoehe - 6);gp.AddLine(breite - 6, hoehe - 1, 5, hoehe - 1);gp.AddLine(0, hoehe - 6, 0, 5);gp.CloseFigure();
e.Graphics.FillPath(new SolidBrush(Color.Orange),gp);
public bool aktiv = false;
if (aktiv){e.Graphics.DrawPath(new Pen(Color.Black, 3),gp);}
string text = (aktiv) ? "ein" : "aus";
Font f = new Font("Verdana", 16);float x = (breite - e.Graphics.MeasureString(text, f).Width) / 2;float y = (hoehe - e.Graphics.MeasureString(text, f).Height) / 2;e.Graphics.DrawString(text, f, Brushes.Wheat, x - 1, y - 1);
private void SelbstzeichnendButton_Click(object sender, System.EventArgs e){aktiv = !aktiv;this.Invalidate();}
private void SelbstzeichnendButton_MouseEnter(object sender, System.EventArgs e){this.Cursor = Cursors.Hand;}private void SelbstzeichnendButton_MouseLeave(object sender, System.EventArgs e){this.Cursor = Cursors.Default;}
Projekt 2: Der Test
Das ganze soll jetzt natürlich auch sofort in einem Visual Studio .NET-Projekt getestet werden. Fügen Sie also in der Projektmappe eine neue Windows-Applikation hinzu, machen Sie diese zum Standardprojekt und ziehen Sie aus der Toolbox das gerade erstellte Control auf das Formular (siehe Abb. 2). Wie Ihnen vielleicht zuvor schon aufgefallen ist, wurde die Variable aktiv als public deklariert. Sinn und Zweck des Ganzen: Damit kann der Zustand der Schaltfläche (oder des Radio Buttons oder der Checkbox, je nach Layout) abgefragt werden. Genau das soll auch hier getan werden. Periodisch soll dieser Zustand überprüft und ausgegeben werden. Erstellen Sie dazu ein Label-Element namens label1. In diesem wird der Zustand periodisch ausgegeben.
private static System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void Form1_Load(object sender, System.EventArgs e){t.Tick += new EventHandler(Form1_UpdateZustand);t.Interval = 100;t.Start();}
private void Form1_UpdateZustand(Object o, EventArgs e){label1.Text = "Zustand: " + selbstzeichnendButton1.aktiv;}
Gutes und schlechtes UI-Design
Jakob Nielsen ist als Usability-Kritiker gefürchtet. Einer seiner bekanntesten Meinungsäußerungen ist sein legendärer Artikel Flash: 99% Bad, der online unter http://www.useit.com/alertbox/20001029.html verfügbar ist. Als einen der Hauptkritikpunkte an der Flash-Technologie sieht er die grafikverliebten Designer, die für jede neue Flash-Anwendung eigene Schaltflächen erstellen und dabei außer Acht lassen, dass nicht jeder Nutzer das für ebenso selbstverständlich und intuitiv bedienbar hält wie der Flasher selbst.Diese mahnenden Worte gelten natürlich für jede Art von Applikation mit Benutzeroberfläche. Also: Aller Liebe zu einem schmissigen Design zum Trotze: Übertreiben Sie es nicht und lassen Sie Unbeteiligte, Unvoreingenommene und Unvorgepictureete Ihr Design testen. Nur wenn diese Personen gut mit Ihren Schaltflächen und selbst erstellten Elementen zurecht kommen, schafft es wohl auch die breite Masse der Benutzer.
Listing 1 - Quellcode für das Steuerelement (Auszug)
using System;using System.Collections;using System.ComponentModel;using System.Drawing;using System.Data;using System.Windows.Forms;namespace DotnetmagazinSelbstzeichnend{public class SelbstzeichnendButton : System.Windows.Forms.UserControl{private System.ComponentModel.Container components = null;public bool aktiv = false;public SelbstzeichnendButton(){InitializeComponent();}protected override void Dispose( bool disposing ){// ...}private void InitializeComponent(){this.Name = "SelbstzeichnendButton";this.Click += new System.EventHandler(this.SelbstzeichnendButton_Click);this.Paint += new System.Windows.Forms.PaintEventHandler(this.SelbstzeichnendButton_Paint);this.MouseEnter += new System.EventHandler(this.SelbstzeichnendButton_MouseEnter);this.MouseLeave += new System.EventHandler(this.SelbstzeichnendButton_MouseLeave);}private void SelbstzeichnendButton_Paint(object sender, System.Windows.Forms.PaintEventArgs e){int breite = this.Width;int hoehe = this.Height;System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();gp.AddLine(5, 0, breite - 6, 0);gp.AddLine(breite - 1, 5, breite - 1, hoehe - 6);gp.AddLine(breite - 6, hoehe - 1, 5, hoehe - 1);gp.AddLine(0, hoehe - 6, 0, 5);gp.CloseFigure();e.Graphics.FillPath(new SolidBrush(Color.Orange),gp);if (aktiv){e.Graphics.DrawPath(new Pen(Color.Black, 3),gp);}string text = (aktiv) ? "ein" : "aus";Font f = new Font("Verdana", 16);float x = (breite - e.Graphics.MeasureString(text, f).Width) / 2;float y = (hoehe - e.Graphics.MeasureString(text, f).Height) / 2;e.Graphics.DrawString(text, f, Brushes.Wheat, x - 1, y - 1);}private void SelbstzeichnendButton_Click(object sender, System.EventArgs e){aktiv = !aktiv;this.Invalidate();}private void SelbstzeichnendButton_MouseEnter(object sender, System.EventArgs e){this.Cursor = Cursors.Hand;}private void SelbstzeichnendButton_MouseLeave(object sender, System.EventArgs e){this.Cursor = Cursors.Default;}}}
using System;using System.Drawing;using System.Collections;using System.ComponentModel;using System.Windows.Forms;using System.Data;namespace DotnetmagazinSelbstzeichnendDemo{public class Form1 : System.Windows.Forms.Form{private DotnetmagazinSelbstzeichnend.SelbstzeichnendButton selbstzeichnendButton1;private System.Windows.Forms.Label label1;private System.ComponentModel.Container components = null;private static System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();public Form1(){InitializeComponent();}protected override void Dispose( bool disposing ){// ...}private void InitializeComponent(){// ...}[STAThread]static void Main(){Application.Run(new Form1());}private void Form1_UpdateZustand(Object o, EventArgs e){label1.Text = "Zustand: " + selbstzeichnendButton1.aktiv;}private void Form1_Load(object sender, System.EventArgs e){t.Tick += new EventHandler(Form1_UpdateZustand);t.Interval = 100;t.Start();}}}


