BigBasti's Blog About Me & my Digital Lifestyle

8Jun/110

Entstehung einer ASP.NET Webanwendung anhand eines Beispiels

Mit diesem Artikel will ich skizzieren wie eine Webanwendung mittels ASP.NET entworfen und umgesetzt wird. Ich will einen Einblick in die Vorüberlegungen, sowie in die Umsetzung des Projektes geben. Nicht mehr, aber auch nicht weniger.

Vorüberlegungen

Wie fast alle Hobbyprogrammierer erstelle auch ich eine Unzahl unnötiger und zum Teil auch unsinniger Anwendungen. Dies ist der Luxus den man sich leisten kann, wenn man damit nicht sein Geld verdienen muss.

Da ich ab und zu auf Dienstreise gehe, ist die aktuelle Uhrzeit am gegenwärtigen Standort immer ein Thema. Oft kommt auch die Frage auf, wie spät es denn bei den Lieben zu Hause wohl sein mag.

Hierfür  kommt natürlich nur, mein neuestes Steckenpferd, eine Webanwendung in Frage.

Die ersten Überlegungen:

  1. Welcher Aufbau einer Webanwendung ist der beste
  2. Wie ermittle ich meinen aktuellen Standort
  3. Welche zusätzlichen Informationen kann ich noch anzeigen

1. Welcher Aufbau einer Webanwendung ist der beste?

Ich hab mich hier, sehr schnell, für eine Webanwendung entschieden, die ihre Informationen aus einem RSS-Feed bezieht. Vorteil hierbei, es handelt sich um ein XML-Format, welches leicht zu lesen ist und auch anderweitig verwendet werden kann.
Eine Windows-, Linux-, MacOS- oder Android Anwendung könnte den RSS-Feed ebenfalls nutzen.

2. Wie ermittle ich meinen aktuellen Standort

Im Prinzip wäre GPS das ideale Werkzeug um den aktuellen Standort auf der Welt bestimmen zu können. Da ich jedoch nur ein Notebook auf meinen Dienstreisen zur Verfügung habe, fällt das mit dem GPS aus, denn die wenigsten Notebooks verfügen über GPS – meines schon gar nicht.

Eine weitere Möglichkeit meinen Standort zu bestimmen wäre über die IP Adresse, mit der sich mein Computer über den jeweiligen Provider mit dem Internet verbindet. Nicht sehr genau, aber wenn es um die Zeitzone geht – genau genug.

Eine andere Alternative, die mir erst vor kurzem bekannt wurde, ist die Ermittlung des Standortes via HTML 5 – Voraussetzung hierfür sind ein Browser der diese neue Technologie unterstützt und aktiviertes JavaScript.

Die Umsetzung via HTML 5 ist zudem genauer als der Weg über die IP-Adresse. Ideal wäre also die Verknüpfung beider Techniken.

Da meine Seite aktuell mit der IP-Adresse arbeitet, möchte ich das als Basis der folgenden Abhandlungen nehmen.

3. Welche zusätzlichen Informationen kann ich noch anzeigen

Weitere Informationen bietet ASP.NET von allein. IP-Adresse, Sprache, Browser, Auflösung usw.

 

Die Umsetzung:

Wie schon erwähnt ist ein RSS-Feed nichts anderes als eine XML Datei. Diese wird als Antwort (Response) auf eine Anfrage (Request) vom Webservice zurückgegeben.

Im Prinzip also recht einfach

  1. Daten ermitteln
  2. XML erstellen
  3. Daten zurückgeben

Überlegung:

Welche Daten benötigen wir und welche Daten soll der RSS-Feed zusätzlich enthalten?

Im Prinzip benötigen wir nur drei Werte um die aktuelle Uhrzeit und Zeitzone übermitteln zu können.

  1. Die Zeitzone als ID – damit kann dann ganz einfach die passende Zeitzone auf dem Windows-PC gefunden werden.
    (Wie das auf einem MAC OS oder einem Linux Rechner umgesetzt werden kann wäre eine weitere Überlegung wert)
  2. Die Zeitverschiebung in Stunden (Als Basis gilt hier immer die koordinierte Weltzeit)
  3. Die Zeitverschiebung in Minuten (Als Basis gilt hier immer die koordinierte Weltzeit)

 

Alle weiteren Daten sind Beiwerk und blähen unseren RSS-Feed nur auf. Aber wer kommt denn schon ohne Beiwerk aus?

Interessant sind natürlich auch

  1. Die momentane IP-Adresse
  2. Die eingestellte Sprache
  3. Der aktuelle Ort (Stadt) bzw. das Land
  4. Die Region (hier kommen wir später darauf zurück)
  5. Die Geokoordinaten (geographische Länge und Breite)

Umsetzung:

Die erste Schwierigkeit besteht darin aus der IP-Adresse den aktuellen Standort zu ermitteln.

Hierfür gibt es eine Menge Webservices, die aber alle nicht sehr aktuell sind und ihre Daten nicht so einfach zur Verfügung stellen wollen.

Als die beste aller Möglichkeiten hat sich die MaxMind GeoLight City Datenbank in Verbindung mit dem Google Maps UserControl von SubGurim.net gezeigt.
Nachteil hier – die Datenbank ist 30MB schwer und muss jeden Monat aktualisiert werden, da sich die IP-Adressen ständig ändern. Was soviel heißt wie, je länger der Monat dauert je ungenauer die Angaben.

