Blog Home  Home Feed your aggregator (RSS 2.0)  
HP's Blog - ProgrammierungTipsUndTricks
Hans-Peter Schelian's Weblog
 
# Wednesday, December 30, 2009

Vor kurzem wurde im Zuge eines Projektes  (Unterstützung eines Entwickler Teams für eine Winform Anwendung im Logistikumfeld) unter anderem die Anforderung an mich heran getragen, dass der Kunde die Ansprechpartner für eine bestimmte Logistische Aktivität aus einer Combobox auswählen möchte.

Hierbei sollte in der Combobox, nicht nur ein Feld aus der Datenbank angezeigt werden, sondern die Anzeige sollte aus insgesamt 2 Feldern zusammengebaut werden.

Das Format sollte so aussehen: Nachname, Vorname

Hier ein Beispiel:

Mustermann, Hans

Dabei sollte die Lösung vollkommen im Client implementiert sein und nicht durch eine geänderte Abfrage der Datenbank realisiert werden.

Nun ist es aber so, dass man dem DisplayMember nur ein Feld der Datengebundenen Tabelle (Objekt) zuweisen kann und nicht mehrere oder sogar wie hier gewünscht diese Felder auch noch in einem bestimmten Format.

Sicherlich könnte man sich ein eigenen Objekt (List Objekt) erzeugen welches man dann als Datenquelle für die Bindung verwendet, aber es geht auch (viel) einfacher.

Hierzu verwenden wir dem Format Event der Combobox.

Dieser Event bekommt zwei Parameter mit übergeben:

object sender

ListControlConvertEventArgs e

Wobei wir für die hier beschriebene Lösung lediglich den ListControlConvertEventArgs Parameter benötigen und auch verwenden.

in e.ListItem wird das Datengebundene Objekt übergeben und in e.Value kann man den gewünschten Wert zurückgeben.

Hier ein zusammenhängendes Beispiel:

private void cmbSpediteurKontakt_Format(object sender, ListControlConvertEventArgs e)
{
	// Hier caste ich mir den übergeben Wert in das original Objekt
	var spediteurKontakt = ((SpediteureKontakte) e.ListItem);  
	// Und nun bastele ich mir die gewünschet Anzeige zusammen
	e.Value = String.Format("{0}, {1}", spediteurKontakt.Nachname, spediteurKontakt.Vorname);
}

Und das Ergebnis sieht dann so aus:

image

Tips und Tricks | C#
Wednesday, December 30, 2009 3:07:00 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, September 29, 2009

In einer Windows Anwendung wird in einer Form unter anderem ein (eigentlich sind es zwei, aber dazu später mehr) DateTimePicker verwendet um Termine einzugeben.

Da der DateTimePicker nicht gerade komfortabel ist um sowohl Datum als auch Uhrzeiten einzugeben, wird ein DateTimePicker in der Option DropDownCalender (das ist deaktivierte Eigenschaft ShowUpDown) eingesetzt um das Datum elegant aus dem DropDownCalender auszuwählen und ein zweites DateTimePicker Control mit der gesetzen Eigenschaft ShowUpDown und dem entsprechenden Custom Format HH:mm um nur die Uhrzeit mit den Pfeilen hoch und runter einstellen zu können.

image

Nun ist es aber so, dass man im DateTimePicker keinen Inkrement Wert eingeben kann, um den die Minuten erhöht bzw vermindert werden wenn man einmal auf den Pfeil hoch oder runter klickt. Somit wird jeweils um eine Minute hoch oder runter gezählt.

In meinem Fall nun wollte der Kunde aber das Termine nicht Minutengenau sondern immer auf 15 Minuten (Viertelstunde) Basis, also 00, 15, 30 und 45 erfasst werden können.

Da ich nicht gleich ein eigenen Control entwickeln wollte, habe ich einfach eine kleine Routine in den Change Event des DateTimePicker eingebaut.

Nachfolgend der Code Ich denke die Routine ist selbsterklärend

private void startzeitTimePicker_ValueChanged(object sender, EventArgs e)
{
    DateTimePicker dtp = (DateTimePicker)sender;
    // Wenn nicht Minute nicht 0,15,45 oder 60 dann müssen wir was tun
    if ((((dtp.Value.Minute != 0) && (dtp.Value.Minute != 15)) && (dtp.Value.Minute != 30)) &&
        (dtp.Value.Minute != 45))
    {
        // Auch noch einfach, nur 14 Minuten drauf und gut ist
        if ((dtp.Value.Minute == 1) || (dtp.Value.Minute == 16) || (dtp.Value.Minute == 31) || (dtp.Value.Minute == 46))
        {
            dtp.Value = dtp.Value.AddMinutes(14);
        }
        else
        {
            // Zeit wurde heruntergezählt.
            if ((dtp.Value.Minute == 14) || (dtp.Value.Minute == 29) || (dtp.Value.Minute == 44) || (dtp.Value.Minute == 59))
            {
                int x = 14;     // Deshalb auf jeden Fall mal 14 Minuten abziehen,
                if (dtp.Value.Minute == 59)  // und wenn die Minuten auf 59 stehen, dann muss noch eine Stunde (60 minuten) mehr abgezogen werden
                {
                    x += 60;
                }
                dtp.Value = dtp.Value.AddMinutes(-x);
            }
            else // Wert muss manuell eingegeben worden sein, dann runden wir auf die nächst höhere Viertelstunde
            {
                for (int i = 0; i < 15; i++)
                {
                    int x = dtp.Value.Minute + i;
                    if ((x == 15) || (x == 30) || (x == 45) || (x == 60))
                    {
                        dtp.Value = dtp.Value.AddMinutes(i);
                        break;
                    }
                }
            }
        }
    }
}

Hoffe das hilft dem einen oder anderen (Oder mir selbst beim nächsten mal wenn ich vor dem gleichen Problem stehen Wink)

Code | Tips und Tricks | C#
Tuesday, September 29, 2009 12:39:00 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, March 02, 2009

Auch wenn wir uns im Zeitalter von NET Framework 3.5 befinden ist dieser Beitrag in der Überschrift mit .NET 2.0 beschrieben, da sich in an dem Konzept der Anwendungseinstellungen seit .NET 2.0 keine Änderungen ergeben haben.

Mit dem Konzept der Anwendungseinstellungen wurde ein mehr oder weniger durchgängiges Konzept zur Speicherung und Verwendung von Anwendungs- und Benutzerspezifischen Daten eingeführt.

Bei der Einstellungen für eine Anwendung wird dabei zwischen zwei Bereichen (Benutzer / Anwendung) unterschieden.

Im Bereich Benutzer können die Einstellungen aus dem Programmcode sowohl gelesen als auch geschrieben werden.

Beispiel (Überprüfen ob eine Einstellung korrekt ist, wenn nicht dann aktualisieren und speichern):

// Lagereinheit Report
reportName = Settings.Default.Lagereinheit_ReportName;
if (!File.Exists(reportName))
{
    string tempName = File.GetFilenName(reportName);
    tempName = Application.StartupPath + "\\Reports\\" + tempName;
    if (File.Exists(tempName))
    {
        Settings.Default.Lagereinheit_ReportName = tempName;
        Settings.Default.Save();
    }
}

Im Bereich Anwendung können die Einstellungen aus dem Programmcode lediglich gelesen werden (Schreibgeschützt), der obige Programmcode würde also einen Fehler erzeugen, da die Anwendungseinstellungen Schreibgeschützt sind.

Was aber, wenn man eine Einstellung die als Anwendungseinstellung definiert ist, durch Programmcode ändern und in die Config Datei zurückschreiben muss.

Dann kann man dazu folgende statische Methode verwenden:

private static void setAppSetting(string SettingdName, string SettingValue)
{
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.PreserveWhitespace = true;
    xmlDoc.Load(config.FilePath.Trim());
    XmlNode appSettingNode = xmlDoc.SelectSingleNode("configuration/applicationSettings");
    XmlNode settingNode = appSettingNode.SelectSingleNode(string.Format("//setting[@name='{0}']", SettingdName));
    XmlNode valueNode = settingNode.SelectSingleNode("value");
    valueNode.InnerText = SettingValue;
    xmlDoc.Save(config.FilePath.Trim());
}

Nachfolgend ein Beispiel wie man diese Methode im Programmcode einsetzt um eine “normerweise Schreibgeschütze” Einstellung zu ändern.

// auf normalem Weg lesend auf die Anwendungseinstellung zugreifen
if (Properties.Settings.Default.ApplicationVerladestelle == "DDC") 
{
	// Und hier mit Hilfe der setAppSetting Methode einen neuen Wert zuweisen und in die config Datei schreiben
    setAppSetting("ApplicationVerladestelle", "DCW");  
}

