BigBastis Blog About Me & my Digital Lifestyle

24Nov/101

C#: ++/– Operator auf eigene Klassen anwenden

Ihr habt den ++/-- (inkrement/dekrement) Operator bestimmt schon tausende Male eingesetzt ohne viel darüber nachzudenken, genau wie ich, doch heute ist mir schlagartig bewusst geworden, dass dieser Operator auch ein paar von meinen Klassen gut stehen würde und die Anwendung und die Lesbarkeit des Codes verbessern würde.

Die Umsetzung gestaltet sich hier zum Glück sehr einfach:


class Program {
    static void Main(string[] args) {

        Person a = new Person("Alf", 10);

        ++a;

        Console.WriteLine(a.ToString());    //Ausgabe: Alf - 11

        a++;

        Console.WriteLine(a.ToString());    //Ausgabe: Alf - 12

        a--;

        Console.WriteLine(a.ToString());    //Ausgabe: Alf - 11

        --a;

        Console.WriteLine(a.ToString());    //Ausgabe: Alf - 10

        Console.ReadLine();
    }
}

public class Person {
    int Alter { get; set; }
    string Name { get; set; }

    public Person(string Name, int Alter) {
        this.Alter = Alter;
        this.Name = Name;
    }

    public static Person operator ++(Person p){
        p.Alter++;
        return p;
    }

    public static Person operator --(Person p) {
        p.Alter--;
        return p;
    }

    public override string ToString(){
        return this.Name + " - " + this.Alter.ToString();
    }
}

Es muss lediglich eine statische Methode in die Klasse eingebunden werden, deren Rückgabewert ebenfalls der Klassentyp ist. Desweiteren folgt vor dem Namen der Funktion das Stichwort "operator" und es muss auch noch ein Parameter vom Typ der Klasse übergeben werden.

Innerhalb dieser Methode können wir nun entscheiden, was passieren soll wenn der Operator benutzt wird. In meinem Fall habe ich mich dazu entschieden, das Alter der Person um eins zu erhöhen, aber der Fantasie sind hier natürlich keine Grenzen gesetzt.

Natürlich kann man auch weitere Operatoren auf diese Weise implementieren, wie zum Beispiel den +/- (Plus/Minus) Operator, dazu gibt es ein nettes Beispiel im MSDN.

Eine feine Sache wie ich finde, nicht kompliziert - kann aber helfen den Code verständlicher und einfacher zu gestalten.

12Nov/101

ASP.NET MVC3: Remote Attribut “richtig” nutzen

Seit gestern probiere ich die neuen Attribute in dem ASP.NET MVC3 RC aus und bin eigentlich sehr zufrieden. Nur ein Attribut wollte bisher einfach nicht funktionieren, nämlich das Remote-Attribut.

In den offiziellen Release Notes ist dieses Attribut beschrieben und es wird folgendes Beispiel benutzt um es dem Leser näher zubringen:

image

Dies habe ich auch als Vorlage genommen und meine Methode im Controller implementiert:

image

Doch das hat nicht funktioniert. Nun weiß ich auch wieso, denn hier wird ein falsches Ergebnis zurück gesendet. Das Ergebnis dieser Methode liefert ein “False” oder “True” ab, mit einem großen Buchstaben am Anfang – und das macht uns alles kaputt!

Hier ist unser Request den jQuery für uns erzeugt:

image

Dieser Request kommt erfolgreich auf dem Server an und wird korrekt verarbeitet. Als Response kommt dann folgendes zurück:

image

Man beachte hier das großgeschriebene “False”. Also arbeitet der Controller völlig korrekt, nur müsste er ein kleines “false” zurückgeben.

Nun wenn man es Quick & Dirty haben will kann man einfach einen String zurückgeben:

image

Der bessere Weg wäre wohl ein JSON Result:

image

Nun funktioniert auch alles wie es soll und ich kann ruhig schlafen Smiley

11Nov/101

ASP.NET MVC3: Was gibts neues?

Einige von euch haben es sicher mitbekommen, dass vor Kurzem der Release Candidate (RC) von ASP.NET MVC3 veröffentlich wurde. Download, Changelog. Diese neue Version bringt einige für uns Entwickler tolle neue Funktionen und Vereinfachungen mit.

Die wohl auffälligste Neuerung ist die neue ViewEngine namens Razor, diese sorgt dafür, dass die Views um einiges schlanker und lesbarer werden durch die Einführung einer neuen Syntax.

Ich will mit euch ein kleines Beispielprojekt erstellen in dem wir Schritt für Schritt einige der Neuerungen durchgehen.

Fangen wir an und erstellen ein neues MVC3 Projekt in Visual Studio und bereits hier gibts es was Neues zu betrachten:

Bild 1: Der Neue Projekt erstellen Dialog

Da wir von Vorn beginnen möchten erstellen wir ein neues Leeres Projekt und wählen die neue Razor ViewEngine.

Layout

Wenn man nun einen Blick auf den Projektmappenexplorer wirft, wird man drin nicht viel finden, lediglich eine CSS- und zwei cshtml-Dateien. Die cshtml-Dateien fangen auch noch mit einem Unterstrich (_) an und haben irgendwas mit Layout im Namen.

Was ist das?

Öffnen wir zunächst im Shared-Ordner die Datei _Layout.cshtml.

image

Diese Datei kann man mit einer MasterPage aus MVC2 vergleichen. Wenn ihr genau hinschaut werdet ihr im Title-Tag einen Platzhalter finden und auch im body-div ist einer namens „@RenderBody()“.

Die Layoutseiten in MVC3 funktionieren anders als die in MVC2, auch wenn diese Seite auch Platzhalter besitzt die später mit Inhalt befüllt werden fällt dennoch die Methode RenderBody() auf. Diese Methode ist in jeder Layout-Seite drin und steht immer an der Stelle wo später der Inhalt der Seite  stehen wird. Neben der RenderBody()-Methode gibt es auch die RenderSection()-methode, die auch einen Namen bekommen und auch als optional markiert werden können.