Abhilfe würde hier die kostenpflichtige Datenbank schaffen – diese ist zwar noch Größer wird aber in einem kürzeren Zyklus aktualisiert. Da ich das Ganze aber nur als Hobby-Projekt umsetzen will, sind mir die Kosten dafür zu hoch.

Die kostenpflichtige Datenbank gibt es ebenfalls hier (GeoCity) und kann natürlich auch mit dem gleichen UserControl betrieben werden.

Ich hab mich für das Binary Format der Datenbank entschieden, da diese Version in Verbindung mit dem SubGurim UserControl sehr schnell ist. Die Zugriffszeiten auf meinen RSS-Feed liegen bei etwa 0.5 Sekunden.

Das SubGurim Control wurde für ASP.NET 2.0 entwickelt, arbeitet aber auch im Framework 4 ohne Probleme.

Bindet man nun alles ein – ein Lesen der Dokumentation hilft wie immer weiter - reduziert sich der Code zur Ermittlung der Geokoordinaten auf


Dim location As Location = GetLocationFromIP(userIP)

Im Prinzip schon fast alles. Nur war es nicht unser Ziel die Geokoordinaten zu ermitteln, wir wollten die Uhrzeit und Zeitzone am aktuellen Standort ermitteln.

Anmerkung:

Die Klasse Location ist hier ein Teil des SubGurim.Net UserControls und enthält neben den GeoKoordinaten auch die ermittelte Stadt, das Land (beides als Klartext) und die Region als Kürzel (z.B. 02 = Bayern, 05 = Hessen).

Da ich für die nächsten Schritte keine weiteren Controls, Dll’s oder anderes frei verfügbares Werkzeug gefunden habe, musste ich die Verknüpfung der Standortdaten zur Zeitzone und das Decodieren des Regionkürzels selbst erledigen.

Überlegung:

Ein jeder Punkt dieser Welt gehört einer bestimmten Zeitzone an. Hier gilt es 3 Fälle zu beachten.

  1. Länder die als Ganzes einer Zeitzone angehören (z.B. Deutschland, Österreich, Schweiz)
  2. Länder die mehreren Zeitzonen angehören (z.B. USA, Russland, Indonesien)
  3. Gebiete die nicht erfasst werden können (Oceane und Wüsten)

1. Länder die als Ganzes einer Zeitzone angehören

Die einfachste Form, die Inhalte der Klasse Location können direkt zur Ermittlung der Zeitzone herangezogen werden.

Vereinfacht: Land = Zeitzone = Zeitverschiebung

2. Länder die mehreren Zeitzonen angehören

Nicht mehr ganz so einfach. Die Zeitzone wird hier anhand des Landes und der Region bestimmt.

Vereinfacht: Land (z.B. USA) - Region (z.B. CA - Kalifornien) = Zeitzone = Zeitverschiebung

Sonderfall: Eine Region enthält mehrere Zeitzonen (z.B. USA/Indiana – Eastern- und Central Time)

Hierfür habe ich noch keine Lösung – mir fehlen die genauen Daten – aber vermutlich wird es dann eine weitere Unterteilung anhand der ermittelten Stadt geben.

Land (z.B. USA) – Region (z.B. IN - Indiana) – Stadt (?) = Zeitzone = Zeitverschiebung

3. Gebiete die nicht erfasst werden können

Da es auf dem Meer oder in der Wüste keinen Provider bzw. keine Empfang gibt, kann dieser Fall ausgeschlossen werden.

Lösung hierfür wäre eine Ermittlung des Standortes via Geokoordinaten (GPS), was im Moment noch nicht umgesetzt wurde.

Die beschriebene Funktionalität hab ich in eine DLL gepackt, das alles beschreiben zu wollen würde hier aber zu weit führen.

Die Punkte 1. und 2. werden komplett durch eine List(of Structure) abgebildet, die alle Länder dieser Welt, inkl. den unter 2. beschriebenen Fällen, enthält.


    Public Structure myTimeZone
        Public Country As String
        Public Region As String
        Public TimeZoneInfo As TimeZoneInfo

        Public Sub New(ByVal c As String, ByVal r As String, ByVal tzi As TimeZoneInfo)
            Country = c
            Region = r
            TimeZoneInfo = tzi
        End Sub
    End Structure

Private mytimezones As New List(Of myTimeZone)

Die Klasse „TimeZoneInfo“ ist schon Bestandteil des Frameworks und bildet die auf dem jeweiligen System installierten Zeitzonen ab.

Die durch das SubGurim.Net UserControl bereitgestellten Informationen (Klasse Location) dienen somit als Filter für die Suche innerhalb der Liste. Die dadurch ermittelten Daten können nun den RSS-Feed füllen.

Erstellen eines RSS-Feeds:

Wie schon erwähnt ist ein RSS-Feed nichts anderes als eine XML-Datei welche durch eine Webseite zurückgegeben wird.

Dies kann auf vielfältige Art und Weiße geschehen.

Anfangs erstellte ich meinen Feed innerhalb einer WebForm (*.aspx). Später jedoch bin ich auf eine Methode gestoßen, die mir eleganter erschien, nämlich das Erstellen eines RSS-Feeds mittels eines „Generic Handlers“ (*.ashx)