Settings.Default.Reload();  // Nicht vergessen, die aktuellen Werte noch mal zu laden
Code | Tips und Tricks
Monday, March 02, 2009 4:43:27 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [7]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, January 06, 2009

Hintergrund

In einer Windows Form Applikation wird in einer Form ein DataGridView verwendet das je nach Bedarf Schreibgeschützt (Alle Spalten) oder zur Eingabe von Daten (nicht Schreibgeschützt) verwendet werden soll. Soweit würde die Anforderung auch kein Problem darstellen, wenn - aber dazu lest einfach den Rest des Beitrags.

Nun ist es aber so, dass auch in der Variante indem das DataGridView als Eingabe (also nicht Schreibgeschützt) verwendet wird, die eine oder andere Spalte des DataGridView sehr wohl (zur Zeit der Entwicklung, also nicht zur Laufzeit) auf Schreibgeschützt gesetzt wurden.

Eigentlich sollte das ganze kein Problem darstellen, also "Einfach sein", wie es im Lied der FANTA4 beschrieben ist, aber wie geht es im Lied weiter; "is es aber nicht", und genau so ist es auch in dem hier beschriebenen Fall.

Das Problem liegt daran, dass durch das setzen der ReadOnly Eigenschaft des DataGridView auf True das DataGridView "vergisst" welche Spalten des DataGridView während der Entwicklungszeit auf ReadOnly gesetzt wurden.

Nachdem man während der Laufzeit die ReadOnly Eigenschaft des DataGridView einmal auf True und dann später wieder zurück auf False gesetzt hat, sind alle Spalten, auch diese die vorher ReadOnly waren plötzlich nicht mehr Schreibgeschützt.

Die Idee

Die Idee der nachfolgenden Lösung war schnell entstanden und wird im folgenden beschrieben.

Beim Start der Windows Form (am besten direkt im Konstruktor) muss man die Information der Schreibgeschützten Spalten sichern, und diese nachdem die ReadOnly Eigenschaft des DataGridView zurück auf False geändert wurde, einfach wieder auf die ursprünglichen Werte herstellen.

Die Lösung

Um die Information, welche Spalten beim Start der Windows Form Schreibgeschützt sind zu speichern, verwende ich ein typisiertes Array von int Werten um den Spalten Index der Schreibgeschützten Spalten zu sichern.

private List<int> saveReadOnlyColumn;

Die Logik zum Speichern der Informationen habe ich in eine eigene Methode (siehe nachfolgenden Code der Methode saveReadOnlyColumnInformation) ausgelagert, welche ich dann einfach am Ende des Konstruktor aufrufe.

private void saveReadOnlyColumnInformation()
{
    saveReadOnlyColumn = new List<int>();

    foreach (DataGridViewColumn o in dgVerladung.Columns)
    {
        if (o.ReadOnly)
        {
            saveReadOnlyColumn.Add(o.Index);
        }
    }
}

So nun haben wir die Informationen der Schreibgeschützten Spalten im typisierten Array für den späteren Gebrauch gespeichert.

Dann kümmern wir uns nun noch darum, diese gespeicherte Information immer dann wenn die ReadOnly Eigenschaft auf False gesetzt wurde, wieder auf die Startwerte zurücksetzen.

Hierzu können wir einfach den ReadOnlyChanged Event des DataGridView verwenden, welcher immer dann ausgelöst wird, wenn man im Code die ReadOnly Eigenschaft ändert.

Nachfolgend ist der Code dargestellt um die vorher gespeicherten Schreibgeschützten Spalten wieder herzustellen

private void dgVerladung_ReadOnlyChanged(object sender, EventArgs e)
{
    if (dgVerladung.ReadOnly == false)
    {
        foreach (int i in saveReadOnlyColumn)
        {
            dgVerladung.Columns[i].ReadOnly = true;
        }
    }
}

 

Der Vollständigkeit halber hier noch der Code des Konstruktor's (Hier erklärt die Zeile 4) dargestellt.

public formPlanungen()
{
    InitializeComponent();
    saveReadOnlyColumnInformation();
    Application.Idle += new EventHandler(Application_Idle);
}
Programmierung | Code | Tips und Tricks | C#
Tuesday, January 06, 2009 11:26:34 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Friday, April 04, 2008

Auf meinem Beitrag "Erweiterungsmethoden am Beispiel einer isNumeric Methode für die String Klasse - C#" habe ich einige Interessante Reaktionen gehabt. Auch wenn die Reaktionen mal wieder über E-Mail und im Gespräch gemacht wurden und nicht über Kommentare direkt am Beitrag gemacht wurden. Ich glaube, soweit sind wir im Deutschsprachigen Raum noch immer nicht. Es gibt immer noch ganz viele die sich einfach nicht trauen einen öffentlichen Kommentar zu hinterlassen. In USA und England ist das ganz anders. Aber gut das ist ein anderes Thema.

Unter anderem wurde darauf hingewiesen, dass die Erweiterung der String Klasse um die Methode isNumeric ganz nett sei, aber nicht das gleiche wie eine Statische Methode wie ich sie im Beitrag "isNumeric - c# oder csharp" beschrieben habe. Die isNumeric Methode aus dem letztgenannten Beitrag lässt sich auf jedes Objekt und nicht nur auf Strings anwenden.

Und natürlich habe diese Kollegen recht, mit dieser Aussage.

Dann wollen wir doch einfach unsere Implementierung aus dem erst genannten Beitrag so ändern, dass Sie auch auf jedes Objekt angewendet werden kann.

Schauen wir uns dazu doch einmal die Herkunft der Datentypen an. Alle Objekt die wir erzeugen wurde irgendwann man vom Typ object abgeleitet, also warum erweitern wir nicht einfach die Basis aller Klassen und Strukturen.

Hier nun die Erweiterungsmethode isNumeric für den Basis Objekt Type object:

    public static class ObjectExtensions
    {
        public static bool isNumeric(this object o)
        {
            double retNum;
            bool isNum = Double.TryParse(Convert.ToString(o), System.Globalization.NumberStyles.Any,
                                         System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
            return isNum;
        }
    }

Und wie man nachfolgend sehen kann, ist es nun möglich die Methode isNumeric auf nahezu jedes Objekt anzuwenden:

        static void Main()
        {
            int i = 10;
            long l = 313131311;
            double d = 12.12;
            string s = "100";

            test(i);
            test(l);
            test(d);
            test(s);

        }

        private static void test(object o)
        {
            if (o.isNumeric())
            {
                Console.WriteLine("Ja das geht auch");
            }
        }
Programmierung | Tips und Tricks | C#
Friday, April 04, 2008 6:08:17 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, March 27, 2008

Wer sich mit WinForms Entwicklung beschäftigt wird wohl auch mit dem DataGridView Control arbeiten.

Wenn man nun für die Spalte ein Checkbox Control "DataGridViewCheckBoxColumn" verwendet kommt es häufig vor, dass man auch auf das CheckedChanged Ereignis reagieren möchte.

Leider gibt es mit dem DataGridView Control nicht direkt die Möglichkeit einen solchen Event zu nutzen.

Außerdem wird bei der Verwendung von Datengebundenen Informationen an das DataGridView Control erst nach verlassen der aktiven Zelle die Dirty Eigenschaft der Zelle gesetzt, so dass dies nicht mehr zulässt direkt auf Änderung der Checked Eigenschaft zu reagieren.

Es gibt aber, wie fast immer, Abhilfe für das Problem.

Um  bereits vor dem Verlassen der Zelle, die Änderung der Checked Eigenschaft auszuwerten, kann man dem DataGridView den Event CurrentCellDirtyStateChanged hinzufügen und in diesem Event den nachfolgend dargestellten Code hinzufügen:

Als Einfaches Beispiel (Allgemeingültig):

private void dgVerladung_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (((DataGridView)sender).CurrentCell.OwningColumn is DataGridViewCheckBoxColumn)
    {
        ((DataGridView)sender).CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}

Ein etwas komplexeres Beispiel (nur als Beispiel):

    private void dgWorkTime_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgWorkTime.CurrentCell.OwningColumn.Name == disabledDataGridViewCheckBoxColumn.Name)
    {
        dgWorkTime.CommitEdit(DataGridViewDataErrorContexts.Commit);
        Day meinTag = (Day)dgWorkTime.CurrentCell.OwningRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Value;
        bool disabled = (bool)dgWorkTime.CurrentCell.Value;
        dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].ReadOnly = disabled;
        dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].ReadOnly = disabled;
        if (disabled)
        {
            dgWorkTime.CurrentRow.Cells[wochentagDataGridViewTextBoxColumn.Name].Style.BackColor =
                Properties.Settings.Default.WorkingTimeDisabledColor;
            dgWorkTime.CurrentRow.Cells[disabledDataGridViewCheckBoxColumn.Name].Style.BackColor =
                Properties.Settings.Default.WorkingTimeDisabledColor;
            dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
                Properties.Settings.Default.WorkingTimeDisabledColor;
            dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
                Properties.Settings.Default.WorkingTimeDisabledColor;
        }
        else
        {
            dgWorkTime.CurrentRow.Cells[wochentagDataGridViewTextBoxColumn.Name].Style.BackColor =
                dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
            dgWorkTime.CurrentRow.Cells[disabledDataGridViewCheckBoxColumn.Name].Style.BackColor =
                dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
            dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
                dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
            dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
                dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;

        }


    }
