Artikel in der Kategorie 'C#'

C# – Umbiegen von DTDs

Beim Arbeiten an einem C#-Projekt und XML-Dateien stand ich mal wieder vor einem Problem, bei dem ich von einer einfachen Lösungen ausgegangen war… was natürlich nicht der Fall ist.

Folgende Situation:

Das Programm erlaubt es dem Nutzer aktuelle Daten in XML-Dateien zu speichern und natürlich auch wieder zu laden. Um die Kontrollarbeit beim Laden zu erleichtern, gibt es immer eine aktuelle Dokumentypdefinition (DTD), die der XML-Datei zugewiesen wird. Die DTD mittels

DTD-Definition in XML
  1. <!DOCTYPE Wurzelelement SYSTEM "datei.dtd">

lokal verlinkt. Dies führt aber zu dem Problem, dass das Programm beim Öffnen der XML-Datei die DTD auch immer am gleichen Ort erwartet. Dies ist natürlich problematisch sobald der Nutzer seine Datei irgendwo nach Belieben speichern möchte. Den absoluten Ort der DTD anzugeben, ist aber auch keine Lösung, da es dann natürlich beim Dateiaustausch oder Ähnlichem zu Problemen kommt.

Die Lösung liegt also darin, den Zugriff auf die DTD auf einen zentralen Ort umzubiegen. Nach reichlich Recherchearbeit und Experimentieren habe ich zwei mögliche Lösungen erarbeitet, die an der gleichen Stelle ansetzen. Das Suchen und Lesen der DTD übernimmt bei C#, wenn man wie ich mit XmlReader arbeitet, ein (abstrakter) XmlResolver. Also habe ich mir einen eigenen XmlResolver von der konkreten Klasse XmlUrlResolver abgeleitet und dort die zuständige Methode GetEntity() überschrieben. Es wird nun jeweils via Regex überprüft, ob eine DTD-Datei gesucht wird (Der Name wird in der Variablen file gespeichert…). Wenn dies der Fall ist, wird der Zugriff geändert:

Variante 1 – DTD liegt in einem Unterverzeichnis des Programmverzeichnis:
Hierbei wird der lokale Suchort auf den zentralen Ort umgebogen und der Get-Aufruf dann einfach an die Basisklasse weitergereicht:

Umbiegen des DTD-Verzeichnisses
  1. string pathToExe = Assembly.GetExecutingAssembly().Location;
  2. pathToExe = pathToexe.Substring(0,pathToExe.IndexOf("name.exe"));
  3. Uri newUri = new Uri(pathToExe + "Unterverzeichnis\\" + file);
  4. return base.GetEntity(newUri, role, ofObjectToReturn);

Die Parameter role und ofObjectToReturn werden einfach so übernommen, da sich da ja nichts ändert.

Variante 2 – DTD liegt als “Embedded Resource” vor:
Hierbei können wir uns das “reale” Speichern der DTD sparen und verminden so Fehlerquellen, wie Nutzer die DTDs löschen oder Ähnliches. Dazu wird nicht der Dateiort auf der Festplatte umgebogen, sondern wir lesen die DTD aus dem Speicher:

Zugriff auf eingebettete DTD
  1. StreamReader r = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("namespace.Resources." + file));
  2. return r.BaseStream;

Wichtig dabei ist, dass man auf die korrekte Schreibweise der Resourcen achtet. Sonst findet er da nichts und man wundert sich, warum es nicht geht ( ;) ).Dies und auch ob die Datei richtig eingebettet wurde kann man einfach über die Methode GetManifestResourceNames() überprüfen.

Wie bereits geschrieben, habe ich mir das eindeutig einfacher vorgestellt, aber nun ist’s ja doch endlich geschafft. Vielleicht kann das jemandem mit einem ähnlichen Problem ja helfen oder vielleicht hat jemand ja noch eine bessere Lösung. :)

Kontextmenü in einem DataGridView

Eigentlich eine ganz einfache, normale Sache:

Man möchte via einem Rechtsklick ein Kontextmenü auf einer Zelle öffnen, die sich in einem DataGridView befindet. Dies sollte möglichst mit einem einzigen ContextMenuStrip geschehen, was am DataGridView hängt. Klingt immer noch ganz einfach. Nun soll sich das Kontextmenü zwar überall öffnen, aber nur auf einer Datenzelle bestimmte Optionen zulassen (z.B. Löschen), also nicht im Kopf und erst recht nicht im leeren Bereich. Klingt immer noch nach einem ganz normalen Vorgehen, was man jeden Tag brauchen könnte. Nun könnte man meinen, dass dies von den Herrschaften bei Microsoft auch so gesehen wird und .NET solch eine Funktion unterstützt… nunja… soweit die Theorie…

Praxis:

  1. dataGridView.CellContextMenuStripNeeded += new DataGridViewCellContextMenuStripNeededEventHandler(dataGridView_CellContextMenuStripNeeded);
  2. private void dataGridView_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
  3. {
  4.   if (e.ColumnIndex > -1 && e.RowIndex < -1)
  5.   {
  6.     //Menüitem aktivieren
  7.   }
  8.   else {
  9.     //Menuitem deaktivieren
  10.   }
  11. }

Ich hatte da eine etwas einfachere Lösung gehofft. Nun möchte ich sogar noch Daten sortieren, wenn man eine Kopfzelle anklickt. Klingt einfach? Lösung folgt die Tage…

ClickOnce updated nicht

Nachdem ich nun eine Weile mit der Mircosoft Visual C# 2008 Express Edition und der API-Doku im MSDN .NET Framework Developer Center gearbeitet, hatte ich nun ein – im Nachhinein recht witziges – Problem. Im Zuge einer ClickOnce-Anwendung habe ich die recht spartanisch gestaltete automatische Updatefunktion mit einer programminternen Variante ersetzt. Nach einer Reihe von Dummydeployments – man kann sonst ja nicht die Funktion testen – war ich schon nah am Rande der Verzweiflung, warum der asynchrone Updater einfach nicht updaten wollte. Alle relevanten Informationen wurden gefunden, aber er tat einfach nichts.

Read more »

Visual C# vs PHP

Ich weiß… der Vergleich hinkt etwas, da Visual C# und PHP nicht gerade im gleichen Bereich alternativ eingesetzt werden, aber das sind die beiden Programmiersprachen, mit denen ich z.Z. arbeite. Im direkten Vergleich merkt man aber mal die extremen Unterschiede in der Programmierung von (Standalone)Anwendungen und Webanwendungen… und was ein gutes Tool alles ausmachen kann.

Für C# nutze ich zZ die kostenfreie Visual Studio C# Express Edition 2008 von Microsoft. Interface zusammenklicken und generierten Code auffüllen. Das macht echt Spaß. Allein das Anlegen eines Eingabeformulars bzw. die Eingabekontrolle geht so locker von der Hand. Einfach einen ErrorProvider und ein paar Funktionen zur Kontrolle hinzufügen und der Rest geht von ganz allein. Wenn man dann an das HTML-Formularhandling denkt… *graus* … vor allem, da ich das Meiste noch wirklich per Hand im guten alten Phase5 mache. Ich habe mir zwar schon eine kleine Klasse geschrieben, die etwas beim Verwalten von Formularen hilft, aber Eingabekontrolle beherrscht sie noch nicht.

Kennt zufällig wer ein gutes Framework für PHP, was da Abhilfe schafft? Ich habe mir zwar schon 1-2 angeschaut, aber bis jetzt hatte ich noch nie die Motivation mich in eins einzuarbeiten.