Was auch neu ist, ist dass wir keine <% %> Tags mehr haben in die wir den ASP.NET Code schrieben können, das alles übernimmt nun das @-Zeichen. Das System ist dabei so intelligent, dass es erkennt bis wohin der Code geht weswegen man auch kein schließen Tag benötigt. Weitere Informationen zu der Razor Syntax.

Wie man sieht sind die ganzen JavaScript Verweise auskommentiert. Bitte entfernt die Kommentarmarkierung, da wir diese Skripte bald benötigen.

Gut, schleißen wir diese Datei und werfen nun einen Blick in die „_ViewStat.cshtml“ in unserem Views-Ordner:

image

Der Inhalt hier ist wie man sieht sehr übersichtlich Smiley

Was macht diese Datei nun? Wie man sieht verweist diese auf die _Layout.cshtml die wir eben betrachtet haben, dabei sorgt dieser Verweis dafür, dass alle Views in dem Views-Ordner inkl Unterordner diesen Verweis auch erhalten. Das hat zur Folge, dass man nun nicht mehr in jeder einzelnen View diese Referenz auf die Layoutdatei eintragen muss und spart sich so den redundanten Code.

Viel mehr gibt es dazu eigentlich auch nicht zu sagen. Gehen wir nun zum nächsten Schritt und erstellen eine neue View.

Views

Wenn ihr den View-Hinzufügen Dialog wählt werdet ihr auch hier eine Veränderung feststellen:

image

Wie man sieht kann man nun auch bei jeder einzelnen View wählen, welche ViewEngine man hier gern verwenden möchte. Legen wir eine gewöhnliche View an.

image

Die neue View enthält nun nur diese paar Zeilen Code (siehe Bild oben).

Zuerst wird ein Codeblock aufgemacht mit der @{ … } Syntax, das bedeutet, dass nun nur noch ASP.NET Code kommt bis die schließen Klammer kommt, auch über mehrere Zeilen.

Innerhalb dieses Codeblocks füllen wir nun den Titel unserer Seite, wenn ihr euch erinnern könnt wurde diese Variable in der MasterPage im Title-Tag wieder ausgegeben.

image

Man beachte hier, dass diese Property dynamisch erstellt wird und es die Property „Title“ eigentlich gar nicht gibt. Diese wird dann zur Laufzeit generiert und mit Inhalt befüllt. So kann man beliebig viele solcher Properties anlegen.

In Zeile 3 geben wir an welche MasterPage hier verwendet werden soll, da wir aber bereits die Datei „_ViewStat.cshtml“ in unserem Views-Ordner liegen haben müssen wir innerhalb der View keinen Verweis mehr auf eine Masterpage setzen. (Außer wir wollen eine andere Nutzen) Also können wir diese Zeile aus unserem Code löschen.

Nun fängt schon ab Zeile 6 der normale HTML Quelltext an, ohne dass wir irgendwo einen Zugehörigkeitsbereich definiert haben. Das ist ein weiteres MVC3 / Razor Feature, denn alles wird an der stelle in der MasterPage eingesetzt wo wir die Methode RenderBody() aufrufen. Eine Ausnahme besteht, wenn wir Sektionen in der Masterpage definiert haben, diese würden dann dementsprechend der passenden Sektion zugeordnet werden.

So könnte eine Sektion aussehen:

image

Das wars nun soweit zu dieser View. Wenn ihr mögt könnt ihr nun weiter damit arbeiten wie mit einer gewöhnlichen View.

Nun wollen wir mal ein Formular anlegen, dass wir zum Registrieren eines Benutzers nutzen wollen. Dazu benötigen wir zunächst ein Model auf das wir dann unsere View mit der Form  typisieren.

Legen wir also eine neue Klasse im Models-Verzeichnis an. Die Model Klassen verhalten sich in MVC3 eigentlich genauso wie in MVC2, nun sind aber einige neue Attribute hinzugekommen die ich euch hier mal vorstellen möchte:

image

Wie ihr seht hat meine Modelklasse 5 Eigenschaften. Die ersten drei sind mit dem Attribut [Required] markiert und sind somit Pflichtfelder. Alle Eigenschaften besitzen ein [DisplayName()] Attribut, dass den Text enthält, der später in der View angezeigt werden soll.

Kommen wir nun zu den neuen Attributen. Erstmal das [Required, Compare(…)] Attribut. Dieses Attribut sorgt hier dafür, dass „RepeatEmail“ mit „Email“ verglichen wird und übereinstimmen muss damit die Validierung erfolgreich ist. So müssen wir diese zwei Werte nun nicht mehr im Controller manuell vergleichen.

Weiter unten bei der About-Eigenschaft finden wir ein weiteres neues Attribut namens [SkipRequestValidation] – Im Prinzip sagt der Name schon alles aus. Dieses Attribut sorgt dafür, dass diese Property beim Binding keine Exception wirft wenn dort HTML enthalten ist.

Früher musste man dann im Controller die Action mit einem [ValidateInput(false)] markieren, was natürlich immer noch funktioniert, aber nun kann man auch einzelne Properties bequem markieren ohne gleich für die ganze Methode den Validationinput abzuschalten.

Hier gibts es nun sogar die Möglichkeit bestimmte Properties von der Validierung auszuschließen:

image

Hier werden die Properties “Body” und “Summary” nicht auf HTML Inhalt überprüft.

Wie ihr sehen könnt habe ich ein Attribut auskommentiert. Auf dieses Attribut habe ich mich persönlich sehr gefreut, aber es scheint nicht zu funktionieren (oder ich mache etwas falsch, wenn ihr meinen Fehler seht bitte kommentieren ACHTUNG: dazu habe ich hier ein Update geschrieben)