Programmierung | Tips und Tricks | C#
Thursday, March 27, 2008 7:43:00 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, March 20, 2008

Da sich Microsoft nicht einig ist wie man die Tage einer Woche in einer Aufzählung hinterlegt, muss man damit klarkommen, dass es verschiedene Auflistungen in verschiedenen Namensräumen gibt.

In einem meiner Projekte war ich nun auch damit konfrontiert, dass ich mit den beiden Aufzählungen:

  • DayOfWeek aus dem Namensraum System

und

  • Day aus dem Namensraum System.Windows.Forms

gleichzeitig zu tun hatte.

Dabei ergab sich die Notwendigkeit dass DayOfWeek in Day umwandeln musste.

Nachfolgend nun eine kleine Methode die aus DayOfWeek ein Day zurückgibt.

public static Day DayOfWeekToDay(DayOfWeek dow)
{
    return (Day)Enum.Parse(typeof(Day), dow.ToString());
}
Programmierung | Tips und Tricks | C#
Thursday, March 20, 2008 7:11:31 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, March 17, 2008

Auch wenn es auf den ersten Blick absurd erscheint, dass man eine Funktion benötigt um den ersten Tag eines Monats zu ermitteln, aber es gibt Situationen in welchen man Datumsbereiche bestimmen möchte in denen man aus einem beliebigen Datum den ersten Tag eines Monats ermitteln muss.

Nachfolgen eine C# Methode die genau dies macht:

public static DateTime FirstDateOfTheMonth(DateTime date)
{
    return date.AddDays(-(date.Day - 1));
}       
Programmierung | Tips und Tricks | C#
Monday, March 17, 2008 6:58:52 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [2]  
Autor: Hans-Peter Schelian  |  Trackback
# Wednesday, March 12, 2008

Ein einzelnes Control auf Readonly zu setzen ist einfach, aber was ist wenn man alle Controls einer Winforms Anwendung auf Readonly setzen möchte. Und ich meine wirklich auf Readonly nicht auf Enabled indem man ein Panel als Container der Controls verwendet und dann die Panel Eigenschaft Enabled auf false setzt.

Zu diesem Zweck habe ich mir eine kleine Routine erstellt, welche Rekursiv alle Controls (die ich möchte) einer ControlCollection auf Readonly setzt.

Hier nun die Methode setReadOnly:

private void setReadOnly(Control.ControlCollection Controls)
{
    foreach (Control control in Controls)
    {
        if (control is TextBox)
        {
            ((TextBox)control).ReadOnly = true;

        }
        if (control is ComboBox)
        {
            ((ComboBox)control).Enabled = false;
        }
        if (control is DateTimePicker)
        {
            ((DateTimePicker)control).Enabled = false;
        }

        if (control.Controls.Count > 0)
        {
            setReadOnly(control.Controls);
        }
    }

}
Programmierung | Tips und Tricks | C#
Wednesday, March 12, 2008 6:49:26 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, February 11, 2008

Mit Visual Studio 2008 hat man zum ersten mal die Möglichkeit des sogenannten Multi-Targeting bekommen.

Das bedeutet, dass man bereits beim anlegen eines Projekts den gewünschten Zielframework angeben kann/muss.

Die zur Auswahl stehenden Framework's sind:

  • 2.0
  • 3.0
  • 3.5

Natürlich stehen dann auch nur die im gewählten Framework enthaltenen Funktionalitäten zur Verfügung. So kan man zum Beispiel bei der Auswahl vom Framework 2.0 kein Linq einsetzen usw.

Nun gut, aber das ist nicht das Thema dieses Beitrags.

Nehmen wir an wir haben ein Windows Forms Projekt erstellt das für den Framework 2.0 sein soll.

Nun erstellen wir für unser Projekt noch ein Setup Projekt.

image

Achten auch schön drauf, dass wir auch beim Setup Projekt das richtige Zielframework auswählen.

Und nachdem wir noch alle benötigten Einstellungen für das Setup Projekt gemacht haben, müssen wir dann beim Test feststellen, dass obwohl wir das NET Framework 2.0 als Zielframework gewählt haben die Setup Routine das Framework 3.5 als Voraussetzung verlangt.

Was haben wir falsch gemacht? --> Nichts.

Aus welchem Grund auch immer (na ja irgend jemand hat wohl einen Fehler gemacht) wird beim erstellen eines Setup Projekten mit VS2008 die Voraussetzungen (Prerequisites) so gesetzt, dass der NET Framework 3.5 vorausgesetzt wird, egal was man ausgewählt hat.

Aber, das kann man einfach manuell ändern.

Also Setup Projekt wie beschrieben erstellen, dann in die Eigenschaften des Setup Projektes gehen, dort den Button "Prerequisites" betätigt und in diesem Fenster nun das richtige Framework auswählen.

image

Auf OK Klicken --> Fertig !!

Programmierung | Tips und Tricks | Visual Studio
Monday, February 11, 2008 12:20:23 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Friday, January 25, 2008

Der NET Framework stellt zur Kommunikation mit Windows Diensten die Komponente ServiceController aus dem Namespace System.ServiceProcess zur Verfügung.

Die Verwendung dieser Komponente ist relativ einfach (Siehe nachfolgendes Beispiel).

Hier ein einfaches Beispiel:

ServiceController myServiceController = new ServiceController("ServiceName");

if (myServiceController.Status == ServiceControllerStatus.Stopped )
{
    myServiceController.Start();
}

 

In diesem Beispiel wird für den Service "ServiceName" auf dem lokalen Rechner ein ServiceController erstellt, und anschließend wird geprüft ob der Service gestoppt ist und wenn, dann wird er gestartet.

Aber was ist wenn der Service "SeviceName" gar nicht auf dem lokalen Rechner installiert ist.

Leider besitzt die Komponente ServiceController keine Eigenschaft oder Methode mit welcher man einfach prüfen kann ob der Service auf dem Computer installiert ist.
Es kommt also zu folgender Fehlermeldung Laufzeitfehler:

image

Natürlich kann man mit einem Try Catch Block den Fehler abfangen, aber es geht auch anders, ich vermeide lieber einen vorhersehbaren Fehler als dann in einer Exception darauf zu reagieren.

Zur Demonstration wie man das anders regeln kann, habe ich eine kleine Beispielklasse Namens ServiceControllerHelper geschrieben die eine statische Methode getServiceControllerForService enthält. Dieser Methode übergibt man einfach den Names des Service und erhält, wenn es den Service gibt, ein ServiceController Objekt zurück andernfalls ein null.

Hier nun die Klasse ServiceControllerHelper:

using System;
using System.ServiceProcess;

namespace ServiceControlSample
{
    public static class ServiceControllerHelper
    {
        public static ServiceController getServiceControllerForService(string shortName)
        {
            ServiceController[] services = ServiceController.GetServices();
            foreach (ServiceController service in services)
            {
                if (service.ServiceName == shortName)
                {
                    return service;
                }
            }
            return null;
        }

    }
}

 

Und so kann man diese Klasse einsetzen:

ServiceController myServiceController;

 

myServiceController = ServiceControllerHelper.getServiceControllerForService("ServiceName");

 

if (myServiceController != null)
{
    if (myServiceController.Status == ServiceControllerStatus.Stopped )
    {
        myServiceController.Start();
    }
}

 

Dieser Beitrag soll lediglich als Einstig in dieses Thema Dienen und keine vollständige Implementierung einer ServiceControllerHelper Klasse darstellen.

Code | Tips und Tricks | C#
Friday, January 25, 2008 12:54:49 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, January 24, 2008

Bedingte Kompilierung oder Kleines Präprozessor ABC für C#

Einleitung

Der Präprozessor unter C# ist nicht zu vergleichen mit den wesentlich komplexeren Präprozessoren der C und C++ Compiler.

