Add-On Perle: Visual Studio Productivity Power Tools
Wer mit Visual Studio 2010 arbeitet und Visual Studio Productivity Power Tools noch nicht kennt sollte schnellst möglich handeln und sich dieses Add-On installieren, denn es macht die Arbeit mit Visual Studio sehr viel angenehmer (kaum zu glauben dass das eigentlich möglich ist
)
Diese Power Tools verbessern hierbei viele Kleinigkeiten, die den Arbeitsprozess angenehmer machen. Hier ein paar Beispiele:
- STRG+Mausklick auf eine Variable befördert euch zu der Klasse
- STRG+1/2/3 Bringen bequeme Sprungmöglichkeiten innerhalb der markierten Klasse
- ALT+Pfeil hoch/runter verschiebt den markierten Text
- Ein neuer Solution navigator
- Ein verbesserter "Verweis hinzufügen" Dialog mit Suchfunktion
- Farbige und pinnbare Tabs
- und und und
Schnell laden, installieren und genießen! Ich kann schon jetzt nicht mehr ohne!
.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.
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;
}
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
Microsoft SQL CE: Blobs Speichern und auslesen
In diesem kleinem Tutorial möchte ich euch zeigen, wie man BLOBs (Binary Large Objects) in die Compact Edition vom Microsofts SQL Server speichern und wieder laden kann. Dies soll eine Art Weiterführung von dem letzten Einstiegspost sein. Ich kann euch aber nicht versprechen, dass noch weitere Beiträge zu dem Thema kommen werden.
Auch wen Relationale Datenbanken überwiegend dazu benutzt werden um Text-Basierte Informationen zu speichern, wie Benutzernamen, IP Adressen oder andere Texte, kommt es doch häufiger vor, dass man auch Dateien wie Bilder in eine Datenbank legen möchte. Dies ist zB. nützlich wenn man eine Benutzerdatenbank hat und zu jedem Benutzer ein Bild hinterlegen will.
Das Vorgehen an Sich ist dabei sehr ähnlich, ihr erstellt ein SqlCeCommand und fügt dem wie gewohnt einige Parameter hinzu:
using (SqlCeConnection con = new SqlCeConnection(conString)) {
con.Open();
using (SqlCeCommand com = new SqlCeCommand
("INSERT INTO cp_image (date, image) VALUES (@Date, @Filename)", con)) {
com.Parameters.AddWithValue("@Date", DateTime.Now.ToString());
com.Parameters.AddWithValue("@Filename",
System.IO.File.ReadAllBytes(filename)).SqlDbType = SqlDbType.Image;
com.ExecuteNonQuery();
}
}
Absolut nichts neues wie ihr sehen könnt, wir geben die Datei, die wir anhängen wollen einfach als byte-Array als Parameter mit.
Sehr wichtig ist hierbei folgende Stelle in Zeile 8:
.SqlDbType = SqlDbType.Image;
Wenn ihr diese Information nicht mit angebt, werdet ihr Probleme bekommen, sobald ihr eine Datei in die Datenbank schreiben wollt die größer ist als 8 KB!!! Denn wenn man einen Parameter angibt ohne dessen Typ mit anzugeben versucht SQLCE den Typ selbst zu bestimmen und wählt dazu dann den Typ varbinary, dessen Größe auf 8000 Byte beschränkt ist!
Mit dem Ausführen des SqlCeCommands sollte die Datei bzw. das Bild in diesem Fall dann in eure Datenbank Datei wandern.
Das Auslesen der Datei aus der Datenbank gestaltet sich ebenfalls nicht kompliziert. Hier meine Funktion um ein Bild aus der Datenbank zu lesen:
public Image getImageFromDB(int id) {
Image img;
byte[] imgData = null;
using (SqlCeConnection con = new SqlCeConnection(conString)) {
con.Open();
using (SqlCeCommand com = new SqlCeCommand
("SELECT image FROM cp_image WHERE id = '" + id.ToString() + "'", con)) {
using (SqlCeDataReader dr = com.ExecuteReader()) {
while (dr.Read()) {
imgData = (byte[])dr["image"];
}
}
}
}
if (imgData != null) {
//Es wurde ein Bild geladen
MemoryStream ms = new MemoryStream(imgData);
img = Image.FromStream(ms);
return img;
}
return null;
}
Dazu benutze ich einfach nur einen gewöhnlichen DataAdapter, mit dem ich aus der Spalte namens "image" die Bildinformationen auslese, diese müssen noch in ein byte-Array gecastet werden, da diese als Object geliefert werden.
Zum Schluss wird noch das Byte-Array in ein System.Drawing.Image verwandelt und zurückgegeben. Dies kann man dann zB. direkt in eine PictureBox laden.
Hier ist die Tabelle, die ich in diesem Beispiel benutzt habe:
Wie ihr eine Verbindung zur Datenbank herstellt und normale Select- und Insert-Befehle benutzt könnt ihr im Ersten Teil nachlesen.
Windows Aero Glas in eigenen Projekten Nutzen Teil 4 – Formulare ohne Border
Dieses Thema hatten wir hier schon oft, und trotzdem gibt es immer noch was neues zu berichten. Windows Forms Fenster stellen sich ganz schön an wenn es darum geht ihre Steuerelemente und sich selbst richtig darzustellen, zu den Steuerelementen wird bald ein Teil 5 (und hoffentlich der letzte) kommen, hier geht es jetzt erst einmal um das (Boderlose) Formular an sich.
Wenn ihr nämlich mal versucht habt ein Windows Form Formular ohne Border (FormBorderStyle=None) mit der mit Glas-Optik zu nutzen habt ihr wohl innerhalb kürzester Zeit feststellen müssen, dass es nicht geht. Warum ist das so?
Nun, ich denke die meisten von euch benutzen die Win32API Funktion DwmExtendFrameIntoClient um das Fenster in eine Glasscheibe zu verwandeln - was auch vollkommen richtig ist, nur wenn man sich den Namen der Funktion durchließt wird man feststellen, dass deren Aufgabe es ist das Frame (den Rahmen) auf das Formular zu erweitern, was auch super funktioniert, solange man einen Rahmen hat. Stellt man nun aber die Eigenschaft FormBorderStyle auf None, hat die Form keinen Rahmen mehr und dementsprechend kann man diesen auch nicht in die Form erweitern.
Was nun? Es muss doch irgendwie gehen!
Natürlich geht das, dazu müssen wir nur eine andere DWM Funktion aus der Win32API Verwenden, nämlich DwmEnableBlurBehindWindow. Diese Funktion macht genau das selbe, nämlich einen definierten Bereich des Formulars in Glas verwandeln.
Aber diese Funktion bietet noch mehr Spielereien, so kann man auch geometrische Figuren in Glas verwandeln oder nur bestimmte Teile einer Form verglasen. Siehe folgende Bilder:
Bild 1: Ein Paar der Möglichkeiten das Glas zu verteilen. (Natürlich kann man den FormBorderStyle auch auf None stellen, dann würde einfach nur der Rahmen fehlen)
Dazu benötigen wir folgenden Code. 1: Das Befüllen des Fensterinhalts mit Glas:
//Import der nötigen Funktion aus der DLL
[System.Runtime.InteropServices.DllImport("dwmapi")]
private static extern int DwmEnableBlurBehindWindow(
System.IntPtr hWnd, ref DWM_BLURBEHIND pBlurBehind);
//Funktion um ein Rechtek auf dem Formular zu malen
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
Wie ihr sehen könnt, ist der Letzte Parameter vom Typ DWM_BLURBEHIND - dies ist ein Struct, welches wir nun erstmal anlegen müssen. (Auf die zweite Funktion komme ich gleich zu sprechen)
public struct DWM_BLURBEHIND
{
public int dwFlags;
public bool fEnable;
public System.IntPtr hRgnBlur;//HRGN
public bool fTransitionOnMaximized;
}
Nun müssen wir nur noch das Struct anlegen, mit Parametern füllen und der Funktion übergeben:
private void Form1_Load(object sender, EventArgs e)
{
//Das Rechteck vorbereiten, dieses soll so groß sein
//wie die Fläche die mit Glas befüllt werde soll
//in diesem Fall soll es die gesamte Form füllen
hr = CreateRectRgn(0, 0, this.Width, this.Height);
//Anlegen des Scructs
DWM_BLURBEHIND dbb;
dbb.fEnable = true;
dbb.dwFlags = 1 | 2; //Konstanten mehr in der API
dbb.hRgnBlur = hr;
dbb.fTransitionOnMaximized = true;
//Aufruf der Funktion
DwmEnableBlurBehindWindow(this.Handle, ref dbb);
}
Zum Schluss sollte man noch dafür sorgen, dass die Form schwarz bemalt wird, damit wir das Glas überhaupt zu Gesicht bekommen. Hierfür überschreiben wir einfach die OnPaintBackground Methode der Form:
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.FillRectangle(
new System.Drawing.SolidBrush(System.Drawing.Color.Black),
this.ClientRectangle);
}
Das war es auch schon, gar nicht mal so komplex finde ich. Wenn ihr das Rechte Bild aus Bild 1 erzeugen wollt geht ihr genauso vor nur benutzt ihr eine andere Funktion im die Fläche für das Glas anzugeben:
[System.Runtime.InteropServices.DllImport("gdi32")]
private static extern System.IntPtr CreateEllipticRgn(
int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
Mit dieser Funktion lassen sich Elliptische Figuren malen, um diese zu verwenden einfach die Zuweisungs Zeile ersetzen:
hr = CreateEllipticRgn(30, 30, 170, 170);
Natürlich kann man auch den Border deaktivieren, um einfach nur die reine Clientoberfläche des Formulars zu bekommen:
Bild 2: Sieht nicht allzu spannend aus
Wie man sieht haben wir nun das was wir wollten, nur hebt das Fenster sich kaum vom Hintergrund ab, obwohl es aktiv ist. Leider können wir dem Formular keinen so schönen Schatten geben wie man es von Windows Vista/7 gewohnt ist, da es keinen Rahmen hat. Wenn man das Formular dennoch etwas hervorheben will, kann man das machen in dem man eine Grafik mit einem Transparentem Verlauf auf der Form platziert und somit den Schatten emuliert, oder man benutzt einen kleinen Trick und weist dem Formular den Schatten der Windows Menüs zu:
private const int CS_DROPSHADOW = 0x00020000;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ClassStyle |= CS_DROPSHADOW;
return p;
}
}
Hier überschreiben wir die CreateParams und sagen, dass das Formular einen Schatten werfen soll. Das ganze sieht dann so aus:
Bild 3: Der Schatten hebt das Fenster etwas vom Hintergrund ab
Diesen Schatten sollte man aber mit Vorsicht genießen, denn das ist der Schatten der Windows Menüs. (Der kommt wenn ihr zB. in irgendeinem Programm auf "Datei" klickt, das erscheinende Menü wird diesen Schatten haben.) Diesen kann man in den Einstellungen deaktivieren, also bitte achten dass der folgende Haken gesetzt ist:
Bild 4: Aktivieren der Menüschatten
Soviel dazu, ich hoffe dieser Artikel hat euch gefallen oder konnte euch weiter helfen, wie oben erwähnt werde ich in den nächsten Tagen einen 5en Teil schreiben, in dem es darum geht wie man GDI+ Steuerelemente möglichst gut auf Glas darstellen kann.
Wie gewohnt gibt es hier das Demoprojekt:

Quellen:
Blog von Kenny Kerr
The Code Project
Microsoft SQL Server Compact Edition – Eine Einfuehrung
Oft müssen Clientprogramme große Datenmangen speichern, und in den meisten Fällen wird dazu eine Datenbank eingesetzt. Wenn man eine Internetverbindung voraussetzt kann man einen mächtigen Datenbank Server anbinden und die Daten in der Cloud sichern. Doch oft ist eine Offline Lösung viel einfacher und effektiver.
Inzwischen gibt es auch viele verschiedene lokale Datenbanksysteme wie zB. SQLite, DB4O oder auch MS SQL Server Compact Edition. Alle diese Serversysteme haben auch den Vorteil, dass diese auf den mobilen Systemen problemlos eingesetzt werden können, haben aber auch den Nachteil, dass sie meist nicht den Vollen Feature Umfang eines echten SQL Servers bieten können.
Vorbereitung
In diesem Artikel soll es um den SQL Server Compact Edition von Microsoft gehen, diesen wollte ich schon immer mal testen und nun hatte ich mal die Zeit dazu.
Sofern ihr es noch nicht habt könnt ihr den SQL CE Server kostenlos bei Microsoft herunterladen. Nach der Installation arbeiten wir wie gewohnt im Visual Studio.
Implementierung
Die ganze Implementierung der Datenbank, Erzeugung der Tabellen sowie die Anbindung erfolgt aus Visual Studio heraus. Die Funktionen die man dort hat sind aber auch stark von dem Provider abhängig, so bieten verschiedene Provider mehr oder weniger Funktionalität.
Um eine Datenbank zu einem Projekt hinzuzufügen öffnet einfach den Projektmappen-Explorer (Ansicht->Projektmappen-Explorer) und Klickt mit der rechten Maustaste auf euer Projekt und dann auf "Hinzufügen"->"Komponente..." und wählt eine Lokale Datenbank aus. In dem Datenbank Assistent Fenster klickt nun 2 Mal auf "Zurück" um auf die erste Seite des Assistenten zu gelangen.
Klickt nun auf "Neue Verbindung". In dem folgendem Fenster sollte man darauf achten, dass als Datenquelle "Microsoft SQL Server Compact 3.5" ausgewählt ist.
Bild 1: Datenbank Konfiguration
Klickt nun auf "Erstellen..." nun solltet ihr das Fenster aus Bild 1 sehen. Wählt oben den Speicherort eurer Datenbank und legt wenn nötig ein Passwort fest um die Datenbank zu verschlüsseln. Bestätigt das Ganze noch zwei Mal mit OK. Visual Studio sollte euch dann darauf hinweisen, dass ihr vorhabt eine Lokale Datenbank zu verwenden und schlägt euch vor diese in euer Projekt zu importieren - das nehmen wir gerne an.
Visual Studio fügt die neu erzeugte Datenbank Datei dem Projekt hinzu und setzt auch die nötigen Verweise auf System.Data und auf System.Data.SqlServerCe.
Anlegen der Tabellen
Beim Anlegen der Tabellen in unserer Datenbank ist uns Visual Studio natürlich ebenfalls sehr behilflich. Öffnet einfach den Server-Explorer (Ansicht->Server-Explorer) und erweitert eure frisch angelegte Datenbank. (Eventuell müsst ihr auch ein Passwort angeben wenn ihr die Datenbank verschlüsselt habt)
Klickt nun mit der rechten Maustaste auf Tabellen und wählt "Tabelle erstellen" um den Tabellen Assistenten zu starten.
Hier könnt ihr eure Tabelle anlegen und konfigurieren. Wenn ihr die nun fertige Tabelle im Server-Explorer rechts anklickt und dann "Tabelleneigenschaften" wählt bekommt ihr noch mehr Konfigurationsmöglichkeiten wie etwa Beziehungen.
Bereits beim Erstellen der Tabellen ist es euch vielleicht schon aufgefallen, dass es nicht so viele Datentypen zur Auswahl gibt wie in einem richtigen SQL Server aber das sind nicht die einzigen Einschränkungen...
Arbeiten mit der Datenbank
Das Arbeiten mit der Datenbank gestaltet sich nicht anders als mit anderen Datenbanken. Die Objekte die man dafür benötigt heißen sogar fast gleich. Alle Klasse die man nutzt haben noch den Zusatz "Ce", also statt "SqlCommand" heißt es nun "SqlCeCommand".
Und auch die Nutzung unterscheidet sich nur geringfügig:
Datenbank Verbindung herstellen:
private SqlCeConnection con;
private string dbLocation = Environment.CurrentDirectory + "\\clipboard.sdf";
private string conString;
private void connectDB(){
conString = "Data Source=\"" + dbLocation + "\"; Password=\"bigbasti.com\"; Encrypt=True";
con = new SqlCeConnection(conString);
con.Open();
}
Eine Select Abfrage durchführen:
///
/// Führt einen Select Befehl auf der Datenbank aus und gibt das ergebnis in einer DataTable zurück
///
///Der auszuführende Befehl
/// Return: Ein DataTable mit den ausgelesenen Daten
public DataTable executeSelect(string query) {
connectDB();
DataTable dt = new DataTable();
SqlCeCommand com = new SqlCeCommand(query, con);
SqlCeDataAdapter sad = new SqlCeDataAdapter(com);
sad.Fill(dt);
con.Close();
return dt;
}
public int checkDouble(string link, string date) {
int retVal;
using (SqlCeConnection con = new SqlCeConnection(conString)) {
con.Open();
//Doppelte Einträge verhindern
DataTable dt = executeSelect("SELECT * FROM cp_link WHERE link LIKE '" + link + "'");
if (dt.Rows.Count > 0) {
executeNonQuery("DELETE FROM cp_link WHERE link LIKE '" + link + "'");
}
}
return retVal;
}
UPDATE, DELETE und INSERT:
///
/// Executes an SQL statement against the SqlCeConnection and returns
/// the number of rows affected.You also can use ExecuteNonQuery to
/// change the data in a database without using a DataSet by executing
/// UPDATE, INSERT, or DELETE statements.
///
///Der Befehl der ausgeführt werden soll
/// The number of rows affected.
public int executeNonQuery(string q) {
int retVal;
using (SqlCeConnection con = new SqlCeConnection(conString)) {
con.Open();
using (SqlCeCommand com = new SqlCeCommand(q, con)) {
retVal = com.ExecuteNonQuery();
}
}
return retVal;
}
public int doSomeInsert(string link, string date) {
int retVal;
using (SqlCeConnection con = new SqlCeConnection(conString)) {
con.Open();
using (SqlCeCommand com = new SqlCeCommand("INSERT INTO cp_link (link, date) VALUES (@link, @date )", con)) {
com.Parameters.AddWithValue("@link", link);
com.Parameters.AddWithValue("@date", date);
retVal = com.ExecuteNonQuery();
}
}
return retVal;
}
Hier gibts eigentlich nichts besonderes. Leider hat man hier, da es eine Compact Edition ist nicht so viele Befehle zur Verfügung wie im Original. So fehlt zB. auch der TRUNCAT Befehl wie ich erstaunt feststellen musste. Ich habe da nicht noch weiter recherchiert was noch alles fehlt, da ich für das was ich gemacht habe keine besonderen Aktionen benötigte.
Deployment
Wenn ihr das Projekt über ein Setup-Projekt deployen wollt dann sollte da eigentlich nichts schief gehen. Ihr könnt aber natürlich auch ohne ein extra Setup-Projekt deployen, dazu solltet ihr folgende Schritte machen:
- Öffnet die Eigenschaften eures Projekts
- Öffnet dort den "Veröffentlichen" (Publish) Tab
- Klickt auf den Knopf "Erforderliche Komponenten"
- Sucht den Eintrag "SQL Server Compact Edition 3.5" und entfernt das Häkchen und schließt die Eigenschaften
- Nun benötigen wir die erforderlichen DLL Dateien, diese liegen in "C:\Programe\Microsoft SQL Server Compact Edition\"
- Es sind 7 oder auch mehr Dateien, abhängig davon wie viele Sprachen installiert sind (siehe Bild links)
- Markiert diese DLLs und zieht diese direkt in den Projektmappen-Explorer, da mit diese DLLs dem Projekt hinzugefügt werden
- Markiert die neuen DLLs in dem Projektmappen-Explorer -> klickt mit der rechten Maustaste darauf und wählt "Eigenschaften"
- Setzt die Eigenschaft "In Ausgabeverzeichnis kopieren" auf "Kopieren, wenn neuer" damit ihr immer die Aktuelle Version im Build Verzeichnis habt
- Klickt im Projektmappen-Explorer mit der rechten Maustaste auf System.Data.SqlServerCe und wählt Eigenschaften
- Setzt hier die Eigenschaft "Lokale Kopie" auf True. Dieser Schritt ist nötig damit das Programm lokal nach der DLL sucht und nicht versucht sich diese aus dem GAC (Global Assembly Cache) zu holen.
Und das war es eigentlich schon. Nun könnt ihr das Projekt problemlos deployen.
.NET: Charts und Diagramme mit MSChart erstellen
Vor Kurzem musste ich für einen Kunden eine Datenauswertung in einer .NET Applikation realisieren und wollte dazu ein schönes Diagramm erstellen. Leider musste ich (zu meinem Erstaunen) feststellen, dass das .NET Framework über kein eigenes Control für solche Zwecke verfügt, zumindest nicht im Framework 3.5!
Nach ein wenig googlen bin ich dann auf die Charting Controls von Microsoft gestoßen, diese können hier heruntergeladen werden und für das Framework 3.5 nachinstalliert werden. Das Framework 4.0 bringt es gleich mit.
Nach der Installation steht euch der Namespace "System.Windows.Forms.DataVisualization.Charting" zur Verfügung, der wirklich alles woran man so denken kann mitbringt, und das völlig kostenlos! Dieses Control kann man auch in ASP.NET Projekten benutzen.
Die eigentlich Benutzung ist auch sehr einfach gehalten, so können wir mit diesem Code einen einfachen Graphen erstellen und anzeigen:
using System.Windows.Forms.DataVisualization.Charting;
private void Form1_Load(object sender, EventArgs e) {
chart1.Series.Add("Balcken");
chart1.Series.Add("Area");
//Ausssehen festlegen
chart1.Series["Balcken"].ChartType = SeriesChartType.Column;
chart1.Series["Linien"].ChartType = SeriesChartType.Line;
chart1.Series["Area"].ChartType = SeriesChartType.Area;
//Zufällige Werte generieren
Random random = new Random();
for (int pointIndex = 0; pointIndex < 10; pointIndex++) {
chart1.Series["Area"].Points.AddY(random.Next(20, 50));
}
for (int pointIndex = 0; pointIndex < 10; pointIndex++) {
chart1.Series["Balcken"].Points.AddY(random.Next(50, 100));
}
for (int pointIndex = 0; pointIndex < 10; pointIndex++) {
chart1.Series["Linien"].Points.AddY(random.Next(70, 100));
}
}
Wie ihr sehen könnt ist es alles sehr sehr simpel aufgebaut und man hat mit nur wenigen Zeilen Code ein schönes Diagramm gemacht:
Bild 1: Schöne Diagramme schnell gemacht
Wie oben bereits erwähnt seid ihr nicht auf diese Diagrammtypen beschränkt, sondern habt eine ziemlich große Auswahl an verschiedenen Diagrammtypen, darunter auch welche mit Farbverläufen und auch in 3D!
Bild 2: Eine breite Auswahl an verschiedenen Visualisierungen
Bei der Auslieferung eurer App solltet ihr darauf achten, dass ihr die System.Windows.Forms.DataVisualization.dll mit ausliefert, da der Benutzer sonst die Anwendung nicht starten kann, da diese DLL nicht Bestandteil von Framework 3.5 ist. Wenn ihr aber mit Sicherheit wisst, dass der Benutzer das Framework in der Version 4.0 installiert hat könnt ihr euch diesen Schritt sparen!
Weitere Informationen findet ihr im MSDN. Ein Demoprojekt gibts hier:



