Command Routing in Windows Forms .NET

When developing a real-world application in Windows Forms .NET, you usually have multiple menus, tool bars, ribbons, context menus, etc.

The challenge here is to provide central handlers to perform actions (e.g. an action to open a file) that came from various sources (e.g. the user clicks a menu item, or a tool bar item).


(Screenshot from my article on CodeProject about the same topic)

MFC

In the pre-.NET days there were the Microsoft Foundation Classes (MFC) that came with what they call „Command Routing„.

In MFC’s command routing the framework was responsible of correctly dispatching to the correct handler. In the handler you could specify (among other things):

  • Whether the command’s visual representation (a button, a menu item, …) is displayed in enabled or disabled state.
  • The action to perform when the command is being executed.

The dispatching took place automatically, depending on the current focused window.

.NET Windows Forms

Now when Windows Forms appeared, they completely lacked this command routing framework.

Therefore I developed my own minimal version that is nearly-bug-free for some projects. You can download an example project here.

Since there are more approaches by other developers to solve the lack of command routing in Windows Forms, I want to use this article to list the good ones I found:

I’m looking forward to read your resources, feel free to post them here in the comments section below.

Windows Presentation Foundation (WPF)

To be honest, I do have no practical experiences in WPF. Luckily my co-worker Dennis has a Microsoft certification on WPF.

From what I do understand, WPF does have very decent command routing. Maybe some reader can comment on whether it would be possible to extract parts of the WPF command routing and use it in Windows Forms? (Here is a German discussion about that topic)

I hope that I will soon be able to try WPF in my own projects. 🙂

(Keyword: event routing event bubbling bubble event tree hierarchy)

„RIGHT“-Funktion in Oracle

Um die letzten n Zeichen einer Zeichenfolge zu ermitteln gibt es in Basic z.B. die Right-Funktion.

Große Dateien sendenHomepage erstellenSharepoint StuttgartTest Management Software

In Oracle gibt es dazu SUBSTR-Funktion. Wird diese mit einer negativen Zahl aufgerufen, so wird vom Ende der Zeichenfolge gezählt, was einer Right-Funktion entspricht.

Beispiele:

substr(‚This is a test‘, 6, 2)Liefert ‚is‘ als Ergebnis
substr(‚This is a test‘, 6)Liefert ‚is a test‘ als Ergebnis
substr(‚TechOnTheNet‘, 1, 4)Liefert ‚Tech‘ als Ergebnis
substr(‚TechOnTheNet‘, -3, 3)Liefert ‚Net‘ als Ergebnis
substr(‚TechOnTheNet‘, -3)Liefert ebenfalls ‚Net‘ als Ergebnis
substr(‚TechOnTheNet‘, -6, 3)Liefert ‚The‘ als Ergebnis
substr(‚TechOnTheNet‘, -8, 2)Liefert ‚On‘ als Ergebnis

(Via Tech on the Net)

Result codes for GetLastError()

Just for reference purposes, all System Error Codes are listed on MSDN:

To actually translate numeric error codes to user-readable error codes, use the FormatMessage function.

In .NET, use the following code snippet as a replacement for the FormatMessage function:

var errorMessage = 
    new Win32Exception(Marshal.GetLastWin32Error()).Message;

(Tip via pinvoke.net)

Serialize a DataTable to a string

Just a short code snippet that helped me recently:

public static string SerializeTableToString(
    DataTable table )
{
    if (table == null)
    {
        return null;
    }
    else
    {
        using (var sw = new StringWriter())
        using (var tw = new XmlTextWriter(sw))
        {
            // Must set name for serialization to succeed.
            table.TableName = @"MyTable";

            // --
            // http://bit.ly/a75DK7

            tw.Formatting = Formatting.Indented;

            tw.WriteStartDocument();
            tw.WriteStartElement(@"data");

            ((IXmlSerializable)table).WriteXml(tw);

            tw.WriteEndElement();
            tw.WriteEndDocument();

            // --

            tw.Flush();
            tw.Close();
            sw.Flush();

            return sw.ToString();
        }
    }
}

Hopefully it is useful for someone out there, too.

SVN Ignore Pattern

Just for reference purposes, this is my current SVN ignore pattern:

*.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo *.rej *~ #*# .#* .*.swp .DS_Store _ReSharper.* */obj *.user *.suo *.pch *.ilk *.obj *.idb *.ncb *.log* thumbs.db */int *.bsc *.resharper

To set this pattern, right-click on any SVN-controlled folder in Windows File Explorer and select „Settings“:

tortoise-pattern-01

Then choose „General“ and fill the „Global ignore pattern“ field.

tortoise-pattern-02

Marshalling .NET Exceptions between Threads

Introduction

Today I faced a strange issue; I was catching an Exception in a BackgroundWorker’s thread, threw a new exception and handled it in the main GUI thread by showing the exception’s Message in a message box.

The strange thing was, that no matter what I did, the main GUI thread always displayed the message of the first generated exception in the background thread.