Es gibt z.B. weder eine Makroverarbeitung noch die Möglichkeit mit #include sogenannte Header Dateien zu inkludieren. Da dieser Beitrag "Kleines Präprozessor ABC" heißt, möchte ich darauf verzichten auf die Einzelheiten solcher Unterschiede einzugehen.

Verfügbare Direktiven des C# Präprozessor

Direktive

Typ

Verwendung

#define Bezeichner Mit #define wird ein Bezeichner gesetzt. Stellen Sie sich einen Bezeichner wie eine boolsche Variable vor. #define ist dann so wie das setzen einer Variablen auf true. Einem Bezeichner können keine Werte zugewiesen werden
#undef Bezeichner Mit #undef heben Sie die Gültigkeit eines Bezeichners auf. Sie können dies auch mit dem setzen einer Variablen auf false setzen vergleichen.
#if Ausdruck Der Code innerhalb des mit #if eingeleiteten Abschnittes wird ausgeführt wenn der Ausdruck wahr ist (Bei einfachen #if Ausdrücken ist das der Fall wenn der auf #if folgende Bezeichner gesetzt ist.
#else   Ergibt der diesem #else vorangegangene Ausdruck false, dann wird der Bereich des #else ausgeführt.
#elif Ausdruck Anstelle des #else kann man mit Hilfe des #elif Ausdrucks diesen Bereich nur dann ausführen lassen, wenn der Angegebene Ausdruck true ergibt
#endif   Zeigt das Ende eines Ausdrucks bzw. einer zusammengehörenden Reihe von Ausdrücken an.
#warning Ausdruck Ausgabe einer Warnung während des Kompilieren's
#error Ausdruck Ausgabe einer Fehlermeldung während des Kompilieren's
#line Ausdruck Ermöglicht die Manipulation und Ausgabe von Zeilennummern, nähere Informationen hierüber kann man hier nachlesen
#region Direktive Bezeichnet den Anfang einer Region im Quelltext.
Beispiel #region Events
#endregion   Ende einer mir #region eingeleiteten Region im Quelltext
#pragma Direktive Mit #pragma gibt man spezielle Anweisungen für das Verhalten des Compilers.
Beispiel: #pragma warning disable 123, 4567 zum Abschalten bestimmter Compiler Warnungen. Nähere Informationen über die #pragma Direktiven gibt es hier

 

Operatoren für die Verwendung mit Ausdrücken

Operatoren Beispiel

Funktionsweise

! #if !DEBUG Das ! negiert den Wert von var. Im Beispiel bedeutet das, ist DEBUG nicht definiert, wird der #if Bereich ausgeführt.
== #if DEBUG == true == Prüft auf Gleichheit. Im Beispiel bedeutet dies, wenn DEBUG definiert ist, dann wird der Bereich des #if Ausdrucks ausgeführt.
!== #if DEBUG !== true == Prüft auf Ungleichheit. Im Beispiel bedeutet dies, wenn DEBUG definiert ist, dann wird der Bereich des #if nicht Ausdrucks ausgeführt.
&& #if DEBUG && TRACE Logisches Und. Wenn DEBUG und TRACE definiert sind, wird der #if Ausdruck wahr, also ausgeführt.
|| #if DEBUG || TRACE Logisches Oder (kein Exklusiv Oder). Der #if Ausdruck ist wahr wenn DEBUG und/oder TRACE definiert sind.

 

Beispiele

Einfaches Beispiel mit einem Define und einer Bedingten Kompilierung

#define DEBUG

#if DEBUG
     // Diese Bereich wird nur kompiliert wenn DEBUG definiert ist
    System.Diagnostics.Debug.WriteLine("DEBUG ist definiert");
#endif

Beispiel einer Anwendung mit einem error Ausdruck und einem #elif

#define RELEASE
#define DEMO

#if RELASE && DEMO
     #error RELEASE und DEMO dürfen nicht zusammen verwendet werden
#elif DEMO
     // HIER zum Beispiel der Code, der eine DEMO Version beschränkt, oder was auch immer
#endif

Programmierung | Tips und Tricks | C#
Thursday, January 24, 2008 1:31:45 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, January 22, 2008

Vor einiger Zeit hatte ich in diesem Beitrag beschrieben wie man Transaktionen mit netTiers verwenden kann. In diesem Beitrag nun möchte ich eine weiter fortgeschrittene Technik zeigen mit welcher man die Transaktion auch in verschachtelten und rekursiven Methoden verwenden kann.

Hintergrund

Obwohl es keine "echten" verschachtelten Transaktionen sind, habe ich die Überschrift so gewählt, da das Ergebnis dem von verschachtelten Transaktionen gleicht.

Der nachfolgende Beitrag erläutert die notwendigen zusätzlichen Vorkehrungen die man treffen muss um Transaktionen in verschachtelten und rekursiven Methodenaufrufen zu verwenden.

Lösungsansatz

Um Transaktionen in verschachtelten Aufrufen verwenden zu können, ist es notwendig festzustellen, ob bereits eine Transaktion vorhanden ist und ob diese geöffnet (BeginTransaktion) ist.

Gibt es eine aktive (äussere) Transaktion, so ist innerhalb der geschachtelten Methode dafür zu sorgen, dass keine lokale Transaktion erzeugt wird, bzw. dass keine globalen Methoden Aufrufe (wie Commit und Rollback) der Transaktion aufgerufen werden (Die Transaktion gehört der aufrufenden Methode, die sich darum kümmern muss ob ein Commit oder Rollback gemacht wird).

Lösung - Erweiterte Version mit localTransaction

int retval = 0;    // Definieren einer int variablen die einen Rückgabewert der Methode aufnehmen kann.
bool localTransaction = false;    // Flag welches verwendet wird um die Verwendung eine lokalen Transaktion zu signalisieren

if (ConnectionScope.Current.TransactionManager == null)
{
    ConnectionScope.Current.TransactionManager = ConnectionScope.CreateTransaction();
    localTransaction = true;    // Es gab keine äussere Transaktion also haben wir eine lokale erstellt und auch gleich geöffnet (impliziter Aufruf von BeginTransaction)
}
TransactionManager tm = ConnectionScope.Current.TransactionManager;
if (!tm.IsOpen)  // jetzt prüfen ob auch eine geöffnete Transaktion vorhanden ist (wenn nicht, dann eine lokale Transaktion aufmachen)
{
    tm.BeginTransaction();
    localTransaction = true;
}               

 

// Hier nun der eigentlich Code
// Wenn alle Datenbankoperationen durchgeführt werden konnten, wird 0 zurückgegeben, sonst ein int Wert aus dem die Aufrufende Methode den Grund des Fehlers erkennen kann und dadurch entscheiden kann ob ein Commit oder ein Rollback durchgeführt werden soll.

if (retval == 0)
{
    if (localTransaction )   // Nur wenn eine lokale Transaktion, dann ein Commit, wenn alles OK war.
    {
        tm.Commit();   
    }
}
else
{
    if (localTransaction )   // Nur wenn eine lokale Transaktion, dann ein Rollback wenn ein Fehler auftritt
    {
        tm.Rollback();   
    }
}

return retval;

Programmierung | Tips und Tricks | C#
Tuesday, January 22, 2008 7:56:08 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, January 17, 2008

Jeder der sich mit der Entwicklung von Programmen beschäftigt kommt früher oder später um die Verwendung eines Debuggers zur Fehlersuche nicht umhin.  

Doch bevor ich weiter schreibe zuerst eine kurze Begriffserklärung der wichtigsten Begriffe:

Bug-1                                 debugger

Was bedeutet denn Debuggen und was machen wir um einen Fehler zu finden?

  • Das Programm wird in der Entwicklungsumgebung im Debug Modus gestartet.
  • Es werden Breakpoints gesetzt.
  • Inhalte von Variablen werden angesehen (evtl. verändert)
  • Der Ablauf des Programms wird verfolgt

Um Komplexere Abläufe zu beobachten bietet es sich an Werte einfach über nachfolgende Methoden

  • System.Diagnostics.Debug.WriteLine("")
  • System.Diagnostics.Trace.WriteLine("")

im Ausgabefenster von Visual Studio auszugeben.

Soweit sind die Möglichkeiten recht umfangreich um einem Fehler auf die Spur zu kommen.

Was aber wenn der Fehler, der beim Kunden auftritt, einfach nicht auf unserem Entwicklungsrechner reproduzierbar ist. Wie kann man vorgehen um mehr Informationen über das Problem zu erhalten, ohne dem Kunden eine Entwicklungsumgebung auf seinen Rechner zu bügeln.