Dieses RemoteAttribut erlaubt es eine Eigenschaft an eine Methode in einem Controller zu binden und währen der Ausführung der View beim Client im Hintergrund über Ajax aufzurufen. Für den Fall von der Property Name würde die Methode UserNameAvailabla im Controller Register aufgerufen werden, welche ein boolean zurück gibt. So wird dann dynamisch eine Fehlermeldung auf der View erscheinen.

Wenn man dieses RemoteAttribut nun benutzen möchte muss man natürlich die oben angegebene Methode im angegebenen Controller implementieren, zB so:

image

Diese Methode wird dann beim Validieren per Ajax aufgerufen und zeigt dann dementsprechend auch eine Fehlermeldung an (Falls nötig):

image

Zum Schluss möchte ich noch darauf hinweisen, dass ich ein bestimmtes kleines Feature vermisst habe, nämlich eine bequeme Möglichkeit einen String auszugeben ohne diesen mit HTML zu Encoden. Denn soweit bietet Razor leider keine Möglichkeit dafür. Deswegen muss man hier mit einer eigenen kleinen Extension Methode aushelfen.

Das wäre es dann nun auch für heute. Ich habe ein kleines Beispiel Projekt vorbereitet, dass ihr hier herunterladen könnt. (.NET 4.0 VS2010)

2Nov/107

C#: Eigene Dialoge erstellen

In der Windows Forms Welt benutzt man Dialoge sehr häufig, egal ob es der OpenFileDialog, SaveFileDialog, PrintDialog, ColorDialog oder einfach nur die simple MessageBox ist, sie erleichtern uns der Leben sehr. So wäre auch ein Dialog sehr nützlich der Benutzereingaben entgegen nimmt und uns diese dann weitergibt, doch so einen Dialog finden wir nur im VisualBasic Namespace in .NET.

Bild 1: Das InputBox Dialogfenster

Der InputBox Dialog nimmt Eingaben vom Benutzer entgegen und gibt diese an unser Programm weiter. Doch was wenn wir den Benutzer nach einem Passwort fragen möchten? InputBox kann den eingegebenen Text leider nicht maskieren. So muss ein eigener Dialog für die Passworteingabe her.

Zum Glück ist diese Aufgabe im .NET Framework wirklich schnell erledigt. In VB.NET gibt es unter Windows Forms sogar extra eine Vorlage für ein Dialogfenster. Aber nun mal Langsam.

Alles was wir benötigen ist ein normale Klasse die von System.Windows.Forms.Form erbt. Dieses Fenster könnt ihr nun so gestalten, wie ihr euch euer Passwortdialogfenster so vorstellt. Ich habe meins so gestaltet:

Bild 2: Mein PasswortDialog Fenster

Bevor wir uns nun an die Innereien dieser Fensterklasse machen müssen wir noch ein paar Kleinigkeiten mit dem Designer vornehmen. Wir müssen nämlich angeben, welche Aktionen zu welchen Dialog Ergebnis führen.

So soll das Klicken des OK-Buttons das DialogResult "OK" Auslösen, der Abbrechen-Button und der X-Button (Fenster schließen) aber das "Cancel" DialogResult. Dementsprechend stellt man die gleichnamige Eigenschaft der Buttons auf "OK" oder "Cancel".

Um dem Benutzer später das Arbeiten mit diesem Fenster intuitiver zu machen kann man nun noch den Form Eigenschaften "AcceptButton" und "CancelButton" die passenden Buttons zuweisen. Das sorgt dafür dass der Benutzer später das Dialog Fenster mit der Taste "Enter" bestätigen oder mit "Escape" abbrechen kann.

Bereits jetzt, ohne auch nur eine Zeile Code geschrieben zu haben funktioniert dieses Fenster wie ein vollwertiger Dialog. Sogar die Buttons, für die wir keinen Code hinterlegt haben schließen beim Klicken das Fenster und übergeben das passende DialogResult.

Alles was wir nun noch machen müssen ist, Parameter anzubieten mit denen wir den Text auf dem Formular modifizieren können und vielleicht die Anzahl der Buttons die angezeigt werden sollen:


public partial class PasswordDialog : Form {

    /// Das vom Benutzer eingegebene Passwort
    public string EnteredPassword {get; private set;}

    /// Erzeugt ein neues Password Dialog
    public PasswordDialog(string windowTitle, string messageTitle,
                            string messageDescription,
                            DialogButtons buttons = DialogButtons.OKCancel,
                            bool windowClosable = true) {

        InitializeComponent();

        //Übergebene Werte übernehmen
        this.Text = windowTitle;
        this.lblTitle.Text = messageTitle;
        this.lblDescription.Text = messageDescription;

        //Optionale Parameter bearbeiten
        if (buttons == DialogButtons.OK) {
            //Wenn nur der OK Button angezeigt werden
            //soll den Cancel Button ausblenden
            this.cmdCancel.Visible = false;
        }

        if (windowClosable == false) {
            //Schließen Button bei Bedarf ausblenden
            this.ControlBox = false;
        }

        EnteredPassword = "";       //Property initialisieren
    }

    private void PasswordDialog_Load(object sender, EventArgs e) {

    }

    private void cmdCancel_Click(object sender, EventArgs e) {
        //Dem Aufrufer mitteilen, dass der Dialog abgebrchen wurde
        //this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
    }

    private void cmdOK_Click(object sender, EventArgs e) {
        //Eingabe übernehmen
        EnteredPassword = txtPassword.Text;
    }
}

public enum DialogButtons {
    OK = 0,
    OKCancel = 1
}