Im Prinzip wird nichts anderes gemacht, als den kompletten Feed mittels LINQ zusammenzubauen und am Schluss als Response zurückzugeben.


Dim doc As New XDocument()

Dim rss As New XElement("rss")
rss.Add(New XAttribute("version", "2.0"))

Dim channel As New XElement("channel")
rss.Add(channel)

channel.Add(New XElement("title", "The TimeZoneNotifier RSS-Feed"))
channel.Add(New XElement("link", "http://TimeZoneNotifier.free-file-download.de"))
channel.Add(New XElement("description", "This site detects the timezone you're currently staying in !"))
channel.Add(New XElement("ttl", "60"))

Dim item As New XElement("item")

Channel.Add(item)

item.Add(New XElement("userIP", userIP))
item.Add(New XElement("userBrowser", userBrowser))
item.Add(New XElement("userLanguage", userLanguage))
item.Add(New XElement("userCity", userCity))
item.Add(New XElement("userRegion", userRegion))
item.Add(New XElement("userLongitude", userLongitude))
item.Add(New XElement("userLatitude", userLatitude))
item.Add(New XElement("userTimeOffsetHours", userTimeOffsetHours))
item.Add(New XElement("userTimeOffsetMinutes", userTimeOffsetMinutes))
item.Add(New XElement("CurrentUTCTime", CurrentUTCTime.ToString))
item.Add(New XElement("userTimeZone", userTimeZone))

doc.Add(rss)

(Den RSS-Feed wie er dann ausgegeben wird, sehen Sie am Ende dieses Artikels)

Sind wir erst einmal hier angekommen, wurde die meiste Arbeit schon erledigt. Der Rest besteht nur noch im Anzeigen der ermittelten Daten.

Anzeigen der Daten mittels einer WebForm:

Hier zum Verständnis, noch der Code für die Anzeige der Daten.

Da wir den Feed mittels einer Webanwendung abfragen, sind manche Informationen für den RSS-Feed nicht sichtbar. Bei diesen Daten verwerfen wir die Informationen die der RSS-Feed enthält einfach und füllen die Ausgabe mit den Werten der WebForm.


Protected Sub Page_Load(ByVal sender As Object, _
                    ByVal e As System.EventArgs) Handles Me.Load

if Postback Then
'RSS-Feed holen
    Dim retRss As New Xml.XmlDocument
    retRss.Load("http://timezonenotifier.free-file-download.de/rss.ashx?ip=" & _
                Request.UserHostAddress)
    'IP-Adresse das Feeds stimmt
    Me.LabelIP.Text = retRss.SelectSingleNode("/rss/channel/item/userIP").InnerText
    'Wurde eine nicht gültige IP-Adresse erkannt -> Anzeigen der Daten abbrechen
    If Me.LabelIP.Text = "No valid IP-Adress !!" Then
        Exit Sub
    End If
    'Kein Browser im RSS-Feed vorhanden, da vom Server aus angefragt wird
    Me.LabelBrowser.Text = Request.Browser.Browser
    'Keine Sprache im RSS-Feed vorhanden, da vom Server aus angefragt wird
    Try
        Dim ci As New CultureInfo(Request.UserLanguages(0))
        Me.LabelLanguage.Text = ci.DisplayName
    Catch ex As Exception
        Me.LabelLanguage.Text = "Unknown"
    End Try
    'erkannte Stadt
    Me.LabelCity.Text =
        retRss.SelectSingleNode("/rss/channel/item/userCity").InnerText
    'erkannte Region
    Me.LabelRegion.Text =
        retRss.SelectSingleNode("/rss/channel/item/userRegion").InnerText
    'erkannte Geogr. Länge
    Me.LabelLong.Text =
        retRss.SelectSingleNode("/rss/channel/item/userLongitude").InnerText
    'erkannte Geogr. Breite
    Me.LabelLat.Text =
        retRss.SelectSingleNode("/rss/channel/item/userLatitude").InnerText
    'erkannte Zeitverschiebung
    Dim _offsethours As Integer =
        CInt(retRss.SelectSingleNode("/rss/channel/item/userTimeOffsetHours").InnerText)
    Dim _offsetminutes As Integer =
        CInt(retRss.SelectSingleNode("/rss/channel/item/userTimeOffsetMinutes").InnerText)
    'aktuelle Zeit am Standort berechnen
    Dim tempRSS As String =
        retRss.SelectSingleNode("/rss/channel/item/CurrentUTCTime").InnerText
    Me.LabelCurrentTime.Text =
        CDate(tempRSS).AddHours(_offsethours).AddMinutes(_offsetminutes).ToLongDateString & " - " & CDate(tempRSS).AddHours(_offsethours).AddMinutes(_offsetminutes).ToLongTimeString

    Me.LabelCurrentTimeDiff.Text = _offsethours.ToString & _
        " h " & _offsetminutes.ToString & " min"
    'erkannte Zeitzone
    Me.LabelCurrentTimezone.Text =
        retRss.SelectSingleNode("/rss/channel/item/userTimeZone").InnerText
Else
    'kein Postback
End If
End Sub

Die Ergebnisse meiner Arbeit:

Das oben beschriebene hab ich unter http://timezonenotifier.free-file-download.de als ASP.NET Webseite veröffentlicht.
Den RSS-Feed gibt es unter http://timezonenotifier.free-file-download.de/rss.ashx
RSS-Feed (mein Standort)
<pre>


<rss version="2.0">
    <channel>
        <title>The TimeZoneNotifier RSS-Feed</title>
        <link>http://TimeZoneNotifier.free-file-download.de</link>
        <description>
            This site detects the timezone you're currently staying in !
        </description>
        <ttl>60</ttl>
        <item>
            <userIP>79.249.177.227</userIP>
            <userBrowser>Chrome</userBrowser>
            <userLanguage>Deutsch (Deutschland)</userLanguage>
            <userCity>Mering / Germany</userCity>
            <userRegion>Bayern</userRegion>
            <userLongitude>10,9833</userLongitude>
            <userLatitude>48,2667</userLatitude>
            <userTimeOffsetHours>2</userTimeOffsetHours>
            <userTimeOffsetMinutes>0</userTimeOffsetMinutes>
            <CurrentUTCTime>07.06.2011 09:40:14</CurrentUTCTime>
            <userTimeZone>
                (UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien
            </userTimeZone>
       </item>
    </channel>
</rss>

Quellen:

http://www.maxmind.com/app/geolitecity
http://de.googlemaps.subgurim.net/
http://lukencode.com/2010/05/19/ip-to-geo-location-in-asp-net-mvc/
http://www.aspcode.net/Creating-an-RSS-feed-for-your-ASPNET-site.aspx

Copyright:

Die Rechte der verwendeten Datenbanken und des UserControls liegt bei den Autoren.

Der angebotene RSS-Feed darf frei verwendet werden – eine Nennung der Quelle des Feeds bzw. der Adresse dieses Artikels wird vorausgesetzt.

Ein Dank geht an Sebastian Gross der mir einige Anregungen gegeben hat.

Dieser Service bleibt kostenlos, die Bedingungen zur Nutzung des RSS-Feed können sich jedoch jederzeit ändern.

Über den Autor

Michael Bernhard
Hobby-Programmierer von VB.NET, C# und ASP.NET Anwendungen. Hauptberuflich als Inbetriebnehmer bei der Böwe Systec GmbH beschäftigt. Vorzeigbare Projekte – TimeZone Notifier und TSV Neusäß Junioren.

kick it on dotnet-kicks.de

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

5Okt/102

VB.NET: Werte verschiedener Typen in einem Array unterbringen

Heute tauchte mal wieder die Frage auf, wie man das anstellen kann verschiedene Werte z.B.: String und Double in einen einzigen Array zu stecken. Dabei sollte dies möglich einfach zu handhaben sein, wie ein mehrdimensionales Array, bei dem jede Dimension einen anderen Typen hat.

Das geht so natürlich nicht, da ein Array immer nur einen Typ haben kann und alle Elemente dessen haben dann auch diesen einen Typ. Doch dieses Problem lässt sich ganz einfach umgehen, in dem man den Array auf eine eigene Klasse typisiert.

Mit anderen Worten, man schafft sich eine eigene Klasse an, die zwei Properties zur Verfügung stellt die jeweils ein String und einen Double Wert annehmen. Wenn man nun ein einfaches eindimensionales Array von diesem Typ erstellt hat man genau die Problemstellung erfüllt, wie im ersten Absatz beschrieben.

Konkret sieht das so aus, wir erstellen uns eine eigene Klasse die unsere Werte halten soll:


Public Class Wert

    Private _doubleWert As Double = 0.0
    Private _stringWert As String = ""

    'Konstruktor
    Public Sub New(ByVal sWert As String, ByVal dWert As Double)
        _stringWert = sWert
        _doubleWert = dWert
    End Sub

    Public Property DoubleWert As Double
        Get
            Return _doubleWert
        End Get
        Set(ByVal value As Double)
            _doubleWert = value
        End Set
    End Property

    Public Property StringWert As String
        Get
            Return _stringWert
        End Get
        Set(ByVal value As String)
            _stringWert = value
        End Set
    End Property
End Class

Wenn man nun einen Array vom Typ dieser Klasse erstellt kann man ganz bequem auf dessen Properties zugreifen:


    Private Sub Form1_Load() Handles MyBase.Load
        'Nutzung

        Dim meinArray(5) As Wert

        meinArray(0) = New Wert("test", 1.5)
        meinArray(1) = New Wert("hallo", 3.6)

        'Ausgabe
        MessageBox.Show("Array auf Index 1: String: " &amp;amp; meinArray(1).StringWert _
                        &amp;amp; " - Double: " &amp;amp; meinArray(1).DoubleWert.ToString())
    End Sub

Wie man sieht ganz einfach .-)
Weitere Informationen zu Arrays:
http://openbook.galileocomputing.de/vb_net/msvb020017.htm#Rxxmsvb020017282MehrdimensionaleArrays
http://msdn.microsoft.com/de-de/library/wak0wfyt.aspx

Nachtrag:

Ich wurde von Thomas Baumann in den Kommentaren darauf hingewiesen, dass man das ganze auch mit einem Struct realisieren kann. Dies kann in vielen Fällen sogar auch performanter sein als mit Klassen, da Strukturen auf dem Stack abgelegt werden, und nicht wie Klassen auf dem Heap, was einen großen Geschwindigkeitsschub bedeutet.