Eventuell würde es ja schon helfen wenn wir die Originaldaten (Datenbank, Dokumente etc.) vom Kunden hätten, auch das können wir uns in vielen Fällen einfach abschminken und darüber sollten wir in den meisten Fällen auch gar nicht nachdenken.

Man kann aber bereits während der Entwicklung eine Menge dazu beitragen, später einmal eine recht gute Fehleranalyse auch vor Ort beim Kunden zu erhalten.

Wenn wir während der Entwicklung, an Stellen an von welchen wir glauben Informationen für eine Fehleranalyse bekommen zu können bereits vorsorglich über die Write... Methoden der Klassen Debug und Trace des System.Diagnostics Namespace Informationen bereitstellen, dann können wir später diese Informationen dazu verwenden einen möglichen Fehler zu analysieren.

Damit wir aber später in einer Anwendung, die dann nicht im Debugger ausgeführt wird, an diese Informationen herankommen, können wir durch Einrichtung eines TraceListener dafür sorgen, dass diese Informationen auf einem beliebigen (na ja fast, es muss ein TraceListener für die gewünschte Ausgabe vorhanden sein, oder wir müssen uns einen eigenen TraceListener schreiben) Ausgabemedium wie Standard Output, einer Textdatei, einer XML Datei eine ... usw. einfach ausgeben werden.

Schauen wir uns doch kurz nachfolgende Aufstellung an welche TraceListener uns das NET Framework (ab 2.0) bereits zur Verfügung stellt:

Die Abstrakte Basis Klasse im Namespace System.Diagnostics (Von der können wir eigene TraceListener ableiten):

  • TraceListener

Folgende Implementierungen sind im Namespace System.Diagnostics verfügbar:

  • ConsoleTraceListener
  • DefaultTraceListener
  • DelimitedListTraceListener
  • Eventing.EventProviderTraceListener
  • EventLogTraceListener
  • EventSchemaTraceListener
  • TextWriterTraceListener
  • XmlWriterTraceListener

Was mach nun ein solcher TraceListener und wie können wir Ihn verwenden.

In einer .NET Anwendung wird in der Klasse Trace des  Namespace System.Diagnostics unter anderem eine statische Eigenschaft Listeners vorgehalten, diese Auflistung enthält alle TraceListener die bei den Debug und Trace Methoden aufgerufen werden.

Wird nun in unserem Code ein Methodenaufruf wie System.Diagnostics.Debug.WriteLine(txtName.Text) durchgeführt, dann führt das dazu, dass für jede in der Auflistung enthaltenen Listener die implementierte Methode WriteLine aufgerufen wird.

Wenn wir das in einem Visual Studio Projekt im Debugger Mode machen ohne irgendwelche Änderungen an der Konfiguration oder Manipulation der Auflistung in unserem Code vorzunehmen, dann enthält die Auflistung Listeners lediglich den Default TraceListener, und das ist der Listener, welche die Ausgabe im Ausgabefenster von Visual Studio vornimmt.

Wie können wir nun einen anderen TraceListener, als Beispiel den TextWriterTraceListener (Dieser Listener schreibt die Daten in eine Textdatei) verwenden um die Ausgaben für uns verwendbar zu machen, auch ohne die Anwendung im Visual Studio auszuführen?

Es gibt 2 Arten wie wir den/die Listener angeben können:

  • Aus dem Source Code
  • Aus der App.config
Angabe eines Listener aus dem Source Code

Hier im Beispiel einer Windows Forms Anwendung:

Wir fügen einfach in den Konstruktor der Windows Form folgenden Code hinzu:

System.Diagnostics.TextWriterTraceListener tListener = new System.Diagnostics.TextWriterTraceListener("Trace.Log");
System.Diagnostics.Trace.Listeners.Add(tListener);

Dieser Code fügt einen neuen Listener, in unserem Fall den TextWriterTraceListener zur Auflistung des Listener hinzu (siehe nachfolgende Abbildung):

Listener Auflistung 

Es geht aber noch einfacher, ja wirklich "noch einfacher".

Angabe des Listener in der App.Config

Um einen oder auch mehrere Listener hinzuzufügen oder auch um einen Listener (z.B. den Default Listener) zu entfernen können wir innerhalb des Bereich "Config" einen eigenen Unter-Bereich System.Diagnostics hinzufügen.

In diesem Bereich können wir dann über die App.Config Datei auf die Listeners Auflistung zugreifen und Veränderungen durchführen.

Hier ein Beispiel, welche den TextWriterListener hinzufügt und außerdem noch den Default Listener entfernt.

<configuration>
  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="FileListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Trace.log" />
        <remove name="Default" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

Soweit zur Einrichtung von TraceListener zur besseren Fehleranalyse beim Kunden.

imageÜbrigens sind Trace und Debug Statements eigentlich vollkommen gleich, ob ein Trace oder ein Debug Aufruf ausgeführt wird, hängt davon ab, ob die vordefinierten Konstanten für bedingte Compilierung während der Erstellung des Programms gesetzt waren (In Visual Studio findet man diese Schalter übrigens in den Projekt Eigenschaften auf dem Karteireiter Build (Erstellen)).

 

Und nun viel Spaß bei der Suche nach den Käfern !

Ich finde die Geschichte um die Herkunft der Bezeichnung Bug für einen Programmfehler trotz der Behauptung das es sich um eine moderne Legende handeln soll, schön und lesenswert.

Wer möchte kann hier mehr über diese Legende lesen

PS: Ich Denke, in nicht so ferner Zukunft, werde ich dann mal über die Erstellung eines eigenen Listener berichten.

Programmierung | Tips und Tricks | C#
Thursday, January 17, 2008 3:21:39 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, January 10, 2008

Bei using muss man zwischen den nachfolgenden Optionen unterscheiden:

  • using --> Direktive
  • using --> Anweisung

using Direktive

using als Direktive zum einbinden von Namespaces kennt sicherlich jeder der mit C# schon einmal etwas programmiert hat, oder sei es auch nur jemand der einen C# Quelltext gesehen hat. Es kommt wohl kein Programm ohne den Import eines Namespaces mit Hilfe der using Direktive aus.

Beispiel für die using Direktive:

using System;
using System.Collections.Generic;

using Anweisung

using als Anweisung hingegen ist vielen sicherlich nicht so geläufig.

Was ist nun die using Anweisung und für was setzt man diese ein.

using als Anweisung definiert einen Bereich in welchem das in der using Anweisung definierte Objekt seine Gültigkeit hat. Das in der using Anweisung deklarierte Objekt wird, nachdem der mit using definierte Gültigkeitsbereich verlasen wird, automatisch verworfen (Dispose etc.).

Man teilt dem NET Framework damit ganz unmissverständlich mit, dass das in der using Anweisung deklarierte oder auch nur einfach eingeschlossene Objekt nach dem verlassen des Gültigkeitsbereiches nicht mehr benötigt wird und dieses zerstört (Referenzen entfernt und Speicher freigegeben werden kann und soll) werden soll.

Damit ein Objekt in der using Anweisung verwendet werden kann muss dieses die IDisposable Schnittstelle implementiert haben.

Mehr Detail Informationen zur using Anweisung gibt es hier

http://msdn2.microsoft.com/de-de/library/yh598w02(VS.80).aspx

Tips und Tricks | C#
Thursday, January 10, 2008 7:51:34 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, December 18, 2007

Spätestens beim Debuggen von WPF Anwendungen sollte man eine Default Einstellung des Visual Studio 2008 ändern.

In den Option kann man dem Debugger sagen, er soll nur Code, den man selbst geschrieben hat Debuggen, Code der von einem Designer in Visual Studio erzeugt wird, wird dann nicht mehr beim Debuggen berücksichtigt.

Ein eventuell auftretender Fehler kann dadurch nicht konsequent verfolgt werden.

Beim Debuggen von WPF Anwendungen geht das soweit, dass selbst Fehler im XAML Code nicht mehr mit dem Debugger verfolgt werden können.

Also sollte man in die Optionen gehen und dort die folgende Einstellung vornehmen.

image

Die Option Enable Just My Code (Managed only) ist per Vorgabe aktiviert. Also einfach die Option deaktivieren und schon verfolgt der Debugger auch Code, der durch Designer erstellt wurden.

Programmierung | Tips und Tricks
Tuesday, December 18, 2007 12:57:06 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, December 10, 2007

Eine immer wieder gestellte Frage lautet: Wie bilde ich die Funktion IIf() unter C# nach, oder gibt es direkt eine vergleichbare Funktion unter C#.

Unter C# ist diese Funktion tatsächlich nicht verfügbar, aber wie wir gleich sehen werden, wird sie auch nicht wirklich vermisst, denn C# hat dafür den mächtigen Bedingten Operator ?:.

