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!
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.
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.
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.
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
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: " & 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: " & 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: " & 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:
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:
Bild 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)
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.
VB.NET: FitzBox IP erneuern ueber uPnP
Die FritzBox ist als Router weit verbreitet und bietet einen Haufen an Konfigurationsmöglichkeiten an. Eins der nützlichsten Features ist uPnP (Universal Plug and Play). Diese Schnittstelle bietet uns eine sehr bequeme Möglichkeit mit dem Gerät zu kommunizieren ohne dass man einen Benutzernamen oder ein Kennwort benötigt!
Wer öfter mal im Internet unterwegs ist, kennst sicherlich mehrere gute Gründe seine IP zu wechseln. Und das ist sehr einfach, denn alles was dafür nötig ist, ist eine erneute Einwahl ins Internet.
Dies kann man sehr einfach über die Benutzeroberfläche der FritzBox machen oder einfach "oldschool"-mäßig das Netzwerkkabel aus der FritzBox ziehen und wieder einstecken. (Letzteres erfordert ebenfalls keinen Benutzernamen und Passwort)
Doch wenn man des öfteren auf diese Funktionalität angewiesen ist, ist es doch relativ nervig und umständlich. Deswegen werden wir hier ein einfaches kleines Programm entwickeln, dass für uns den Router zu einer Neueinwahl ins Internet "zwingt".
Wie oben bereits erwöhnt heißt die Technologie dafür UPnP (Universal Plug and Play). Über diese Technologie lassen sich sehr viele Befehle an die Fritzbox senden, vorausgesetzt, man befindet sich in dem selben Netzwerk.
Von Statusabfragen über Auslastungen und Übertragung, lassen sich sogar Telefonnummern anwählen und Anrufe steuern, im Prinzip kann man alle Funktionen darüber ansteuern. Hier gibts eine nette Übersicht mit vielen Beispielen.
Wie werden die Anfragen eigentlich an die Fritzbox übertragen? Das geschieht sehr einfach über einen POST Aufruf, denn auf der Fritzbox (und auch auf den meisten anderen Routern) läuft ein kleiner Webserver denn man ganz einfach über den Browser erreicht, wenn man die adresse "fritz.box" in die Adresszeile tippt.
Dort ist die Fritzbox genauso aufgebaut wie eine gewöhnliche Webseite. Und diese nimmt auch POST und GET aufrufe entgegen. Genau hier setzen wir an und werden unsere Befehle rüber schicken.
Dim client As New TcpClient
client.Connect("fritz.box", 49000)
Dim stream As NetworkStream = client.GetStream
Dim bytes As Byte() = New Byte((My.Resources.msg.Length)) {}
bytes = Encoding.ASCII.GetBytes(My.Resources.msg)
stream.Write(bytes, 0, bytes.Length)
bytes = New Byte(1024) {}
Dim str As String = String.Empty
Dim count As Integer = stream.Read(bytes, 0, bytes.Length)
str = Encoding.ASCII.GetString(bytes, 0, count)
stream.Close()
client.Close()
Der Code ist sehr simpel, wir erstellen uns einen WebClient und verbinden uns mit der FritzBox. Nach der Verbindung senden wir ein POST Request raus und lesen anschließend die Antwort wieder aus.
Mit dem obigen Code senden wir folgenden Befehl an die Fritzbox:
POST /upnp/control/WANIPConn1 HTTP/1.1
HOST: fritz.box:49000
SOAPACTION: "urn:schemas-upnp-org:service:WANIPConnection:1#ForceTermination"
CONTENT-TYPE: text/xml ; charset="utf-8"
Content-Length: 293
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:ForceTermination xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1" />
</s:Body>
</s:Envelope>
Wie man sieht ist der Befehl ein gewöhnlicher POST Aufruf inkl. Header. Dieser übergibt einen Befehl urn:schemas-"upnp-org:service:WANIPConnection:1#ForceTermination", der der Fritzbox sagt die Verbindung ins Internet neu aufzubauen.
Man muss hierbei sehr aufpassen, besonders die Content-Length Angabe muss genaustens passen, da sonst ein Fehler als Antwort eintrudelt.
Wie immer gibts auch eine kleine Demoanwendung.
Download hier: Download [VS 2008 Projekt]
VB.NET: Saubere Klassen erstellen mit Properties
Heutzutage wird fast nurnoch Objektorientiert programmiert. Das bedeutet, dass ein größeres Projekt schnell mal auf eine Klassenanzahl von 100 und mehr wächst.
Damit man den Überblick behält und den Code, den man schon geschrieben hat möglichst wiederverwenden kann, sollte man ein paar grundlegende Regeln beachten.
In diesem Artikel möchte ich ein Beispiel zeigen, wie man eine Klasse in ein Projekt integriert und dank der benutzten Standardmethoden an ein Datagrid bindet.
Warum schreibe ich diesen Artikel überhaupt? Erst gestern habe wurde in einem Forum die Frage gestellt, warum man eine Liste (Of T) nicht als DataSource für ein DataGridView benutzen kann. Dabei ist das sehr wohl möglich, der User hat seine Klasse aber "unschön" gestaltet, weswegen sich die anderen Steuerelemente auch quer gestellt haben.
Hier ein Beispiel einer solchen "Unschönen" Klasse:
Public Class Auto
Public marke As String
Public motor As String
Public geschw As Integer
End Class
Dim al As New List(Of Auto)
al.Add(New Auto With {.marke = "VW", .motor = "Diesel", .geschw = 0})
al(0).geschw += 10
Wie man sieht sind hier alle Klassenvariablen öffentlich zugänglich, und die Klasse hat keinerlei Kontrolle darüber, wie sie befüllt werden. Ein weiterer Nachteil ist auch die Inkompabilität zu anderen Steuerelementen, da diese einen Standardkonformen Aufbau der Klasse erwarten. Finden sie bestimmte Elemente in der Klasse nicht so verweigern diese die Funktionalität!
Wie müsste diese Klasse also umgebaut werden, damit diese kompatibel zu anderen Objekten wird?
Public Class Auto
Private geschw As Integer
Public Sub New(ByVal marke As String, ByVal motor As String)
Me.Marke = marke
Me.Motor = motor
End Sub
Private _marke As String
Public Property Marke() As String
Get
Return _marke
End Get
Set(ByVal value As String)
_marke = value
End Set
End Property
Private _motor As String
Public Property Motor() As String
Get
Return _motor
End Get
Set(ByVal value As String)
_motor = value
End Set
End Property
Public Sub beschleunigen(ByVal a As String)
geschw += a
End Sub
Public Sub bremsen(ByVal a As String)
geschw -= a
End Sub
End Class
Wie man sieht ist die Klasse nun natürlich um einiges länger geworden. Leider kann man das zur zeit noch nicht anders machen. Aber Abhilfe kommt in der neuen VB.NET Version, wo man die properties so wie in C# 3.0 erstellen kann.
Die Zugriffe auf die Klassenvariablen werden nun über properties gesteuert, die nun auch zB. überprüfen könnten, ob der übergebene Wert überhaupt passt und gegebenenfalls noch Änderungen daran vornehmen.
Aber was bringt uns nun dieser längere Aufbau der Klasse? Nun, wenn wir unser Objekt, das aus dieser Klasse entsteht an andere Objekte übergeben, erwarten diese, dass sie properties vorfinden, mit denen sie arbeiten können.
Hat die Klasse keine Properties so können wir auch bestimmte Funktionen auch nicht benutzen. Ich möchte das mal an einem DataGridView demonstrieren, da dieses Element von den properties sehr schön Gebrauch macht!
Fügt man folgenden Code einem Form mit einer DataGridView hinzu:
Private Sub Form1_Load() Handles MyBase.Load
Dim autos As New List(Of Auto)
autos.Add(New Auto("VW", "Diesel"))
autos.Add(New Auto("Audi", "Benziner"))
autos.Add(New Auto("Mercedes", "Diesel"))
autos.Add(New Auto("Renault", "Diesel"))
autos.Add(New Auto("BMW", "Beziner"))
Dim bs As New BindingSource
bs.DataSource = autos
Me.DataGridView1.DataSource = bs
End Sub
erhält man folgendes Bild:
Bild 1: Die Datagridview stellt alle Informationen angenehm dar
Wie man sieht konnte man mit nur 3 Zeilen Code (Abgesehen vom Erzeugen der Werte) alle Daten der Liste visualisieren. Man beachte desweiteren, dass sogar die Namen der Properties (Marke und Motor) ausgelesen werden und der richtigen Tabelle zugeordnet werden!
Dies könnte man natürlich auch ohne Properties hinbekommen, doch würde man viel mehr Aufwand betreiben müssen und man denke an die Zukunft, in der unser Modell sehr einfach zu warten wäre.
Hier gibts ein kleines Demoprojekt: Download [VS 2008]
Kostenloses E-Book von Microsoft zum Thema VB.NET
Es ist wieder soweit. Microsoft bietet wiedermal ein Buch komplett gratis zum Download an. Dieses Mal ist Visual Basic.NET das Thema.
Das Buch ist von dem Hauseigenen Verlag MicrosoftPress, welches einen sehr guten Ruf genießt. Ich selber habe mehrere Bücher von denen.
Dieses Buch kostet normalerweise 59 € und ist somit ein sehr gutes "Schnäppchen". Dabei kann es sich vom Inhalt sehen lassen. Dieses Buch vermittelt alles von den Grundlagen, bishin zur fortgeschrittenen Programmierung mit Datenbanken.
Auch wenn ihr kein VB.NET Programmierer Seit kann ich es euch trotzdem empfehlen dieses Buch zu laden, den man weiß ja nie!
Download: Hier
Windows Aero Glass in eigenen Projekten nutzen Teil 2
In dem ersten Teil dieses Beitrags, der nun schon einige Monate her ist habe ich gezeigt, wie man die Aero Glas Effekte auch in eigene Projekte einbauen kann. Doch leider waren da noch einige Probleme, so konnten wir nur das ganze Formular in Glas verwandeln und die Darstellung der Steuerelemente war falsch.
Genau hier möchte ich den zweiten Teil ansetzen und diese letzten Probleme aus der Welt schaffen!
Wir beginnen genauso wie im ersten Teil nur nennen wir unsere Klasse diesmal GlassForm um nicht durcheinander zu kommen.
Diesmal importieren wir eine weitere Methode aus der Windows API, nämlich DwmIsCompositionEnabled() die es uns ermöglicht zu prüfen, ob diese Effekte, die wir nutzen möchten auch verfügbar sind.
Die zweite Funktion DwmExtendFrameIntoClientArea müsste aus Teil 1 schon bekannt sein, diese hilft uns die Formränder zu vergrößern und somit die Glasoberfläche ins Formularinnere zu holen.
Da wir dieses Mal die eine Option für alle Ränder haben möchten, sodass wir zB. nur den oberen Rand in Glas verwandeln, benötigen wir ein Structure, in dem wir die Werte für jeden Rand speichern können und den wir dann später der Funktion DwmExtendFrameIntoClientArea übergeben.
So sieht unsere Klasse aus:
Imports System.Runtime.InteropServices
Public Class GlassForm
_
Private Shared Sub DwmExtendFrameIntoClientArea _
(ByVal hwnd As IntPtr, ByRef margin As AeroDemo2.MARGINS)
End Sub
_
Public Shared Function DwmIsCompositionEnabled() As Boolean
End Function
Public Shared Function ExtendGlassFrame _
(ByVal hwnd As IntPtr, ByVal margin As AeroDemo2.MARGINS) As Boolean
GlassForm.DwmExtendFrameIntoClientArea(hwnd, margin)
Return True
End Function
End Class
Public Structure MARGINS
Public left As Integer
Public right As Integer
Public top As Integer
Public bottom As Integer
Public Sub New(ByVal l As Integer, ByVal r As Integer, _
ByVal t As Integer, ByVal b As Integer)
left = l
right = r
top = t
bottom = b
End Sub
End Structure
Wie man sieht enthält die Structure MARGINS je eine Variable für jede Seite des Formulars, wobei der Wert den sie beinhaltet den Abstand zum eigentlichen Rahmen angibt!
Wechseln wir nun zur Klasse Form1, da wir hier fertig sind. In der Form_Load Methode können wir uns nun einen neuen Structure erstellen und diesem unsere gewünschten Rahmenabstände übergeben. Daraufhin rufen wir unsere Funktion auf die für sns das Glas herzaubern soll.
Doch wenn wir das Programm nun starten werden wir kein Glas vorfinden! Das liegt daran, dass Windows die Farbe Schwarz für Transparente Darstellung auf Glas verwendet. Das bedeutet, dass alle Steuerelemente, die Schwarze Farbe enthalten transparent und somit zu Glas werden! Das konntet ihr aber auch im ersten Teil schon beobachten!
Dieses Problem nehmen wir aber später nocheinmal in Angriff! Kommen wir ersteinmal zu unserem Glasfenster!
Damit wir auch wirklich Glas sehen, müssen wir ersteinmal das Fenster mit schwarzer Farbe füllen. Dazu schreiben wir folgendes in die Form_Paint Methode:
Dim margins As AeroDemo2.MARGINS
Private Sub Form1_Load() Handles MyBase.Load
'Den "normalen" Bereich festlegen
margins = New AeroDemo2.MARGINS(0, 0, 35, 50)
'Die Fensterränder erweitern
GlassForm.ExtendGlassFrame(Me.Handle, margins)
End Sub
Wenn man das Programm startet, müsste das ca so aussehen:
Bild 1: Alles ist schwarz, bis auf den Glasteil
Bitte nicht erschrecken, denn eigentlich ist es keine Überraschung, dass wir nur schwarz sehen, denn wir haben im letzten Schritt alles Schwarz gefärbt. Der Aero Teil ist dadurch nun transparent geworden!
Da das Schwarze aber ziemlich blöd aussieht möchten wir das natürlich wieder auf unsere Standardeinstellung ändern. Dazu müssen wir diesen schwarzen Rechteck da oben nun wieder mit der Ausgangsfarbe füllen! Das machen wir so:
Private Sub Form1_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
If GlassForm.DwmIsCompositionEnabled() = True Then
'Hintergrund Schwarz füllen, um den Glass effenkt zu erhalten
e.Graphics.Clear(Color.Black)
'Nun das Stück, dass nicht aus Glas sein soll wieder "zurückmalen"
Dim clientArea As New Rectangle(margins.left, margins.top, _
Me.ClientRectangle.Width - margins.left - margins.right, _
Me.ClientRectangle.Height - margins.top - margins.bottom)
Dim b As Brush = New SolidBrush(Me.BackColor)
e.Graphics.FillRectangle(b, clientArea)
End If
End Sub
Nachdem wir das Rechteck wieder mit der Ausgangsfarbe bemalt haben sieht das schon viel angenehmer aus:
Bild 2: Das Fenster hat nun wieder die "Normale" Farbe
Was natürlich nun auffällt ist das Komplett schwarze Label (ja, es ist wirklich eins) und der nicht lesbare Button.
Dies sind die oben besagten Probleme von GDI Objekten. Diese werden Standardmäßig genutzt um Abwärtskompabilität zu gewährleisten. Schaltet man aber das TextRendering auf GDI+ so werden auch die Schriften korrekt dargestellt.
Um das zu ändern muss die Option SetCompatibleTextRenderingDefault auf true gesetzt werden. Dies muss aber geschehen bevor das Programm gestartet wird. Also klicken wir mit der Rechten Maustaste auf unser Projekt -> Hinzufügen -> Modul
In das Modul schreibt ihr nun folgendes rein:
Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(True)
Application.Run(New Form1) 'Name eurer Form
End Sub
Nun müssen wir nurnoch dafür sorgen, dass unser Projekt durch das Modul gestartet wird. Das machen wir in dem wir auf "Projekt" oben im Menü klicken und dann auf Eigenschaften. Hier muss nun der Haken bei "Anwendungsframework aktivieren" herausnehmen und dann bei "Startobjekt" "Sub Main" wählen:
Bild 3: Das Projekt über das Modul starten
Wenn wir das Programm nun starten sollte man die Texte und auch den Button lesen können!
Nun sind wir eigentlich schon durch. Was noch zu sagen bleibt ist, dass man es möglichst vermeiden sollte Steuerelemente auf dem Glas zu platzieren, da diese sehr oft falsch dargestellt werden.
Wenn ihr unbedingt Schrift auf Glas haben wollt solltet ihr diese Schrift auf das Formular Zeichnen und nicht einfach über ein Label dort platzieren. Das selbe gilt auch für Grafiken. Leider kann man nicht einfach mit der Graphics.DrawString() Methode Zeichnen sondern muss den Text erst in ein GraphicsPath Objekt "zeichnen" bevor man es dem Glas übergibt. So stellt man sicher, dass die Textverläufe richtig dargestellt werden!
Hier ein Beispiel:
'Text zeichnen
Dim txt = Me.CreateGraphics()
Dim path = New GraphicsPath()
path.AddString("Ich bin ein gezeichneter Text", _
New FontFamily("Tahoma"), CInt(FontStyle.Regular), _
20, New Point(60, 0), StringFormat.GenericDefault)
Dim brush = New PathGradientBrush(path)
Dim clr As Color() = {Color.Transparent}
txt.SmoothingMode = SmoothingMode.HighQuality
brush.CenterColor = Color.White
brush.SurroundColors = clr
txt.FillPath(Brushes.Black, path)
brush.Dispose()
path.Dispose()
txt.Dispose()
Und wie es aussehen könnte:
Bild 4: So könnte ein Aero Formular aussehen
Weitere Informationen auch mit Beispielen findet ihr hier: (englisch & C#)
Microsoft
CodeProject
CodeProject
Wie üblich habe ich das Ganze in ein kleines Demoprojekt verpackt: Download
Wie hats euch gefallen, möchtet ihr einen dritten Teil? Freue mich auf euer Feedback!
Teil 1 des Tutorials gibts hier
Zum dritten Teil gehts hier lang