Ich habe mich für die Version mit Klassen entschieden, da ich diese noch weiter verarbeite und hier nicht auf Klassen verzichten kann.

Ein Beispiel für eine Umsetzung  mit Strukturen findet ihr in dem Kommentar von Thomas.

Weitere Infos zu Unterschieden zwischen Klassen und Strukturen 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.

12Aug/107

Tod den Modulen in VB.NET

In diesem Beitrag möchte ich ein paar Worte über Module unter VB.NET verlieren. Wie ihr dem Titel vielleicht auch schon entnehmen könnt steh ich diesen nicht allzu freundschaftlich gegenüber. Deswegen möchte ich in diesem Artikel erklären warum man keine Module benutzen sollte.

Wenn man Module nicht benutzen sollte warum gibt es sie dann?

Module gibt es in VB.NET nur aus einem Grund, nämlich um VB6 Programmierern den Umstieg auf die .NET Plattform zu erleichtern. Denn .NET ist komplett Objektorientiert und hier haben Module keinen Platz. Aber um es den Entwicklern einfacher zu machen bestehende Projekte auf die .NET Plattform zu porten wurden die Module mitgenommen.

Modul, Klasse wo ist eigentlich der Unterschied?

Wenn man sich so ein Modul anschaut hat es eine Enorme Ähnlichkeit mit einer gewöhnlichen Klasse, unterscheidet sich aber dennoch gewaltig von ihr.

Im Prinzip sind Module auch Klassen, die aber vielen Beschränkungen unterliegen. Hier die Hauptpunkte:

  • Alle Methoden und Variablen eines Moduls sind automatisch Shared, was bedeutet, dass man auf diese Methoden/Variablen zugreifen kann ohne eine Instanz der Klasse bilden zu müssen.
  • Alle Methoden und Variablen eines Moduls sind in dem gesamten Projekt verfügbar und sind somit Global - was nicht im Sinne von OOP ist. Der Programmierer kann dabei direkt auf die Methoden und Variablen zugreifen, dies erzeugt das Bild von Globalen Objekten und steht dabei im Gegensatz zu dem Klassischen Objektzugriff Object.Member.
  • Module sind automatisch NonInheritable, was die Instanzierung verhindert, desweiteren kann ein Modul auch nicht mit weiteren Schnittstellen wie z.B.: Interfaces erweitert werden.
  • Module sind zur Laufzeit einzigartig. Das bedeutet, dass alle Programmteile immer auf die selben Werte zugreifen, anders als bei einer Klasse wo jede Instanz eigene Werte hat.
  • Ein weiterer Punkt ist, dass Module nicht nach Außen hin sichtbar sind und somit eine Wiederverwendung ausschließen, was im totalen Gegensatz der OOP ist.

Warum werden Module dann so häufig benutzt?

Im Normalfall ist es entweder Unwissenheit oder Faulheit, oder beides. Denn viele Einsteiger (und auch alte VB6 Hasen) haben Probleme damit das Objektorientierte Prinzip zu verstehen, und da kommen die Module wie gerufen, denn diese ersparen die korrekte Deklarierung, man muss keine Namespaces beachten, die Instanzierung kann man sich auch sparen und man muss sich nicht mit Properties herumschlagen! Also geht im Endeffekt alles schneller aber dafür wird lauter Spaghetticode erzeugt!

Schlusswort

Wenn du noch relativ neu in der Objektorientierten Programmierung bist oder ein umsteiger von VB6, lass dich nicht davon verleiten schnellen unsauberen Code zu schreiben. Lass dir lieber etwas mehr Zeit um das OOP Konzept zu verstehen und mach es dann richtig. Je länger man diesen Pakt mit dem Teufel eingeht, desto schwieriger wird es dann später die alten Gewohnheiten los zulassen.

Ein weiterer Punkt ist, dass es unter C# keine Module gibt, daher wird man spätestens dann Probleme bekommen wenn man an einem C# Projekt mitarbeitet und dann gezwungen ist sauberer zu programmieren!

22Jul/101

XML: Den Namen eines XmlNode Aendern

Wenn man unter .NET mit XML Dokumenten arbeitet, nimmt man sehr oft Änderungen am Dokument vor. Doch letztens ist mir aufgefallen, dass eine der einfachsten Änderungen garnicht so einfach ist, nämlich wenn man den Namen eines XmlNode ändern will. Also die XmlNode.Name Eigenschaft.

Denn wenn man mal einen Blick ins MSDN wirft wird man feststellen, dass diese Eigenschaft read-only ist! Das ist natürlich blöd, da man nun keinen einfachen Weg hat den Namen des Knotens zu ändern.

Wenn man diesen aber unbedingt ändern will muss man das über einen kleinen Umweg machen. Man muss ein neues XmlElement erstellen und dabei den Namen ändern, anschließend noch alle Attribute und Kinder-Elemente des alten Elements übernehmen und an der Stelle des alten wieder in das XmlDocument einfügen.

Bild 1: Struktur & Änderungswunsch