Eine Logik wie hier unter VB dargestellt (Beispiel: Gib die größere von zwei Zahlen als Rückgabewert an die linke variable zurück):

Dim max As Integer = IIf(x > y, x, y)

Die Variable max bekommt, wenn x größer ist als y den Wert von x, ansonsten (y ist gleich oder größer x) den Wert von y zugewiesen.

kann unter C# unter Zuhilfenahme des ? Operators wie folgt gelöst werden:

int max = (x > y) ? x : y;

Allgemein gesagt hört sich das so an: Der bedingte Operator (?:) gibt abhängig vom Wert eines logischen Ausdrucks einen von zwei Werten zurück.

Etwas näher beschrieben lautet das dann so:

Auf der linken Seite des Operators ist ein logischer Ausdruck, der, wenn man Ihn auswertet, true oder false ergeben kann.

Je nachdem ob er true oder false ergibt wird der Rechte oder linke Ausdruck auf der rechten Seite des Operators ? ausgewertet, also das links neben dem : oder eben rechts davon stehende.

Übrigens können die Ausdrücke sowohl auf der linken als auch auf der rechten Seite des ? Operators durchaus sehr komplex sein, ein weiteres kleines Beispiel zur Demonstration von etwas komplexeren Ausdrücken wird nachfolgend dargestellt:

string currency = "$";
double price = 12.23;
double money = (currency == "$")?(price * 1.56):(price);


Mehr zum Thema interessante Operatoren in C# gibt es unter anderem hier : Operator ??

Tips und Tricks | C#
Monday, December 10, 2007 2:30:44 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, December 03, 2007

Eine Fehlermeldung wie diese:

"Der Zugriff auf das Steuerelement txtBarcode erfolgte von einem anderen Thread"

oder so ähnlich, deutet daraufhin, dass versucht wird aus einem anderen Thread Zugriff auf ein Control zu nehmen als der in dem das Control erstellt wurde.

Das geschieht zum Beispiel wenn, man im Timer Event oder dem DataReceived Event der SerialPort Klasse aus dem System.IO.Ports Namespace versucht auf ein Control in einem Windows Forms Programm zuzugreifen.

Eine Lösung für das Problem zeigt das nachfolgende Beispiel indem ein Barcode Scanner an die seriellen Schnittstelle angeschlossen ist und der Barcode in der Text Eigenschaft des Controls txtBarcode ausgegeben werden soll.

Hier nun der Code Ausschnitt des DataReceived Events mit Thread sicherem Zugriff auf das txtBarcode Control:

if (this.txtBarcode.InvokeRequired)
{
    this.txtBarcode.Invoke((MethodInvoker)delegate()
    {
        this.txtBarcode.Text = bCode;
    }
    );
}
else
{
    this.txtBarcode.Text = bCode;
}
Tips und Tricks | C#
Monday, December 03, 2007 5:21:18 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, November 29, 2007

Einfach, kann Einfach, Einfach sein !

Sicherlich für viele ein alter Hut, aber ich habe das so vorher noch nie versucht oder gemacht.

Die Aufgabe die sich mir (wirklich nicht zum ersten mal) gestellt hat, war wie folgt:

Erstellen einer Interface Datei zur Übergabe von Informationen aus einem EDV System A zu einem EDV System B. (In dem Fall Gestern ging es um Informationen von LKW Verladungen für DHL)

Die Übergabe Datei soll in einer Textdatei mit festen Feldängen übergeben werden. Hierzu habe ich eine Definition bekommen, welche die Felder, die Reihenfolge der Felder und die Länge der Felder definiert hat..

Da ich nicht alle Felder (es waren auch nicht alles Pflichtfelder) in meinem System zur Verfügung hatte, musste ich an manchen Stellen einfach die fehlenden Daten mit den entsprechenden Anzahl Leerzeichen auffüllen.

Das kann man so machen (basteln wir uns den String zusammen):

string buffer;
buffer = "1234567890"; // Das könnte die Kundennummer sein
buffer += "         "; // Hier soll zum Beispiel der Spitzname mit 10 Stellen des Kunden hin 
// den habe ich aber nicht also 10 Leerzeichen -
// aber sind es wirklich 10 und was machen wenn es 500 Zeichen sein sollen
//dann geht das zum Beispiel einfach so:
buffer += "".PadLeft(500);

Wie gesagt, manchmal kann Einfach, einfach, Einfach sein

Tips und Tricks | C#
Thursday, November 29, 2007 9:30:47 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, November 22, 2007

Nachdem ich bereits vor einiger Zeit auf meiner Workstation die mit einem englischen Windows XP ausgestattet ist die Visual Studio Beta 2 (VS2008) ohne Probleme installieren und auch verwenden konnten, habe ich vor einigen Tagen zum weiteren Test, VS2008 Beta 2 noch zusätzlich auf einem Notebook (Windows XP in Deutsch) installiert.

image Die Installation verlief dabei auch ohne Probleme, danach konnte ich auch VS2008 ohne Probleme starten und ein Projekt, egal welches erstellen und auch speichern.

Wenn ich nun aber F5 zum ausführen/starten des Projektes betätigt habe, wurde kurz mit dem Übersetzen des Projektes gestartet und dann hat sich das ganze Visual Studio einfach ohne weitere Meldung verabschiedet (geschlossen). Gerade so, als hätte man Beenden ausgewählt.

Nachdem ich mir das etwas näher angeschaut habe ist mir aufgefallen, dass das Problem nicht an der Deutschen Installation liegt, sondern daran dass VS2008 Beta 2 ein Problem damit hat, wenn der Pfad/Dateiname des Projektes mehr als 64 Zeichen lang ist.

Das wiederum ist auf einer Deutschen Maschine in der Standard-Installation leicht der Fall, wie im folgenden Beispiel zu sehen ist:

C:\Dokumente und Einstellungen\Testuser.TESTDOMAIN\Eigene Dateien\Visual Studio 2008\WindowsFormsApplication1\WindowsFormsApplication1.sln.

Also einfach darauf achten, dass die Pfade für die Projekte nicht zu lang werden.

Zum Beispiel C:\Projekte\...

Programmierung | Visual Studio | Tips und Tricks
Thursday, November 22, 2007 8:55:57 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]  
Autor: Hans-Peter Schelian  |  Trackback
# Wednesday, October 24, 2007

Der Titel dieses Beitrages sollte eigentlich besser lauten:

EventType clr20r3 - oder starte niemals einen Windows Dienst ohne eine globale Fehlerbehandlung.

Ich hatte einen neuen Windows Dienst geschrieben, der wie schon so viele Dienste vorher die Aufgabe zur Systemüberwachung auf eingehende Interfacedateien per FTP durchführen sollte.

Der Dienst bezieht aus einer SQL Datenbank die Informationen für welche Mandanten dieser Dienst zu verrichten ist und überwacht dann die ebenfalls in der SQL angegebenen FTP Verzeichnisse auf eingehende neue Daten. Sind neue Daten vorhanden lädt der Dienst diese Dateien herunter, verarbeitet diese und löscht dann wenn alles erfolgreich war die Daten auf dem FTP Server.

So ungefähr in dieser Art habe ich schon unzählige Dienste erstellt. Auch dieser Dienst, der in der Entwicklungsumgebung einwandfrei funktioniert, macht nach der Installation auf dem W2K3 Server des Kunden Probleme.

Ich starte den Dienst, es dauert ein paar Sekunden und es kommt die Meldung, dass der Dienst nicht gestartet werden konnte.

image

Also schaue ich im Ereignisprotokoll nach und stoße auf eben diese Meldung :

image Ich habe eine ähnliche Fehlermeldung schon öfter gehabt, und die hat eigentlich immer auf falschen Einstellungen in der ´Konfigurationsdatei hingedeutet.

Aber warm muss ich eigentlich im trüben Wasser fischen, wieso gibt mir das System keine ordentliche Fehlermeldung geben. Da kan doch nur Bill Gates (Ich meine Microsoft) dran schuld sein, haben wohl wieder mal vergessen ordentliche Fehlermeldungen einzubauen.
Andere werden doch das gleiche Problem haben, was machen die denn gegen eine solche Abstrakte Fehlermeldung, was fängt man damit an.

Also einfach mal nach EventType clr20r3 gegoogelt (oh man was für ein Wort smile_speedy). Die Suchergebnisse sind weniger als zufriedenstellend. Das einzige was sicher zu sein scheint, der Fehler tritt wohl immer mit Windows Diensten auf, nicht auf allen Maschinen (was sich später als Logisch herausstellt) und er tritt auf Systemen mit dem NET Framework 2 auf.

