Eine .NET WinForms Anwendung nur einmal starten / arbeiten mit einem mutex

Wenn man eine Anwendung nur einmal durch den Anwender starten lassen möchte, dann ist das mit der Hilfe von einem erzeugten Mutex sehr einfach zu realisieren. Nur ein paar Zeilen Quellcode sind dafür in der Main() Methode notwendig. Wie immer ist der Beispiel-Code in .NET C#. bool createdNew;///Einen neuen Mutex erzeugen, damit die Anwendung nur einmal gestartet werden kann.System.Threading.Mutex appMutex = new System.Threading.Mutex(true, Application.ProductName, out createdNew);///Wenn die Erzeugung erfolgreich warif (createdNew){///... dann kann die Anwendung ausgeführt werdenLogIn frmLogIn = new LogIn();Application.Run(frmLogIn);if (frmLogIn.DialogResult == DialogResult.OK)Application.Run(new MainForm());// den Mutex wieder frei gebenappMutex.ReleaseMutex();}else{///Wenn die Anwendung schon ausgeführt wird -> Hinweis-Dialogstring msg = String.Format("Das Programm \"{0}\" wurde bereits gestartet!", Application.ProductName);MessageBox.Show(msg, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);}

Cool (und kostenlos) Threading .NET 2.0 E-Book

Ich habe gerade eine coole Seite über Threading in .NET 2.0 gefunden, die auch viele Beispiele in C# bietet. http://www.albahari.com/threading/ Zudem gibt es (derzeit links unten) einen PDF, das nun auf meinem "muss ich unbedingt mal komplett Lesen" Stappel liegt ;-)

Mal wieder eine coole Komponente: Outlook 2003 Style Sidebar

Gerade habe ich auf GotDotNet mal wieder eine wirklich coole Komponente gefunden, mit der man die Sidebar von Outlook 2003 abbilden kann. Zu finden gibt es dieses .NET Komponenten - die übrigens in C# implementiert wurde - inkl. dem kompletten Sourcecode und einer Beispielanwendung, wie man die Komponente einsetzt, genau hier (klickst Du).

Windows Form Textbox und Strg-A bzw. Ctrl-A

Eigentlich bin ich von den Controls des .NET Frameworks 2.0 sehr zufrieden und der Umfang der enthaltenen Controls ist durchaus brauchbar. Aber gerade bin ich doch ein wenig entäuscht, da es die Jungs wohl nicht geschafft, übersehen bzw. nicht an eine sehr simple doch oft gebraucht Funktionalität zu denken. Ich habe eine Windowsforms-Anwendung auf der sich ein Multiline-Textfeld befindet. Jeder (oder sagen wir lieber viele) Benutzer sind es gewohnt den gesamte Inhalt eines solchen Textfeldes mit Strg-A zu markieren... doch was ist das? Nichts rührt sich, nichts wird markiert :( Nach einer recherche im Web musste ich wohl feststellen, dass ich nicht zu blöd bin, sondern dieses Problem wohl auch bei anderen existiert. Also beleibt einem nichts übrig als diese Enterprise-Funktionalität selber zu implementieren... das würde dann so aussehen: if ( (e.Control) && (e.KeyCode==Keys.A)){this.myTextbox.SelectAll();} Mahlzeit!

In einem BackgroundWorker einen Treeview aufbauen / populate

In .NET 2.0 ist das Arbeiten mit Threads dank des Backgroundworkers deutlich einfacher geworden. Heute Abend habe ich mich mal damit beschäftigt in einem Backgroundworker einen Treeview zu füllen. Der Zugriff auf ein Control in der Methode DoWork() ist nämlich nicht möglich, aber es gibt einen Trick, dieses zu lösen. Zunächst einmal die Basics um einen Backgroundworker bereitzustellen: this.backgroundworker = new BackgroundWorker();this.backgroundworker.WorkerReportsProgress = true;this.backgroundworker.DoWork += new DoWorkEventHandler(backgroundworker_DoWork);this.backgroundworker.ProgressChanged += new ProgressChangedEventHandler(backgroundworker_ProgressChanged);this.backgroundworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundworker_RunWorkerCompleted); Wichtig dabei ist lediglich das Event ProgressChanged zu implementieren und die Property WorkerReportsProgress auf "true" zu setzen. Bis hier noch nicht wirklich aufregend....auch der Auruf mit this.backgroundworker.RunWorkerAsync(); ist wie gewohnt. In der Methode DoWork() kommt dann aber der Trick... void backgroundworker_DoWork(object sender, DoWorkEventArgs e){   TreeNode treenode= new TreeNode();    treenode.Text = 'name';   this.backgroundworker.ReportProgress(0, treenode);} Hier jetzt einfach mal so viel Vorstellungskraft aufbringen als würde DoWork() eine Schleife, Datenbankabfrage oder ähnliches durchführen, die viel Zeit in Anspruch nimmt. Die Möglichkeit eine Fortschritt zu Reporten kann man nun nutzen um ein Object an eine Form weiterzugeben. Die Implementierung der Methode/Event ProgessChanged könnte dann so aussehen: void backgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e){this.tvNavigator.BeginUpdate();this.tvNavigator.Nodes.Clear();this.tvNavigator.Nodes.Add((TreeNode)e.UserState);this.tvNavigator.EndUpdate();} Mit diesem Trick ist es nun möglich einen TreeView asyncron zu füllen....