Konkret sieht das Ganze so aus: C#


        private void Form1_Load(object sender, EventArgs e) {
            //XmlDomument erstellen und eine Xml-Datei laden
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(Application.StartupPath + "\\xml.xml");

            //Zur Demonstration die alte XML-Struktur ausgeben
            System.Console.WriteLine(xmlDoc.OuterXml);

            //Das Element das umbenannt werden soll heraussuchen
            XmlElement oldElement = (XmlElement)xmlDoc.GetElementsByTagName("books")[0];

            //Das herausgesuchte Element mit einem neuen Namen klonen
            XmlElement newElement = renameXmlElement(oldElement, "my-books");

            //Das alte Element mit dem neuen ersetzen
            oldElement.ParentNode.ReplaceChild(newElement, oldElement);

            //Kontrolle: Die neue Struktur ausgeben
            System.Console.WriteLine(xmlDoc.OuterXml);

            xmlDoc.Save(Application.StartupPath + "\\xml.xml");
        }

        public static XmlElement renameXmlElement(XmlElement e, string newname) {
            //Neues XmlElement mit neuem namen anlegen
            XmlElement newElement = e.OwnerDocument.CreateElement(newname);

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

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

            //Das neue XmlElement zurückgeben
            return newElement;
        }

Und hier das Ganze noch mal in VB.NET:


Private Sub Form1_Load(sender As Object, e As EventArgs)
	'XmlDomument erstellen und eine Xml-Datei laden
	Dim xmlDoc As New XmlDocument()
	xmlDoc.Load(Application.StartupPath + "\xml.xml")

	'Zur Demonstration die alte XML-Struktur ausgeben
	System.Console.WriteLine(xmlDoc.OuterXml)

	'Das Element das umbenannt werden soll heraussuchen
	Dim oldElement As XmlElement = DirectCast(xmlDoc.GetElementsByTagName("books")(0), XmlElement)

	'Das herausgesuchte Element mit einem neuen Namen klonen
	Dim newElement As XmlElement = renameXmlElement(oldElement, "my-books")

	'Das alte Element mit dem neuen ersetzen
	oldElement.ParentNode.ReplaceChild(newElement, oldElement)

	'Kontrolle: Die neue Struktur ausgeben
	System.Console.WriteLine(xmlDoc.OuterXml)

	xmlDoc.Save(Application.StartupPath + "\xml.xml")
End Sub

Public Shared Function renameXmlElement(e As XmlElement, newname As String) As XmlElement
	'Neues XmlElement mit neuem namen anlegen
	Dim newElement As XmlElement = e.OwnerDocument.CreateElement(newname)

	'Dem neuen XmlElement alle Attribute des alten Elements übergeben
	For Each a As XmlAttribute In e.Attributes
		newElement.SetAttributeNode(DirectCast(a, XmlAttribute))
	Next

	'Dem neuen XmlElement alle Kinder Elemente des alten Elements anhängen
	For Each n As XmlNode In e.ChildNodes
		newElement.AppendChild(n.Clone())
	Next

	'Das neue XmlElement zurückgeben
	Return newElement
End Function

Kompliziert ist es natürlich nicht, aber trotzdem etwas ärgerlich, dass man diese Methode dann doch selber implementieren muss.

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

19Jul/102

Goodbye VB.NET hallo C#

Es ist seltsam, wie man sich mit der Zeit entwickelt. Vielleicht ist es euch auch währen der letzten Beiträge auch aufgefallen. Nach fast einem Jahrzehnt bin ich nun von VB.NET auf C# umgestiegen, und möchte auch garnicht mehr zurück.

Der Umstieg ist bei mir aber nicht zufällig gekommen. Aktuell arbeite ich auch Beruflich immer öfter auch in Java Projekten mit und muss mich dann immer komplett umstellen was die Syntax angeht. Dies war immer besonders ärgerlich! Mit C# habe ich mich bis vor ein paar Monaten nie wirklich beschäftigt, ich wusste dass es existierte aber mehr auch nicht.

Die ersten Umstieggedanken kamen bei mir als ich Hilfe im Netz zu etwas komplexeren Themen gesucht habe. Da musste ich schnell feststellen, dass das eine C# Domäne war. Man findet nur wenig Support in VB.NET, obwohl es eigentlich die selbe Sprache ist.

Das Problem ist auch, dass VB eine Sprache ist, die damals für Anfänger entwickelt wurde und es DIE Sprache ist wenn es darum geht mit den Programmieren anzufangen. Was nichts schlimmes ist, aber dem entsprechend sind auch alle VB-Foren überfüllt mit Anfängern, die statt ein Buch zu lesen jede Kleinigkeit nachfragen und fertigen Code zum C&P erwarten.

Wer ein Beispiel sucht sollte hier, hier oder hier mal gucken...

Aber das war nicht der Hauptgrund für mich zum Wechseln, wie gesagt bot es sich an, da ich öfters mal mit Java in Kontakt komme, aber auch ein weiterer wichtiger Punkt spielte eine große Rolle.

Die Schreibarbeit

Denn der Visual Basic Code ist sehr viel umfangreicher, da fast alles ausgeschrieben wird und somit fast richtige Sätze entstehen. Das ist für Anfänger gut, aber wenn man länger damit arbeitet dann macht diese enorme Tipperei einen wirklich fertig, das macht dann auch Visual Studios IntelliSense auch nur wenig erträglicher. Zumahl habe ich die Erfahrung gemacht, dass je anspruchsvoller ein Projekt mit der Zeit wird desto komplexer wird auch die Syntax von VB.

Diese einfache C-Ähnliche Syntax geht einfach viel schneller und angenehmer von den Händen. Jaja, natürlich ist das alles Geschmackssache, ich will hier ja auch niemanden überzeugen, sondern einfach nur meine Gedanken dazu schreiben.

Trotzdem will ich hier die Frage in den Raum stellen, welche der beiden Sprachen ihr lieber benutzt und was eure Argumente für oder gegen "eure" Sprache sind. Nebenbei habe ich hier rechts in der Seitenleiste eine kleine Umfrage gestartet, da könnt ihr ja mal mit abstimmen!

Die Zukunft

Vielleicht werde ich die Nächsten Tutorials auch Bi-Lingual verfassen, aber im Verlauf der Zeit werden wohl alle Beispiele hier in C# sein.

Übrigens: Im web gibt es viele Anbieter, die web basierte Übersetzungstools für VB.NET zu C# und umgekehrt anbieten. Diese wandeln den Code zu ca 90% zuverlässig in die jeweils andere Sprache um, es sind nur noch kleine kosmetische Eingriffe nötig. Ich benutze da gerne den Converter von Developerfusion

29Apr/100

VB.NET: Einstellungen in der Windows Registry Speichern

In so gut wie jedem Computerprogramm müssen Informationen wie Einstellungen oder Benutzerdaten gespeichert werden. Um dies zu realisieren gibt es viele Ansätze wie zB: eine Datenbank, eine XML oder INI Datei oder direkt in der Registry. In diesem kleinem Tutorial werde ich auf die letztere Methode eingehen.

Zu nächst sei gesagt, dass ich es euch nicht empfehlen würde eine Einstellungen in der Registry zu sichern. Das hat folgende Gründe:

  • Die Daten sind sehr versteckt und nicht jeder Benutzer weiß, wie man an diese herankommt
  • Man kann diese nur sehr schwer sichern oder auf ein anderen System umziehen
  • Es wird oft vergessen diese Einstellungen aus der Registry zu löschen, sodass diese als "Leichen" im System bleiben
  • Speicherort ist von der Version des Programms abhängig

Auf der anderen Seite gibts aber auch natürlich Vorteile:

  • Einstellungen sind vor ungeübten Nutzern "geschützt"
  • Sehr geringer Programmier-Aufwand
  • Einstellungen können oft auch nach einer Neuinstallation weiter verwendet werden. (Kann auch zum Nachteil werden wenn Einstellungen fehlerhaft sind)

Besonders in früheren Windows Zeiten wurden fast alle Einstellungen in der Registry gesichert. Inzwischen setzt sich die Sicherung der Daten in XML Dateien durch und das .NET Framework bietet seit der 3er Version auch den Namensraum My.Settings mit denen diese XML Dateien automatisch erzeugt werden. Aber heute scheuen wir uns mal den anderen Weg an!

Wie oben bereits erwähnt kann man mit sehr wenig Aufwand seine Einstellungen in der Registry sichern, diese werden dabei hier gesichert:

HKEY_CURRENT_USER\Software\[Unternehmen]\[Programm]\[Version]

Hier werden die Einstellungen gesichert, dabei werden die Variablen in den Klammern automatisch gefüllt.

.Dazu bietet uns der Application-Namesraum alles was wir benötigen. Was wir aber noch machen sollten ist die Überprüfung der Werte und das Exceptionhandling.

Alles was man zum speichern eines Werts angeben muss ist der Name unter dem der Wert gespeichert werden soll und der Wert selbst. Alle Werte werden dann automatisch unter dem oberen Pfad abgelegt.


	'''
	''' Speichert einen Wert unter angegebenem Namen in der Registry
	'''
	Public Sub SaveSetting(ByVal name As String, ByVal value As String)
	    Try
	      	Application.UserAppDataRegistry.SetValue(name, value)
	    Catch ex As Exception
	      	debug.WriteLine("Fehler: " &amp; ex.Message)
	    End Try
  	End Sub

Um die Werte nun wieder auszulesen benötigt man lediglich den Namen, den man zum speichern benutzt hat und man bekommt den Wert aus der Registry geworfen!


	'''
	''' Lädt den String aus der Regitry passend zu dem angegebenem Namen
	'''
	Public Function LoadSetting(ByVal name As String) As String
		Try
			If Not Application.UserAppDataRegistry.GetValue(name) Is Nothing Then
				Return CType(Application.UserAppDataRegistry.GetValue(name), String)
		  	Else
		    	Return String.Empty
		  	End If

		Catch ex As Exception
		  	debug.WriteLine("Fehler: " &amp; ex.Message)
		  	Return String.Empty
		End Try
	End Function

Hier muss man aber beachten, dass das Programm, das die Werte ausließt die selbe Version haben muss wie das was sie in die Registry geschrieben hat, da der Pfad (siehe oben) nicht mehr passt!