Das alles ist und war mir einfach zu wenig und da ich bereits einige male über einen Fehler dieser Art gestolpert war, habe ich mir dieses mal die Zeit genommen, dass ganze mal etwas mehr Global zu betrachten und nicht nur den speziellen Fehler in diesem Fall im trüben Wasser zu suchen (und hoffentlich auch zu finden).

Ich möchte auch jetzt nicht den ganzen Weg beschreiben wie ich hier vorgegangen bin und was ich alles getestet habe, da man ja einen Service der im Start nicht funktioniert nicht einfach so Debuggen kann.

Die Essenz meiner Untersuchungen haben folgendes Ergeben:

Diese Blöde Fehlermeldung wird vom NET Framework ausgegeben wenn keine ordentlich Exception innerhalb des beim Start ausgeführten Codes enthalten ist. Dabei kann der Fehler eine Fehlende Datei, die falsche oder fehlende Konfiguration, ein Problem mit einem verwendet Port oder wie es in meinem Fall war, einfach das nicht Vorhandensein einer Tabelle in der DB sein.

Ja ich hatte einfach vergessen, nach der Installation des Dienstes auch das SQL Skript zur Erstellung des Tabellen und Gespeicherten Prozeduren auszuführen.

Und was macht man, damit man nicht mehr diese nichtssagende Fehlermeldung bekommt, sondern etwas mit dem man dann auch etwas anfangen kann:

Lösung:

Man muss in der ersten, was auch immer, die durch die OnStart() Methode aufgerufen wird unbedingt darauf achten dass man dort eine try - catch Anweisung einbaut, die dann eine ordentlich Fehlermeldung erzeugt und am besten auch in das Ereignisprotokoll schreibt.

Programmierung | Tips und Tricks | Windows Dienste
Wednesday, October 24, 2007 1:13:23 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [3]  
Autor: Hans-Peter Schelian  |  Trackback
# Friday, October 05, 2007

Immer wieder taucht die Frage auf, wie man auf einfache Art und Weise eine Datensicherung einer SQL Datenbank anlegen kann.

Auch wenn Heute viele gar nicht mehr genau wissen wozu man die Eingabeaufforderung, Batch Dateien und Kommandozeilen (das sind Programme die keine GUI haben) Programme verwendet ist die Zeit dieser Tools noch lange nicht zu Ende, wie auch dieser Beitrag wieder einmal beweist.

Und eines dieser Kommandozeilen Programme, das osql tool des SQL Server, ist auch die Basis dieses Artikel.

Das osql tool des Microsoft SQL Server (sowohl im SQL Server 2000, der MSDE und auch in allen SQL 2005 Versionen enthalten) bietet die Möglichkeit Transact SQL Befehle (T-SQL) direkt von der Kommandozeile auszuführen.

Nutzen wir doch diese Möglichkeit und erstellen wir uns eine Batch Datei (Datei die auf Kommandozeilen Ebene ausgeführt werden kann), mit der wir eine Datenbank eines SQL Server's sichern können.

Ich Denke nicht dass man jede Zeile der Batch Datei erklären muss, deshalb hier nun den Inhalt der Batch Datei:

@ECHO OFF
rem Für die Verwendung mit SQL Server 2000 oder der MSDE in der nachfolgenden Zeile  das rem am Anfang entfernen
rem set SQL_UTIL_HOME="%ProgramFiles%\Microsoft SQL Server\80\Tools\Binn\osql"
rem Für die Verwendung mit SQL Server 2005 oder SQL Server 2005 Express in der nachfolgenden Zeile  das rem am Anfang entfernen
set SQL_UTIL_HOME="%ProgramFiles%\Microsoft SQL Server\90\Tools\Binn\osql"
rem Tragen Sie in der nachfolgenden Zeile den Namen des SQL Server ein z.B. (localhost)
set SQL_SERVERNAME=HP-DN01\SQL2005
rem Tragen Sie in der nachfolgenden Zeile den Namen der zu sichernden Datenbank ein
set SQL_DBNAME=FASC
rem Tragen sie den Namen inkl. Pfad der Backup Datei ein
set SQL_BACKUP_FILE='C:\temp\fasc.bak'
@ECHO ON
%SQL_UTIL_HOME% -E -S%SQL_SERVERNAME% -n -Q "BACKUP DATABASE %SQL_DBNAME% TO DISK = %SQL_BACKUP_FILE% WITH INIT"

Hier kann man die Datei herunterladen Backup.Bat.

Einfach herunterladen, das ZIP File entpacken und dann die Backu.BAT Datei öffnen und die darin gesetzten Environment Variablen auf die eigenen Bedürfnisse anpassen.

 

Code | Tips und Tricks | Tools
Friday, October 05, 2007 10:40:01 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Monday, September 24, 2007

Ein Fehlermeldung wie die nachfolgende:

Fehler 1 Ungültige Resx-Datei. Der angeforderte Wert Ctrl konnte nicht gefunden werden. Zeile XXX Position Y.

Deutet darauf hin, dass man versucht mit der Deutschen Version von Visual Studio 2005 ein Projekt, welches mit einer englischen Version der Entwicklungsumgebung erstellt wurde, zu übersetzen versucht.

Der Fehler tritt auf, wenn in den Resource Dateien von Windows Forms, Tastenkürzel (Shortcuts) zur Schnellanwahl von Menüfunktionen vergeben sind.

In den Resource Dateien steht dann so etwas wie:

<data name="editDeleteToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>Ctrl+D</value>
</data> 

Das Ziel dieses Eintrags ist es mit der Tastenkombination <Steuerung>+D das Edit Menü aufzurufen.

Im englischen wäre der Eintrag auch korrekt.

Im Deutschen jedoch muss jedoch für Ctrl, was für Control - dem englischen Ausdruck für Steuerung steht, die Deutsche Abkürzung Strg verwendet werden.

Der Eintrag muss also für die Verwendung in einer Deutschen Visual Studio Version wie folgt aussehen:

<data name="editDeleteToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>Strg+D</value>
</data> 
Programmierung | Tips und Tricks
Monday, September 24, 2007 5:11:06 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, August 21, 2007

Immer wieder geschieht es dass ich während der Programmierung über den zu verwendenden Zugriffsmodifizierer für Klassen und Methoden nachdenken muss (manchmal länger als ich eigentlich möchte ). Aus diesem Grund mach ich mir hier wieder mal eine kleine Gedächtnisstütze in Form dieses Blog Eintrags.

Hier nun eine kleine Tabelle in welcher man einfach den benötigten Zugriffsmodifizierer ablesen kann:

Zugriffsmodifizierer Zugriff von beliebiger Stelle Zugriff aus Assembly

Zugriff aus abgeleiteterKlasse in eigener Assembly

Zugriff aus abgeleiteter Klasse in anderer Assembly Zugriff aus eigener Klasse
public Ja Ja Ja Ja Ja
protected internal - Ja Ja Ja Ja
internal - Ja Ja - Ja
protected - - Ja Ja Ja
private - - - - Ja

Programmierung | Tips und Tricks | Sprachen | C#
Tuesday, August 21, 2007 10:18:49 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Wednesday, August 15, 2007

Auch dieser Blog Eintrag ist wieder so etwas wie ein Post It für mich selbst, Ich denke aber das bestimmt der eine oder andere genau das Problem hat und schon mit diesem kleinen Hinweis weiter kommt.

Hintergrund:

Bei der Verwendung von CodeSmith und den netTiers 2.0 Templates ist es möglich mit Transaktionen zu arbeiten. In diesem Blog Eintrag wird kurz beschrieben wie man diese unter C# und im Domain Komponenten Modell einsetzen kann.