Hier passiert eigentlich nichts aufregendes, im Konstruktor werden nur alle übergebenen Parameter auf das Formular übertragen (Text, Buttons...)  und die Property für das Passwort initialisiert. Schließlich wird noch bei dem Klick auf den OK-Button der Text in der TextBox noch in der Property gespeichert und dann wird das Formular auch schon mit dem passenden DialogResult geschlossen.

Wenn wir das Ganze nun nutzen wollen dann kann das wie folgt aussehen:


private void cmdShowDialog_Click(object sender, EventArgs e) {
    PasswordDialog pass = new PasswordDialog("Passwort benötigt",
                                        "Bitte geben Sie Ihr Passwort an",
                                        "Wenn Sie Zugriff auf erweiterte " +
                                        "Funktionen benötigen müssen Sie Sich" +
                                        "mit Ihrem Passwort Authentifizieren");
    DialogResult res = pass.ShowDialog();

    if (res == System.Windows.Forms.DialogResult.OK) {
        this.txtDialogResult.Text = pass.EnteredPassword;
    } else {
        this.txtDialogResult.Text = "Der Benutzer hat den Dialog abgebrochen";
    }
}

Bild 3: Die DemoAnwendung

Das wars auch schon. So schnell hat man einen eigenen Dialog erstellt.

An dieser Stelle noch der Hinweis, dass der oben gezeigte Quelltext nur mit .NET 4.0 kompiliert werden kann, da ich hier Optionale Parameter benutzt habe. Wenn ihr diese rausnehmt dann läuft dieser Code problemlos auch mit älteren .NET Versionen.

Hier habe ich für euch auch noch ein kleines Demoprojekt gebastelt:

Download: VS2010 C# .NET4.0
kick it on dotnet-kicks.de

21Okt/101

ASP.NET MVC 2: Custom ErrorPages anlegen

Wenn man Webseiten entwickelt möchte man diese natürlich möglichst anschaulich für den Nutzer gestalten, und dazu gehört es auch für den Benutzer anschauliche und auch verständliche Seiten zu präsentieren, auch wenn mal ein Fehler auftaucht. Im Normalfall passen die Default-Fehlerseiten von ASP.NET leider nicht ins Bild der restlichen Seite, weswegen wir gezwungen sind unsere eigenen Fehlerseiten anzulegen.

Bild 1: Typische 404 Fehlermeldung von ASP.NET

Eigene Fehlerseiten sind unter ASP.NET MVC 2 schnell in vier Schritten eingerichtet.

1. Änderungen an der Web.config vornehmen

Zunächst müssen in der Web.config die customErrors eingeschaltet werden. Hier können die Attribute "defaultRedirect", welches die Adresse definiert die im Falle eines Fehlers aufgerufen wird und "mode", dass einstellt ob Fehler angezeigt werden sollen oder nicht.

Mit den inneren "error"-Ästen kann man dazu noch spezifische Seiten zu einzelnen Fehlercodes zuweisen. Man kann hier sowohl eine  absolute Adresse angeben oder nur eine relative. Für alle nicht spezifizierten Errorcodes wird die Url aus dem customError Tag benutzt.

Wenn ihr diese Einstellungen Lokal testen wollt dann solltet ihr auch den "mode" auf "On" stellen, da ihr euere Seiten sonst nicht zu Gesicht bekommt.

2. Anlegen des Controllers für die Fehlerbehandlung

Wie ihr sehen könnt habe ich hier eine lokale Adresse angegeben. Hierbei wird der Controller "Error" aufgerufen und je nach Fehlercode eine andere Methode.

Im Controller haben wir hier eigentlich nichts besonderes:

Ich war an dieser Stelle mal faul und habe nur eine einzige View für alle Fehler angelegt, die dann die übergebene message ausgibt. Dazu kommt noch, dass wir der Response noch den passenden HttpStatusCode übergeben, da manche Anwendungen danach gehen und so prüfen, ob ein Aufruf erfolgreich war.

3. Die View

Wie eben erwähnt macht die View in meinem Beispiel nichts anderes als den Inhalt der Messagevariable auszugeben. Hier könnt ihr natürlich eurer Kreativität freien Lauf lassen und auch für jeden Error eine einzelne View anfertigen.

4. Glogal.asax - Catch All Route registrieren

Zum Schluss sollten wir noch eine sogenannte Catchall Route einrichten, die wir nach allen unseren Routen positionieren damit diese nur greifen kann, wenn keine der anderen gepasst hat, diese Route wird in jedem Fall aufgerufen, wenn keine der vorderen gepasst hat.

Wenn die CatchAll Route greift wird der Home Controller aufgerufen und die Action "NotFound" ausgeführt.

Im Grunde sind wir nun fertig, alle von uns eingetragenen Fehler werden nun abgefangen und durch von uns präparierten Seiten ersetzt. Das einzige was noch passieren kann ist dass ihr im IIS andere ErrorPages eingestellt habt, dann dann werden diese angezeigt und nicht die von euch erstellten. Hier müsst ihr die "Error Responses" auf "Detailed errors" stellen.

18Okt/102

.NET 4.0 Entfernung zwischen zwei Punkten (Koordinaten) berechnen

Letztens habe ich wieder mal in .NET Framework rum gewühlt und bin dabei über den System.Device.Location Namensraum gestolpert. Dieser bietet einige tolle Funktionen die man sich mal anschauen sollte.

Dieser Namensraum existiert seit .NET 4.0 und ist dazu da um die mit Windows 7 eingeführten Sensoren zu nutzen, wie den Bewegungssensor usw. (Siehe Bild)

Bild 1: Die Windows Sensoren