Wenn man die oberen Methoden implementeirt hat kann man nun die Einstellungen sehr bequem speichern:


	'Die Einstellung aller Controls auf der Form speichern
	Sub Button1Click(sender As Object, e As EventArgs)
		Dim rs As New RegSaver 'Diese Klasse beinhaltet die oben definierten Funktionen

		'Daten für alle Controls speichern
		For Each c As Control In Me.Controls
			If TypeOf c Is RadioButton Then
				Dim a As RadioButton = CType(c, RadioButton)
				rs.SaveSetting(a.Name,a.Checked)
			End If

			If TypeOf c Is CheckBox Then
				Dim a As CheckBox = CType(c, CheckBox)
				rs.SaveSetting(a.Name,a.Checked)
			End If

			If TypeOf c Is TextBox Then
				Dim a As TextBox = CType(c, TextBox)
				rs.SaveSetting(a.Name,a.text)
			End If
		Next
	End Sub

und auch wieder auslesen:


	'Die werte aller Controls wieder laden
	Sub MainFormLoad(sender As Object, e As EventArgs)
		Dim rs As New RegSaver
				'Daten für alle Controls laden
		For Each c As Control In Me.Controls
			Try	'beim ersten starten gibt es keinen wert der gecastet werden kann!
				If TypeOf c Is RadioButton Then
					Dim a As RadioButton = CType(c, RadioButton)
					a.Checked = cbool(rs.loadsetting(a.Name))
				End If

				If TypeOf c Is CheckBox Then
					Dim a As CheckBox = CType(c, CheckBox)
					a.Checked = cbool(rs.loadSetting(a.Name))
				End If
			Catch ex As Exception
				debug.WriteLine("Fehler: " &amp; ex.Message)
			End Try

			If TypeOf c Is TextBox Then
				Dim a As TextBox = CType(c, TextBox)
				a.Text = rs.loadSetting(a.Name)
			End If
		Next
	End Sub

Wenn die Werte das erste Malgeladen werden, kann es zu fehlern kommen, da man NULL-Strings zurückbekommt, deswegen sollte man hier immer die Exceptions catchen! Falls es in der Registry noch keine passenden Einstellung zu dem Namen gibt, wird diese beim Laden automatisch angelegt!

Bild 1: Demoapplikation mit den geladenen Einstellungen

Wenn der Code durchgelaufen ist wird folgende Struktur in der Registry angelegt:

Bild 2: Die Einstellungen in der Registry

Natürlich bleibt es euch überlassen, wie ihr die Einstellungen benennet, ich habe hier den Namen des Controls verwendet um dessen Status zu speichern, damit ich nicht so viel Code schreiben musste.

Welche Methode ihr nutzt müsst ihr natürlich selber an Hand des Projekts entscheiden, kennen sollte man sie aber alle.

Ein Kleines Demoprojekt gibts hier:

Download: Diesmal ausnahmsweise ein SharpDevelop Projekt

18Mrz/100

VB.NET: Netzwerkadapter Einstellungen auslesen

Das .NET Framework bietet viele sehr nützliche Schnittstellen, so auch um Netzwerkinformationen auszulesen. Alles was man braucht findet man in dem System.Net Namensraum. In dieser Demo möchte ich euch zeigen, wie man alle verfügbaren Netzwerkadapter auflistet und deren Informationen ausließt.

So lässt sich sehr einfach eine Auflistung aller Netzwerkschnittstellen des Systems zusammenstellen:


    Dim adapter As NetworkInterface     'Eine Netzwerkadapter Instanz
    Dim adapters As NetworkInterface()  'Array mit allen Netzwerkadaptern

    Public Sub DisplayDnsConfiguration()
        adapters = NetworkInterface.GetAllNetworkInterfaces()

        'Alle Adapter in der Liste durchlaufen
        For Each Me.adapter In adapters
            Dim properties As IPInterfaceProperties = adapter.GetIPProperties()

            lstNetworks.Items.Add(adapter.Description)
        Next adapter
    End Sub

Hier wird die lstNetworks mit allen verfügbaren Netzwerkadaptern gefüllt. Da wir nun die Bezeichnungen der Netzwerkadapter haben können wir uns nun daran machen deren Einstellungen auszulesen.

Die Einstellungen sind hierbei genauso schnell ausgelesen, denn wenn man erst die richtige NetworkInterface-Instanz erwischt hat kann man ganz bequem auf die Einstellungen zugreifen:

bild1Bild 1: Ein paar Eigenschaften des Netzwerkadapters

Den dazugehörigen Code spare ich mir mal an dieser Stelle, da es einfach zu viel Text ist. Ihr könnt diesen aber natürlich in dem Demoprojekt nachgucken.

Wie in Bild 1 auch zu erkennen ist hat man auch Zugriff auf die übertragenen Bytes in beide Richtungen. So kann man zB. auch sehr einfach die aktuelle Netzwerkauslastung bestimmen, indem man den alten Wert speichert und mit dem neuen Wert nach einer Sekunde vergleicht. Dadurch bekommt man die übertragenen Bytes pro Sekunde. (Dies könnt ihr auch in dem Demoprojekt nachgucken)

bild2

Bild 2: Das Demoprogramm in Aktion (klicken zum Vergrößern)

In dem Demoprogramm habe ich mal versucht die am häufigsten benötigten Informationen einer Netzwerkschnittstelle zusammenzufassen, aber das meiste ist hier aber auch nur Copy’n’Paste!

Das Demoprogramm zeigt alle verfügbaren netzwerkschnittstellen, deren Eigenschaften sowie die aktuelle Adapterauslastung in KB/s.

vs2008demo

Get Adobe Flash playerPlugin by wpburn.com wordpress themes