Lösung (c#):

if (ConnectionScope.Current.TransactionManager == null)
     ConnectionScope.Current.TransactionManager = ConnectionScope.CreateTransaction();
 TransactionManager tm = ConnectionScope.Current.TransactionManager;
 if (!tm.IsOpen)
 {
     tm.BeginTransaction();
 }
  // Hier nun alle notwendigen Datenbankoperationen

 if (allesOK)
    tm.Commit();
 else
     tm.Rollback();

Mehr ist nicht zu berücksichtigen !!

Programmierung | C# | Tips und Tricks
Wednesday, August 15, 2007 10:17:50 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback

Jetzt reicht es und ich mache mir selbst ein Geschenk indem ich nun endlich dieses Problem einmal in einem Blog Eintrag festhalte.

Hintergrund:

Immer wieder stehe ich vor der Aufgabe das ich von irgendwoher Daten aus einer Datei verarbeiten (meistens Importfunktionen) muss, die unter anderem auch Datums und oder Zeitwerte enthalten.

Die Werte werden in den verschiedensten Formaten übergeben, eine der beliebtesten Formate ist es aber dass beideInformationen (Datum und Uhrzeit) in zwei verschiedenen Felder übergeben werden. Dabei wird häufig (jedenfalls ist das bei mir so) das nachfolgende Format für die Speicherung der Werte verwendet:

Datum als String z.B. : 20070815 also im Format yyyyMMdd

Uhrzeit als String z.B. : 120310 also im Forma HHmmss

Nun besteht die Aufgabe darin diese Daten zu lesen und dann in ein DateTime Objekt zu konvertieren.

Lösung:

Nachfolgend nun die von mir favorisierte und schon dutzende male verwendet Lösung:

// Source ist in C#

 string dateStr = s[2]; // Hier steht einfach ein Datumswert wie 20070815 drin
 string timeStr = s[3]; // Hier steht eine Uhrzeit wie 120310 drin 
 string dateTimeStr = dateStr + " " + timeStr; //Da steht nun  20070815 120310 also Datum mit Uhrzeit drin
 string dateTimeStrFormat = "yyyyMMdd HHmmss"; // Genau hier das ist das Format

 // Und hier wird nun der zusammen gebastelte Datum  / Uhrzeit String in ein DateTime Objekt konvertiert
 objInfo.TransDate = DateTime.ParseExact(dateTimeStr, dateTimeStrFormat, DateTimeFormatInfo.InvariantInfo);

So und nun hoffe ich das ich beim nächsten mal nicht wieder nach diesem Stückchen Quellcode suche wenn ich mal wieder vor der Aufgabe stehe, und eventuell stolpert ja auch der eine oder andere über diese Information wenn er / sie mal so etwas machen möchte.

Programmierung | Code | Tips und Tricks | C#
Wednesday, August 15, 2007 10:16:35 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, July 10, 2007

Beim durchstöbern des Web bin ich auf den Beitrag "Install a Windows Service in a smart way instead of using Windows Installer MSI package" gestoßen. Dieser Beitrag beschreibt einen alternativen Weg wie man Windows Dienste ohne einen eigens dafür erstellte Setup Routine installieren kann.

Der Artikel ist in Englisch und wurde auf http://www.codeproject.com/ veröffentlicht.

Programmierung | Tips und Tricks | Windows Dienste
Tuesday, July 10, 2007 10:03:34 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Tuesday, December 05, 2006

Hier ein kurzer Source Code Ausschnitt zur Cursor Behandlung in MDI Forms.

 // Cursor var definieren um Cursor zu sichern
 Cursor oldCursor;
            
// aktuellen Cursor Zustand speichern
oldCursor = this.MdiParent.Cursor;

// Neuen Cursor setzen  
this.MdiParent.Cursor = Cursors.WaitCursor;

// Hier Aktionen ausführen 
------

// Cursor wieder zurück setzen
this.MdiParent.Cursor = oldCursor;
Tips und Tricks | C#
Tuesday, December 05, 2006 12:43:21 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Thursday, April 27, 2006

Nachdem ich selbst das Problem am Anfang hatte und auch immer wieder von Freunden, Bekannten und Kollegen auf das Thema Debuggen von Web Services unter Visual Studion 2005 angesprochen werde, hier nun ein kleiner Blog Eintrag der beschreibt wie man dabei am besten vorgeht.

Hier zuerst noch einmal das Szenario:

Ich möchte einen Web Service erstellen und während der Entwicklung auf einfachem Wege in der Lage sein die einzelnen Web Methoden debuggen zu können.

Also einfach ein zweites Projekt (Windows Form) in der Projektmappe erstellt, referenz auf den Web Service in der Projektmappe hinzufügen, als Startprojekt festlegen, und fertig.

Anmerkung zum Szenario:

So sollte es gehen, leider geht es aber "noch" nicht"

Und hier nun die Lösung:

Zuerst einmal genau so vorgehen wie es im Szenario beschrieben ist und dann folgende Einstellungen zusätzlich vornehmen:

In den Projektmappen Eigenschaften in den Allgemeinen Eigenschaften Startprojekt auswählen.

Dort die Option mehrere Startobjekte auswählen und bei den beiden Projekten (der Web Service und das Windows Forms Projekt) die Aktion auf Starten stellen.

Das ganze noch kurz mit OK bestätigen und fertig.

Wenn ich nun in der Windows Form eine Web Methode verwende, kann ich innerhalb der web methode einfach mit F9 einen Haltepunkt (Breakpoint) setzen.

Hoffe das die (schnelle und kurze) Beschreibung vielen Helfen wird.

Programmierung | Tips und Tricks | Web Services
Thursday, April 27, 2006 9:20:23 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Wednesday, May 04, 2005

Response.Write("<")
Response.Write("script language=javascript>window.open('" & Request.Url.ToString() & "');")
Response.Write("</script")
Response.Write(">")

Tips und Tricks | JavaScript
Wednesday, May 04, 2005 6:26:35 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback
# Saturday, January 15, 2005

Um in einer App.config Datei Enum Werte wieder in eine Variable des Augzählungstypes einzulesen kann folgende Funktion verwendet werden.

Enum.Parse()

Beispiel:

In diesem Beispiel lesen wir den NotifyFilter eines FileSystemWatcher ein:

Der Key in der App.config sieht wie folgt aus:

<add key="fdwNotiFyFilter" value="FileName, DirectoryName, LastWrite" />

Und hier die Vewendung:

fdw.NotifyFilter = (NotifyFilters)Enum.Parse(typeof(NotifyFilters), ConfigurationSettings.AppSettings["NotifyFilter"]);
Programmierung | Tips und Tricks | C#
Saturday, January 15, 2005 6:17:52 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]  
Autor: Hans-Peter Schelian  |  Trackback

Durch den Einsatz des nachfolgenden Skripts im Agent des Exchange Server kann man die Attachment's einer Email automatisch in einem lokalen Verzeichnis speichern.

Um ein überschreiben der Dateien zu vermeiden wird ein temporärer Dateiname gebildet.

 

Um das Skript ausführen zu können muss in jedem Fall ein Exchange Server betrieben werden.

Hier nun das Skript:

<SCRIPT language=VBScript RunAt="Server">

'------------------------------------------------------------------------------
'FILE DESCRIPTION: Exchange Server Event Script
'------------------------------------------------------------------------------

OptionExplicit

'------------------------------------------------------------------------------
' Global Variables
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
' Event Handlers
'------------------------------------------------------------------------------

' DESCRIPTION: This event is fired when a new message is added to the folder
PublicSub Folder_OnMessageCreated
Dim objSession
Dim objItem
Dim objAttachment
dim i
dim atName

Set objSession = EventDetails.Session

Set objItem = objSession.GetMessage(EventDetails.MessageID, Null)

If objItem.Type ="IPM.Note"or objItem.Type ="IPM.Post"Then
If objItem.Attachments.Count > 0 then
for i = 1 to objItem.Attachments.Count
Set objAttachment = objItem.Attachments.Item(i)
atName ="C:\Attachment\Inbox\" & GetTempName("C:\Attachment\Inbox\")
ifNot FileExist(atName) then
objAttachment.WriteToFile(atName)
else
objAttachment.WriteToFile(atName & "neu")
EndIf
next
endif
EndIf

Set objAttachment =nothing
Set objSession =Nothing
Set objItem =Nothing

EndSub



--------------------------------------------------------------------------------


' DESCRIPTION: This event is fired when a message in the folder is changed
PublicSub Message_OnChange
EndSub



--------------------------------------------------------------------------------


' DESCRIPTION: This event is fired when a message is deleted from the folder
PublicSub Folder_OnMessageDeleted
EndSub



--------------------------------------------------------------------------------


' DESCRIPTION: This event is fired when the timer on the folder expires
PublicSub Folder_OnTimer
EndSub



--------------------------------------------------------------------------------


Function FileExist(filespec)
Dim fso, msg
Dim bRet
Set fso =CreateObject("Scripting.FileSystemObject")
If (fso.FileExists(filespec)) Then
bRet =true
Else
bRet =false
EndIf
FileExist = bRet
EndFunction



--------------------------------------------------------------------------------


Function GetTempName(path)
Dim fso, fsp
Dim bRet
Set fso =CreateObject("Scripting.FileSystemObject")
fsp = fso.GetTempName
GetTempName = fsp
EndFunction



</SCRIPT> 
Tips und Tricks | Exchange Server | vbScript
Saturday, January 15, 2005 4:25:37 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [2]  
Autor: Hans-Peter Schelian  |  Trackback
Copyright © 2010 Hans-Peter Schelian - Schelian IT Beratung. All rights reserved.