Cause

As described by user „nobugz“ in this discussion, the cause is:

This is an architectural limitation to the way exceptions are marshalled from one thread (the BGW’s) to another (the UI thread).  Only a single exception object can get copied, the designers chose the first one thrown as the most likely to be relevant.  You’ll need to work around this restriction.

Interestingly enough this behavior seems to occur only in an Application.ThreadException handler which seems to pick up the exception from somewhere (maybe TLS?)

Resolution

The resolution is to handle the exception directly in the RunWorkerCompleted handler where it gets forwarded to and not to throw the exception any further, until the Application.ThreadException handler of the GUI thread handles it.

Example application

To show the error in a small example project I created a tiny Windows Forms application which you may download from here.

Main Window with erroneous Behavior

When the check box is activated, the erroneous behavior is being performed when clicking the button:

The first generated exception is being displayed to the end user instead the actual one that is being thrown later in the background thread.

Main Window with expected Behavior

When the check box is not activated when clicking the button, the expected result is being displayed; the latest thrown exception:

Summary

I’ve just shown you a case where (in my eyes) the .NET Framework behaves wrong.

Maybe you find this article useful or even prove that I am wrong or how to handle in a more elegant way.

Looking forward for your feedback!

Fehlermeldung „Invalid URI: The hostname could not be parsed.“

Fehlermeldung

Beim Laden aus einer XML-Datei erhielt ein Kunde unseres CMS Zeta Producer folgende Fehlermeldung:

System.UriFormatException

—————–

Invalid URI: The hostname could not be parsed.

—————–

at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
at System.Uri..ctor(String uriString, UriKind uriKind)
at System.Xml.XmlResolver.ResolveUri(Uri baseUri, String relativeUri)
at System.Xml.XmlUrlResolver.ResolveUri(Uri baseUri, String relativeUri)
at System.Xml.XmlTextReaderImpl..ctor(String url, XmlNameTable nt)
at System.Xml.XmlDocument.Load(String filename)
at Zeta.EnterpriseLibrary.Tools.Storage.PersistentXmlFilePairStorage.load()
at Zeta.EnterpriseLibrary.Tools.Storage.PersistentXmlFilePairStorage.set_FilePath(FileInfo value)
at ZetaProducer.RuntimeBusinessLogic.Code.AppHost.Host.InitializeInstance()
at ZetaProducer.RuntimeMain.Code.AppHost.Host.InitializeInstance()
at ZetaProducer.Main.Code.AppHost.Host.InitializeInstance()
at ZetaProducer.Main.Code.AppHost.Host.process()

Ursache

Ursache war, dass der Kunde Parallels unter Mac OS X verwendet hat um Zeta Producer in einer virtuellen Maschine laufen zu lassen.

Gleichzeitig lagen die „Eigenen Dateien“ auf Mac OS X und wurden via Parallels in die virtuelle Maschine eingebunden.

Der Pfad war sichtbar als:

\\.psf\Home\Documents\Zeta Producer 9

Damit scheint die Uri-Klasse von .NET nicht zurecht zu kommen.

Lösung

Eine schnelle Lösung aus Anwendersicht ist, den UNC-Pfad „\\.psf\Home\Documents\Zeta Producer 9“ als Laufwerksbuchstaben unter Windows zu verbinden und dieses Laufwerk dann als Home-Ordner zu konfigurieren.

Eine programmierte Lösung wird so aussehen, dass ich das Laden eines XmlDocument selbst durchführe (via Stream) und dann den geladenen Stream (bzw. die Zeichenfolgen als string) an das XmlDocument übergebe. So umgehe ich die Uri-Klasse, die wohl mit UNC-Pfaden die mit „.“ beginnen Schwierigkeiten hat.

.NET-Property nicht im PropertyGrid anzeigen

Um eine Eigenschaft („Property“) in .NET nicht im Eigenschaftsraster („PropertyGrid“) anzuzeigen, verwende ich folgende 3 Attribute:

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string MyAttribute
{
    get
    {
        // ...
    }
}

Damit ist es dann sowohl im Windows-Forms-Designer unsichtbar (sofern die Eigenschaft in einem Benutzer-Steuerelement [„UserControl“] enthalten ist, als auch wenn ich es zur Laufzeit an eine Instanz der PropertyGrid-Klasse binde.)

Text mit C# aus einer Datei lesen

Dieser Artikel ist für mich als Referenz, weil ich es immer wieder mal recherchiere.

HOWTO: Text mit .NET/C# aus einer Textdatei lesen

1. Möglichkeit: Microsoft-Knowledge-Base-Artikel How to read a text file by using System.IO and Visual C#.

2. Möglichkeit: So wie in der MSDN-Dokumentation beschrieben: How to: Read Text from a File.

In aller Kürze:

string line;
using (var sr = new StreamReader("TestFile.txt"))
{
    line = sr.ReadToEnd();
}

// ...do something with line...

Ich hoffe es hilft mir und Euch da draußen mal.