Dockpanel fuer WinForms

Gerade habe ich eine doch sehr cooles .NET Opensource Projekt auf sourcefroge.net gefunden. Dabei handelt es sich um eine in C# geschriebene Komponenten die eine Dockingfunktionalität bietet. Vorlage war dabei wohl das Visual Studio.net 2005. Für so manche .NET Windows Anwendung dürfte das eine wirkliche Bereichung sein. Hier geht es zu der Komponente: Link

Einstellungen einer Windows Anwendung speichern unter .NET 2.0

Die ersten Erfahrungen mit dem .NET Framework 2.0 waren sehr erfreulich. Als eines der ersten Features habe ich mir die "Anwendungseinstellungen" vorgenommen und war total begeistert. Endlich wird einem diese lästige Aufgabe komplett abgenommen auch das Binden von Einstellunge auf Eigenschaften eines Controls finde ich super! Nur noch ein Load() und Save() in den Code einbauen und fertig. So dachte ich zumindest aber der Frust kam relativ schnell: Das Speichern von benutzerabhängigen Einstellungen wird unter c:\Dokumente und Einstellungen\benutzername\.... vorgenommen. So weit kein Problem nur das die Versionsnummer mit im Pfad stehen muss finde ich richtig übel. So bald sich die Versionnummer ändert ist es mit den alten Einstellungen essig ... es gibt zwar eine Upgrade() Methode aber bei der Verwendung von SettingKey funktioniert das nicht mehr :( Also habe ich versucht den Pfad anzupassen und habe gegoogelt .. siehe da: Es geht nicht .... super gemacht, toll durchdacht aber dann trotzdem leider auf den letzten Metern versaut. Immerhin hat man die Möglichkeit einen eigenenen Provider zu schreiben, um die Einstellungen abzuspeichern. Das wiederum ist klasse gelöst und mehr als sinnvoll! Nun hier ist eine Provider, der die Einstellungen im XML-Format unter dem Anwendungsverzeichnis speichert: using System;using System.Collections;using System.Collections.Generic;using System.Text;using System.Configuration;using System.Windows.Forms;using System.Xml;using System.Collections.Specialized;using Microsoft.Win32;namespace GaliNeo.Framework.GaliNeoSettings{class GaliNeoSettingsProvider : SettingsProvider{XmlDocument doc = null;XmlNode root = null;XmlNode userSettings = null;XmlNode appSettings = null;public GaliNeoSettingsProvider(){doc = new XmlDocument();LoadSettings();}public override string ApplicationName{get { return Application.ProductName; }set { }}public override void Initialize(string name, NameValueCollection col){base.Initialize(this.ApplicationName, col);}public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals){foreach (SettingsPropertyValue propval in propvals){SettingsProperty prop = propval.Property;setValue(prop, propval, context);}this.WriteSettings();}public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection props){// Create new collection of valuesSettingsPropertyValueCollection values = new SettingsPropertyValueCollection();string sectionName = this.GetSectionName(context);string settingKey = (string)context["SettingsKey"];// Iterate through the settings to be retrievedforeach (SettingsProperty setting in props){SettingsPropertyValue value = new SettingsPropertyValue(setting);value.IsDirty = false;value.SerializedValue = getValue(setting, context );values.Add(value);}return values;}private XmlNode SerializeToXmlElement(SettingsProperty setting, SettingsPropertyValue value){XmlElement element = doc.CreateElement("value");string text1 = value.SerializedValue as string;if ((text1 == null) && (setting.SerializeAs == SettingsSerializeAs.Binary)){byte[] buffer1 = value.SerializedValue as byte[];if (buffer1 != null){text1 = Convert.ToBase64String(buffer1);}}if (text1 == null){text1 = string.Empty;}if (setting.SerializeAs == SettingsSerializeAs.String){//text1 = this.Escaper.Escape(text1);}element.InnerXml = text1;XmlNode node1 = null;foreach (XmlNode node2 in element.ChildNodes){if (node2.NodeType == XmlNodeType.XmlDeclaration){node1 = node2;break;}}if (node1 != null){element.RemoveChild(node1);}return element;}private string GetSectionName(SettingsContext context){string groupName = (string)context["GroupName"];string settingKey = (string)context["SettingsKey"];string section = groupName;if (!string.IsNullOrEmpty(settingKey)){object[] arrName = new object[] { section, settingKey };section = string.Format("{0}.{1}", arrName);}return XmlConvert.EncodeLocalName(section);}#region DANprivate string getXPathQueryProperty(SettingsProperty setting, SettingsContext context){string xPathQuery = getXPathQuerySection(setting, context);if (setting.Name != ""){xPathQuery += "/" + setting.Name;} //if (propName != "")return xPathQuery;}private string getXPathQuerySection(SettingsProperty setting, SettingsContext context){string xPathQueryGroup = doc.DocumentElement.LocalName ;if (this.IsUserScoped(setting)){xPathQueryGroup += "/userSettings";}else{xPathQueryGroup += "/appSettings";}xPathQueryGroup += "/" + GetSectionName(context);return xPathQueryGroup;}private void CreatePropNode(SettingsProperty setting, SettingsPropertyValue value, SettingsContext context){string xPathQuery = getXPathQuerySection(setting, context);XmlNode groupNode = doc.SelectSingleNode(xPathQuery);if (groupNode == null){groupNode = doc.CreateElement(GetSectionName(context));if (this.IsUserScoped(setting)){userSettings.AppendChild(groupNode);}else{appSettings.AppendChild(groupNode);}} //if (node == null)XmlNode nodeProp = doc.CreateElement(setting.Name);nodeProp.AppendChild (this.SerializeToXmlElement(setting,value) );groupNode.AppendChild(nodeProp);}private void setValue(SettingsProperty setting, SettingsPropertyValue value, SettingsContext context) //string propName, string groupName, object value, object defaultvalue){string xPathQuery = getXPathQueryProperty(setting,context);XmlNode node = doc.SelectSingleNode(xPathQuery);if (node != null){XmlNode valueNode = SerializeToXmlElement(setting, value);node.RemoveAll();node.AppendChild(valueNode);}else{CreatePropNode(setting, value, context);} //if (node != null)}private object getValue(SettingsProperty setting, SettingsContext context){string xPathQuery = getXPathQueryProperty(setting, context);XmlNode node = doc.SelectSingleNode(xPathQuery + "/value");if (node != null){return node.InnerXml;}else if (setting.DefaultValue != null){return setting.DefaultValue;}else{return null;}}// Helper method: walks the "attribute bag" for a given property// to determine if it is user-scoped or not.// Note that this provider does not enforce other rules, such as // - unknown attributes// - improper attribute combinations (e.g. both user and app - this implementation// would say true for user-scoped regardless of existence of app-scoped)private bool IsUserScoped(SettingsProperty prop){foreach (DictionaryEntry d in prop.Attributes){Attribute a = (Attribute)d.Value;if (a.GetType() == typeof(UserScopedSettingAttribute))return true;}return false;}#endregionprivate void LoadSettings(){string configFile = getPath() + "user.config";if (System.IO.File.Exists(configFile) ){doc.Load(configFile);root = doc.DocumentElement;userSettings = doc.DocumentElement.SelectSingleNode("./userSettings");appSettings = doc.DocumentElement.SelectSingleNode("./appSettings"); ;}else{try{root = doc.CreateElement("configuration");userSettings = doc.CreateElement("userSettings");root.AppendChild(userSettings);appSettings = doc.CreateElement("appSettings");root.AppendChild(appSettings);doc.AppendChild(root);}catch {}}}private string getPath(){return Application.StartupPath + @"\App_Data\" ;}private void WriteSettings(){if (!( System.IO.Directory.Exists(getPath()))){System.IO.Directory.CreateDirectory(getPath());}string configFile = getPath() + "user.config";doc.Save(configFile);}}} Nun das ist noch nicht 100% best practice Entwicklung (musste schnell gehen) aber es funktioniert ....:)    

Neue Themen .NET 2.0, Winform und Google-API

Ab Heute wird die Thematik dieses Blogs ein wenig erweitert. Nun ist es endlich so weit und di Entwicklung mit .NET 2.0 geht bei mir endlich los. In der nächsten Zeit wird der Schwerpunkt mehr auf .NET Winforms und der Google-API liegen. Aber keine Angst ich werde DotNetNuke nicht wirklich vernachlässigen.