Wie es der Namensraum schon sagt geht es hier um den Location Sensor. Leider habe ich keinen Rechner oder Laptop der über ein GPS Chip verfügt auf dem ich diese Funktionalität testen könnte. Aber das war mir auch nicht so wichtig, denn eigentlich war ich auf der Suche nach einer Möglichkeit Entfernungen zu berechnen anhand von Koordinaten.

Ich war schon kurz davor eine eigene Funktion zu basteln, als ich diesen Namensraum sah. Zum Glück, denn ich muss  sagen, denn Microsoft hat hier wirklich gute Arbeit geleistet, so ist es möglich mit einem Einzeiler die Entfernung zwischen zwei Koordinaten zu errechnen. Hier ein Beispiel:


using System.Device.Location;

private void Form1_Load(object sender, EventArgs e) {
    GeoCoordinate bielefeld = new GeoCoordinate(52.020833, 8.535);   //Ja, das gibt es wirklich
    GeoCoordinate muenchen = new GeoCoordinate(48.139722, 11.574444);
    double meters = bielefeld.GetDistanceTo(muenchen);  // => 483,303 km
}

Wie man sieht ist ist das wirklich sehr sehr einfach. Natürlich bietet dieser Namensraum noch viel mehr, so kann man beispielsweise auch EventListener einrichten, die Events feuern wenn sich das Gerät bewegt und sich die Koordinaten somit ändern. Ein Beispiel dazu findet ihr hier.

8Sep/102

.NET: Ein Transparentes Label erstellen

Immer häufiger fragen Leute in Foren nach, wie man das Label dazu bringen kann den Hintergrund transparent darzustellen. Es wird vieles versucht, doch am Ende müssen alle feststellen, dass es mit dem "normalen" Label aus dem Framework nicht möglich ist eine transparente Darstellung zu erreichen.

Bild1: So wie es ist und wie es sein sollte

Das verwirrende hierbei ist, dass das Label es erlaubt die Hintergrundfarbe auf "Transparent" zu stellen, doch das macht den Hintergrund nicht wirklich transparent, sondern setzt den Hintergrund des Labels auf das des Containers in dem sich das Label befindet. Also wenn man das Label auf der Form Platziert und die Form Blau als Hintergrundfarbe hat wird das Label ebenfalls Blau als Hintergrund bekommen, wenn es auf Transparent gestellt ist.

Dies erweckt den Eindruck von Transparenz, doch was wenn man andere Steuerelemente oder Farbverläufe mit dem Label überdeckt? - Das führt zu einem unschönem Ergebnis (siehe Bild oben). Um diesen Fall ordentlich darzustellen muss man das Control selber zeichnen.

Ich habe hier für euch eine kleine Klasse namens TransparentLabel erstellt, dass nach einem Compile-Vorgang in eurer Werkzeugleiste als Steuerelement auftauchen wird. Hierbei mache ich nichts anderes, als den Text des Controls selbst zu zeichnen und dafür zu sorgen dass der Hintergrund nicht gezeichnet wird in dem man die Methode überschreibt und leer lässt.

Hier der Code:


//Unser Transparent Label erbt alles vom "nemalen" Control
class TransparentLabel : Control {

    //Wenn sich der Text ändert soll das Steuerelement
    //neugezeichnet werden
    protected override void OnTextChanged(EventArgs e) {
        base.OnTextChanged(e);
        Invalidate();
    }

    //Das Zeichnen des Elements übernehmen wir selbst:
    protected override void OnPaint(PaintEventArgs e) {
        //base.OnPaint(e);

        //Erst erstellen wir einen Pinsel mit der Farbe
        //Die der Benutzer für das Control eingestellt hat
        Brush myBrush = new SolidBrush(ForeColor);

        //Den Text zeichnen
        e.Graphics.DrawString(Text, Font, myBrush, new PointF(-1, 0));
    }

    //Den Hintergrund nicht zeichnen
    protected override void OnPaintBackground(PaintEventArgs pevent) {
        //base.OnPaintBackground(pevent);
    }

    //Mit dem Überschreiben der CreateParams
    //Können wir den Hintergrund "wirklich"
    //tranzparent machen.
    protected override CreateParams CreateParams {
        get {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x20;
            return cp;
        }
    }
}

Und für VB.NET Nutzer:


Class TransparentLabel
	Inherits Control

	'Wenn sich der Text ändert soll das Steuerelement
	'neugezeichnet werden
	Protected Overrides Sub OnTextChanged(e As EventArgs)
		MyBase.OnTextChanged(e)
		Invalidate()
	End Sub

	'Das Zeichnen des Elements übernehmen wir selbst:
	Protected Overrides Sub OnPaint(e As PaintEventArgs)
		'base.OnPaint(e);

		'Erst erstellen wir einen Pinsel mit der Farbe
		'Die der Benutzer für das Control eingestellt hat
		Dim myBrush As Brush = New SolidBrush(ForeColor)

		'Den Text zeichnen
		e.Graphics.DrawString(Text, Font, myBrush, New PointF(-1, 0))
	End Sub

	'Den Hintergrund nicht zeichnen
	Protected Overrides Sub OnPaintBackground(pevent As PaintEventArgs)
		'base.OnPaintBackground(pevent);
	End Sub

	'Mit dem Überschreiben der CreateParams
	'Können wir den Hintergrund "wirklich"
	'tranzparent machen.
	Protected Overrides ReadOnly Property CreateParams() As CreateParams
		Get
			Dim cp As CreateParams = MyBase.CreateParams
			cp.ExStyle = cp.ExStyle Or &H20
			Return cp
		End Get
	End Property
End Class

Wie man sieht ist es wirklich nicht viel (Ergebnis seht ihr ja im Bild oben). Ich habe hier zwar nur die Grundfunktionen implementiert, diese reichen aber schon völlig aus, um es als ein transparentes Label zu benutzen. Nach Bedarf könnt ihr diese Klasse ja schnell und einfach erweitern.

6Sep/104

Java und C# – Die feinen Unterschiede

Immer wieder begegnen mir Leute, die Java und C# in die selbe Schupp-lade stecken. Man hört dann meist Sätze wie "C# ist Java von Microsoft", "Dann lerne ich doch gleich lieber Java statt Microsofts Kopie". Jeder der sich mit der .NET Plattform beschäftigt wird natürlich wissen, wie falsch diese Aussagen sind, die meist nur auf alten Vorurteilen und Unwissenheit basieren.

Die Anfänge

Natürlich haben diese Vorurteile irgendwo etwas wahres dran, so ist es nicht von der Hand zu weisen, dass sich die Syntax der beiden Sprachen sehr sehr ähnlich ist, und auch die Ausführung des Codes in einer VM kann man auch zu den Gemeinsamkeiten zählen. So ist C# ja auch entstanden, als eine Mischung aus Java und C++. Dies sollte es den Leuten erleichtern auf die neue Plattform zu wechseln und schnell einen Einstieg zu finden, desweiteren hat es sich in der Technischen Welt bewährt gute und erfolgreiche Konzepte zu kopieren und zu perfektionieren/verbessern. So würde ich auch C# mehr als eine Weiterentwicklung von Java betrachten.

Wo liegen die Unterschiede?

Natürlich fragt man sich dann, wo die Unterschiede sind und wenn man sich etwas damit befasst, wird man feststellen, dass es doch ziemlich viele Unterschiede und (meiner Meinung nach sehr nützliche) Weiterentwicklungen gibt.

Hier ein paar Beispiele:

  • Properties - Saubere Model-Klassen ohne einen riesen GetBla & SetBla Gestrüppe
  • Delegates (Funktionszeiger) - Übergabe von Funktionen als Parameter uvm.
  • LINQ (Language Integrated Queries)  - Objektorientierte SQL/XML-Abfragen
  • Events - Erleichtert strickte Trennung von Schichten und Informationsweitergabe
  • Lambda Ausdrücke - Viel Arbeit auf wenig Platz (Wird eventuell im JDK 1.7 Einzug halten)
  • Enums und Colections

Desweiteren gibt es auch wichtige Unterschiede, die unter der Haube ablaufen:

  • Assembly-Konzept - Einfache und schnelle Interaktion zwischen den .NET Sprachen wie C#, Visual C++.Net, Visual Basic.Net oder Delphi.Net
  • Namespaces - Auf den ersten Blick sehr ähnlich zu Packages , aber nicht auf den zweiten ;-)

Natürlich gibt es noch vieles mehr was an Unterschieden zu entdecken gibt. Wer da interessiert ist kann dich mal diese Seite anschauen mit vielen Beispielen und Vergleichen.

Einsatzbereiche

Sogar in den Einsatzgebieten ähneln sich die Plattformen sehr, denn wie mit Java kann man auch in .NET sowohl für Desktop, für Web und für mobile Plattformen entwickeln, wobei die mobilen Plattformen noch auf Windows Mobile beschränkt ist. (Aber Mono für Android ist ist ja auch schon unterwegs)

Dank Mono kann man viele .NET Programme auch sehr einfach auf Unix Systeme portieren, womit man teilweise auch die Plattformunabhängigkeit von Java erreicht. (Mono unterstützt zur Zeit das komplette .NET Framework 2.0)

Die Zukunft

Microsoft entwickelt die .NET Plattform sehr aktiv. Vor Kurzem ist die 4te Version des Frameworks inklusive einer neuen Visual Studio Version erschienen. Weitere Releases und Weiterentwicklungen dürften somit auch bald folgen.

Bei Java schaut man momentan noch in eine ungewisse Zukunft, da Sun vor einer Weile von Oracle aufgekauft wurde, weis man nun nicht mit Sicherheit was aus den Produkten wird. So musste die Community bereits den Verlust von Open Solaris hinnehmen und es hagelte auch schon die ersten Patentklagen wegen dem Aufbau der Java VM auf verschiedenen Plattformen. - Keine Rosigen Aussichten für Java Fans.

So scheinen nun sich die Feindbilder zu ändern. Lange Zeit war Microsoft wohl der Anführer der Bösewicht Nummer 1 - Liste, doch das Unternehmen hat sich gewandelt und stellt inzwischen sogar zu vielen Projekten sogar den Code zur Verfügung, so ist zum Beispiel ein großer Teil des .NET Frameworks einsehbar und auch das ASP.NET MVC Framework lässt sich komplett einsehen.

Die .NET Plattform ist inzwischen eine sehr mächtige geworden, und alle die es runterspielen oder ignorieren kann ich einfach nicht ernst nehmen. Und auch wenn man ein Open-Source-Junkie oder einfach nur ein Microsoft-Feind ist sollte man dennoch wissen, wovon man redet.

16Aug/101

.NET: Mit Extension Methods arbeiten

Aus gegebenen Anlass möchte ich heute einen kleinen Artikel zu dem Thema Extension Methods in .NET schreiben, einfach nur weil es eine sehr simple Möglichkeit bietet sich das Leben zu erleichtern. Falls es euch nichts sagt, mit Extension Methods, die  es seit C# 3.0 gibt ist es möglich bestehende Klassen, sogar interne Core-Klassen wie Integer oder String mit eigenen Methoden zu erweitern.

Wieso das nützlich ist fragst du dich? Na ganz einfach, habt ihr schon mal die Methode String.toInt32() vermisst? Diese ist nur eine (sehr simple) Methode, die man häufiger gebrauchen könnte, die es aber nicht gibt. Diese könnte man sehr einfach mit einer Extension Method erweitern.

Als Beispiel möchte ich hier aber den Artikel von vor ein paar Tagen nehmen, wo wir eine Methode erstellt haben mit der man ein XmlElement in einem XmlDocument umbenennen konnte. Dies mussten wir machen, da die Klasse XmlDocument keine Methode dafür anbietet. Nun wollen wir mal die XmlDocument Klasse um diese Methode erweitern.

Das ganze funktioniert sehr einfach:


    public static class XmlDocumentExtension
    {
        public static bool RenameXmlElement(this XmlDocument doc,
                                            XmlElement oldElement,
                                            string newName) {
            try {
                //Neues XmlElement mit neuem namen anlegen
                XmlElement newElement = oldElement.OwnerDocument.CreateElement(newName);

                //Dem neuen XmlElement alle Attribute des alten Elements übergeben
                foreach (XmlAttribute a in oldElement.Attributes) {
                    newElement.SetAttributeNode((XmlAttribute)a);
                }

                //Dem neuen XmlElement alle Kinder Elemente des alten Elements anhängen
                foreach (XmlNode n in oldElement.ChildNodes) {
                    newElement.AppendChild(n.Clone());
                }

                oldElement.ParentNode.ReplaceChild(newElement, oldElement);
            } catch {
                return false;
            }

        return true;
        }
    }

Wir legen eine ganz normale Klasse an, diese kann heißen wie man will, ich habe sie hier nur zum besseren Verständnis so benannt. In der klasse legen wir nun die Funktion an um die wir unsere Klasse erweitern wollen. Hierbei ist wichtig, dass sowohl die Klasse als auch die Funktion als public und static markiert wird. Des Weiteren sollte man einen Verweis auf System.Code.dll setzen.

Bei der Deklaration der Funktion muss man dann angeben welche Klasse erweitert werden soll. Hier erweitern wir die Klasse XmlDocument weswegen vor diesem Parameter noch ein this steht. Dieser Parameter wird nur dazu benötigt und muss dann später bei dem Aufruf der Funktion nicht gesetzt werden.

Bild 1: Auch IntelliSense erkennt unsere Erweiterung an und bietet diese an

Natürlich bleibt die "original" Klasse XmlDocument unberührt von dieser Aktion und wird nur virtuell erweitert. Diese Erweiterung ist natürlich auch nur für das Aktuelle Projekt nutzbar.

Ich finde dieses Feature wirklich sehr nützlich und kann es euch deswegen auch nur empfehlen.

public static bool RenameXmlElement(this XmlDocument doc,
XmlElement oldElement,
string newName) {
try {
//Neues XmlElement mit neuem namen anlegen
XmlElement newElement = oldElement.OwnerDocument.CreateElement(newName);

//Dem neuen XmlElement alle Attribute des alten Elements übergeben
foreach (XmlAttribute a in oldElement.Attributes) {
newElement.SetAttributeNode((XmlAttribute)a);
}

//Dem neuen XmlElement alle Kinder Elemente des alten Elements anhängen
foreach (XmlNode n in oldElement.ChildNodes) {
newElement.AppendChild(n.Clone());
}

oldElement.ParentNode.ReplaceChild(newElement, oldElement);
} catch {
return false;
}

return true;
}

20Jul/102

Windows Aero Glas in eigenen Projekten Nutzen Teil 5 – Der letzte Versuch

In den letzten Tagen habe ich mal wieder ein wenig mit der Windows API herumgespielt, speziell mit der Aero Glas API (DWMAPI). Ich habe euch ja auch noch versprochen einen Beitrag über Steuerelemente in Verbindung mit Glas nachzuliefern. Nunja hier ist er :)

Bild 1: Probleme mit der Transparenz

In Bild 1 sehen wir das Problem, viele der Windows Forms Steuerelemente nutzen GDI zur Darstellung und haben deswegen keinen Alpha-Kanal, der aber für die Darstellung auf Glas benötigt wird. Deswegen nutzen wir die Schwarze Farbe um unsere Fläche in Glas zu verwandeln, leider denkt der DWM dann, dass auch die Texte auf unseren Steuerelementen zu Glas werden sollen.

Ich habe mich nun ein wenig im Internet umgehört und verschiedene Ansätze gefunden und diverse Hacks mit der Windows32API Ausprobiert, die aber alle leider nicht mehr funktionieren. So will ich euch heute zwei Möglichkeiten vorstellen wie man es doch hinkriegen könnte.

1. Owner Drawing

Da die Steuerelemente mit GDI gerendert werden haben wir ein kleines Problem wie man oben sieht. Also müssen wir die Darstellung der Steuerelemente selbst in die Hand nehmen. Dieser Vorgang nennt sich OwnerDrawing und bedeutet soviel wie "SelberZeichnen". Ich will das Ganze mal anhand einer Textbox demonstrieren.

Und genauso ist auch das Vorgehen. Wir erstellen eine neue Klasse und nennen Sie "AeroTextBox". Diese Klasse erbt nun von der Original TextBox. Nun müssen wir ein paar kleine Änderungen an der Klasse vornehmen in Bezug auf die Darstellung, also des WM_PAINT Ereignisses.

Da wir das Zeichnen des Steuerelements selbst in die Hand nehmen wollen müssen wir die WM_PAINT Message abfangen und auf unsere eigene Methode umleiten:

C#


    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        base.WndProc( ref m);

        switch (m.Msg)
        {
            case 0xf: //WM_PAINT
                RedrawControlAsBitmap(this.Handle);
                break;
        }
    }

VB.NET


 Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
	MyBase.WndProc(m)

	Select Case m.Msg
		Case &Hf 'WM_PAINT
			RedrawControlAsBitmap(Me.Handle)
			Exit Select
	End Select
End Sub

Wie man sieht fangen wir hier die Message ab und rufen unsere eigene Methode auf, die das Zeichnen übernehmen wird:

C#


    public void RedrawControlAsBitmap(IntPtr hwnd)
    {
        Control c = Control.FromHandle(hwnd);
        using (Bitmap bm = new Bitmap(c.Width, c.Height))
        {
            c.DrawToBitmap(bm, c.ClientRectangle);
            using (Graphics g = c.CreateGraphics())
            {
                g.DrawImage(bm, new Point(-1, -1));
            }
        }
        c = null;
    }

VB.NET


Public Sub RedrawControlAsBitmap(hwnd As IntPtr)
	Dim c As Control = Control.FromHandle(hwnd)

	Using bm As New Bitmap(c.Width, c.Height)
		c.DrawToBitmap(bm, c.ClientRectangle)

		Using g As Graphics = c.CreateGraphics()
			g.DrawImage(bm, New Point(-1, -1))
		End Using
	End Using
	c = Nothing
End Sub

Was hier passiert ist eigentlich sehr simpel, wir zeichnen das Control neu und legen das gezeichnete Bild nun über das Control drüber. Nur diesmal machen wir das ganze mit einem Alpha Kanal, sodass der DWM uns die Darstellung nicht versaut!

Nun bleibt noch ein kleines Problem, nämlich dass bei manchen Aktionen des Benutzers wie zB. bei MouseOver oder KeyPress Events das Steuerelement neugezeichnet wird und unsere Funktion da oben nicht greift. Hier müssen wir manuell dafür sorgen, dass Windows erneut die WM_PAINT Message versendet. Das erreichen wir indem wir die einzelnen Events überschreiben und die Invalidate()-Methode aufrufen. Diese Methode erklärt die ganze Oberfläche des Steuerelements für ungültig und bewirkt, dass das Steuerelement neu gezeichnet wird.

C#


    protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e)
    {
        base.OnMouseClick(e);
        Invalidate();
    }

    protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
    {
        base.OnMouseMove(e);
        Invalidate();
    }

    protected override void OnTextChanged(System.EventArgs e)
    {
        base.OnTextChanged(e);
        Invalidate();
    }

    protected override void OnMouseEnter(System.EventArgs e)
    {
        base.OnMouseEnter(e);
        Invalidate();
    }

VB.NET


Protected Overrides Sub OnMouseClick(e As System.Windows.Forms.MouseEventArgs)
	MyBase.OnMouseClick(e)
	Invalidate()
End Sub

Protected Overrides Sub OnMouseMove(e As System.Windows.Forms.MouseEventArgs)
	MyBase.OnMouseMove(e)
	Invalidate()
End Sub

Protected Overrides Sub OnTextChanged(e As System.EventArgs)
	MyBase.OnTextChanged(e)
	Invalidate()
End Sub

Protected Overrides Sub OnMouseEnter(e As System.EventArgs)
	MyBase.OnMouseEnter(e)
	Invalidate()
End Sub

Nun wird das Control richtig dargestellt. Wie ihr vielleicht auch gesehen habt haben wir in unserer Methode, die für das Neuzeichnen verantwortlich ist nicht TextBox sondern die Superklasse Control benutzt. Das bedeutet für uns, dass wir mit diesem Code jedes Steuerelement ableiten und auf diese Weise neuzeichnen können mit mehr oder weniger viel Erfolg. Ich habe mal ein paar Klassen abgeleitet und für euch auf dem Glass-Form platziert:

Bild 2: Die OwnerDrawn Steuerelemente

Wie man sieht ist das schon eine sehr vielversprechende Verbesserung der Darstellung, wenn auch nicht perfekt. Aber natürlich steht es euch noch frei die Darstellung weiter zu verbessern und auf die einzelnen Steuerelemente anzupassen.

Nachdem ihr die selbst erstellten Klassen die von den einzelnen Steuerelementen erben einmal kompiliert habt, wird Visual Studio dies bemerken und euch diese neuen Steuerelemente zusammen mit den anderen in der Toolbox anbieten. Diese können dann wie gewohnt per Drag & Drop auf das Formular gezogen werden.

Bild 3: Bequemer Gehts nicht

Wie ihr euch vorstellen könnt ist es ein ziemlicher Aufwand jedes Steuerelement noch mal neu anzulegen und dessen komplette Darstellung zu übernehmen. Es ist wirklich sehr frickelig! Deswegen wird dieser Ansatz auch eher selten verwendet! Die Demo dazu könnt ihr euch übrigens am Ende des Artikels herunterladen.

Variante 2. "The Microsoft Way"

Natürlich stolpert man irgendwann bei der Recherche über das Windows API SDK in dem es einige Beispiele dazu gibt wie man die neuen Funktionen in Windows benutzt, darunter natürlich auch Glas.

Der Ansatz den Microsoft da aber nach ihrer eigenen Best Practise verwendet ist aber eher ernüchternd. Hier geht es nach dem Motto, platziert einfach keine Steuerelemente auf dem Glas! - Super nicht?

In dem einzigen Beispiel das Microsoft da aufführt wird eine Form gezeigt, dessen Rahmen in das Formular bis zum darauf liegendem Panel erweitert wurde, nicht wirklich spannend. Aber wenn ich so überlege, fällt mit spontan auch kein Windows Programm auf, bei dem irgendwelche Steuerelemente auf dem Glas liegen - kennt ihr welche? In dem meisten Programmen liegen fast nur Grafiken auf dem Glas oder so ähnliche Controls wie die im Windows Explorer.

Also sage ich es mal Microsofts Worten: lasst die Controls vom Glas weg oder benutzt WPF! ;-)

Hier noch die Demo zu der Variante 1: Download [VS 2008 Projekt]

Zur zweiten Variante habe ich keine Demo gemacht, ihr könnt euch ja das Windows API SDK herunterladen und dort im Aero Ordner nachgucken.

<< Teil 4 des Tutorials

Get Adobe Flash player