Category Archives: Windows Forms (C#)

C#: Dictionary inlinie initialisieren

Es ist extrem praktisch und anschaulich, wenn man  in einer Programmiersprachen ein Array in einer Zeile erzeugen und befüllen kann:

string[] Names = { "Vincent", "Johannes", "Anna" };

Bisher wusste ich nicht, dass es bei einem Dictionary genauso einfach geht. Bei einem einfachen Telefonbuch sieht es dann so aus:

public static readonly Dictionary<string, string> 
    Lieferoffsets = new Dictionary<string, string>()
{
  {"Peter Müller","01234/1234"},
  {"Franz Maier","05678/5678"},
  {"Hans Glück","09876/9876"}
};

Manchmal erfreuen einen auch die kleinen Dinge an einer Programmiersprache …

Cheers,
Ralf

Windows Forms: Textbox-Text selektieren, wenn Control den Fokus erhält

Man kann nicht im Handler eines Enter-Events mittels Textbox.SelectAll(); alles markieren. Wenn man es asynchron durchführt, geht es aber:

BeginInvoke((MethodInvoker) delegate() {txtOrder.SelectAll();});

Cheers,
Ralf

Windows Forms: Funktionstasten erkennen

Will man z.B. das Drücken der  F10-Taste erkennen, muss man in der Form-Klasse die Funktion ProcessCmdKey überschreiben:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == Keys.F10)
    {
        MessageBox.Show("F10 Pressed");
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

Cheers,
Ralf

Windows Forms: Immer wieder Ärger mit ConstraintExceptions

Immer wieder, wenn man die Datenbank angepasst hat, kommt es vor, dass die autogenerierten DataSet-Funktionen beim Füllen eine ConstraintExpection feuern.

Auch wenn man die Innerste der inneren Exceptions untersucht, bekommt man die eigentlich Ursache nicht heraus.

Jetzt bin ich endlich auf den Tipp gestoßen:
http://dotnetdebug.net/2006/07/16/constraintexception-a-helpful-tip/ 

Der Tipp stimmt allerdings nur, wenn es bereits in der ersten Zeile „knallt“. Ist der Fehler nicht in der ersten Zeile, muss man erst die Zeilennummer herausbekommen. Dazu einfach im „Watch“-Fenster folgenden Code hinzufügen:

DataSet1.Tables["Tabelle1"].Rows[
  DataSet1.Tables["Tabelle1"].Rows.Count-1].RowError

DataSet1 und Tabelle1 müssen natürlich angepasst werden.

Cheers,
Ralf

 

Windows Forms: Verweis auf unterschiedliche „DEBUG“ und „RELEASE“ Bibliotheksversionen

Wenn man für DEBUG und RELEASE Konfigurationen auf unterschiedliche Bibliotheksversionen verweisen will, dann muss man die Projektdatei manuell editieren:

<Reference Include="Foo" Condition="'$(Configuration)'=='Debug'"/>
<Reference Include="Bar" Condition="'$(Configuration)'=='Release'"/>

Diese Info stammt aus dem Stackoverflow-Forum:
http://stackoverflow.com/questions/694415/c-sharp-vs2008-add-separate-debug-release-references-to-a-project 

Wenn es um die gleiche Bibliothek in unterschiedlichen Ordnern geht, dann würde mir das aber so besser gefallen :

<Reference Include="MyLib">
  <HintPath>..\..\MyLib\bin\$(Configuration)\MyLib.dll</HintPath>
</Reference>

Cheers,
Ralf

Windows Forms: Mehrzeilige Felder mit DataGridView

In einem DataGridView kann man auch Zeilenumbrüche darstellen, wenn man den WrapMode von DefaultCellStyle von DataGridViewTextBoxColumn auf true setzt.

Cheers,
Ralf

Windows Forms: NumericTextBox

Ab Werk bei Windows Forms gibt es leider keine numerische Textbox. Ich habe mich dabei ertappt, dass ich das in jedem Projekt neuprogrammiert habe. Heute soll damit Schluss sein: Nach einem kurzen Besuch bei der Suchmaschine meiner Wahl fand ich ein Snippet, das ich gleich etwas anpasste (und auch noch weiter anpassen muss):

public class NumericTextBox : TextBox
{
    bool allowSpace = false;
    private bool isInt;

    // Restricts the entry of characters to digits 
    // (including hex), the negative sign,
    // the decimal point, and editing keystrokes (backspace).
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);

        NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
        string decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
        //string groupSeparator = numberFormatInfo.NumberGroupSeparator;
        string negativeSign = numberFormatInfo.NegativeSign;

        string keyInput = e.KeyChar.ToString();

        if (Char.IsDigit(e.KeyChar))
        {
            // Digits are OK
        }
        else if (keyInput.Equals(decimalSeparator) || // keyInput.Equals(groupSeparator) ||
            keyInput.Equals(negativeSign))
        {
            // Decimal separator is OK
        }
        else if (e.KeyChar == '\b')
        {
            // Backspace key is OK
        }
        //    else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0)
        //    {
        //     // Let the edit control handle control and alt key combinations
        //    }
        else if (this.allowSpace && e.KeyChar == ' ')
        {

        }
        else
        {
            // Swallow this invalid key and beep
            e.Handled = true;
            //    MessageBeep();
        }
    }

    public int IntValue
    {
        get
        {
            return Int32.Parse(this.Text);
        }
    }

    public double DoubleValue
    {
        get
        {
            return double.Parse(this.Text);
        }
    }
    protected override void OnValidating(CancelEventArgs e)
    {
        base.OnValidating(e);
        try
        {
            if (!isInt)
                this.Text = Math.Round(double.Parse(this.Text), 2).ToString("F2");
            else
                this.Text = Math.Round(double.Parse(this.Text), 0).ToString();
        }
        catch
        {
            e.Cancel = true;
        }
    }
    public bool AllowSpace
    {
        set
        {
            this.allowSpace = value;
        }

        get
        {
            return this.allowSpace;
        }
    }
    public bool IsInt
    {
        set
        {
            this.isInt = value;
        }

        get
        {
            return this.isInt;
        }
    }
}

Vorhandene Textboxen in den „Designer.cs“-Files suchen und ersetzen. Fertig!

Der Vorschlag kommt übrigens von MS direkt. Man hätte es aber auch direkt in VS integrieren können …
http://msdn.microsoft.com/en-us/library/ms229644(v=vs.80).aspx

Cheers,
Ralf

Report Viewer: externe Bilder darstellen

Will man externe Bilder darstellen, könnte es sein, dass man über die Syntax der Pfadangabe stolpert. Ich hatte direkt den Pfad zum Bild eingetragen und war etwas verwundert, dass MS anhand der Backslashes in der Pfadangaben nicht von selbst auf den Dateizugriff kam. Man muss es also klar voranstellen, was man will. Report Viewer unterstützt Bilder über Http- und File-Zugriff:

  • Http:
    http://www.Beispiel.de/MeinBild.png
  • File:
    file:\\\D:\Ordner\MeinBild.png

Aus Sicherheitsgründen muss man zusätzlich den externen Zugriff bei Bildern auch erlauben:

reportViewer1.EnableExternalImages = true;

Cheers,
Ralf

Windows Forms: Grid-Container mit Hintergrundfarbe

Jeder der schon mit WPF (oder Silverlight) gearbeitet hat, empfindet es sicher wie ich als Qual wenn mit der älteren Technologie „Windows Forms“ arbeitet. In WPF gibt es eine Vielzahl von Containern, die einem eine Menge Arbeit abnehmen. Jeder der in WPF Stunden zubringt um einzelne Elemente neu zu verschieben, nur weil sich die Seitenbreite geändert hat, sollte sein Tun dringend überdenken.

Immer wenn ich also „Back to the roots“ gehe und ein Windows Forms-Projekt bearbeite, dann vermisse ich bespielsweise das Grid-Element, das sehr viele Elemente prozentual über den kompletten Bereich verteilen kann. Es gibt unter Windows Forms allerdings ein Element namens TableLayoutPanel, das ein bisschen Ähnlichkeit hat … allerdings mit der Eleganz einer Bahnschranke :-)

Hier ein paar Einschränkungen:
– Das Ausrichten übereinander ist nicht möglich.
– Es kann nur ein Element pro Zelle eingefügt werden
– Copy & Paste: Das neue Element wird in die letzten Zeile erstes Feld eingefügt
– Die Performance bei vielen UI-Elementen ist leider nicht besonders
– Bei einem Resize der Komponente könnte es flackern
– Zeilen und Spalten lassen sich nicht in der Design-Ansicht einfärben

Will man z.B. eine unterschiedliche Hintergrundfarbe für die erste und dritte Spalte haben, dann muss man das CellPaint-Ereignis abfangen und folgenden Handler programmieren:

void tableLayoutPanel1_CellPaint(object sender, 
        TableLayoutCellPaintEventArgs e)
{
    if (e.Row == 0 || e.Row == 2) {
        Graphics g = e.Graphics;
        Rectangle r = e.CellBounds;
        g.FillRectangle(Brushes.Blue, r);
    }
}

Besser als nichts. Ich setzte es gerne ein, wenn man schnell einen Prototypen basteln will.

Cheers,
Ralf

 

Report Viewer: Berichte mit Master-Detail-Ansicht

Grundsätzlich ist ja der Aufbau der Reports so, dass alles von oben nach unten abgearbeitet wird. Wenn wir beispielsweise zwei Tabellen (Tablix-Elemente) mit Schülern und Noten untereinander setzen, dann bekommen wir zuerst alle Schüler und danach die Notenliste.

In einem Bericht möchte man aber häufig eine kombinierte Ansicht. Man möchte nacheinander alle Noten pro Schüler … und das am besten seitenweise getrennt.

Wenn man das mit RDLC-Files machen möchte dann bereitet man die Daten am besten in einer flachen Tabelle vor. Jede Zeile hat also neben der Schülerinformation (z.B. Name, Geburtsdatum, …) auch die Informationen zu der einzelnen Note (z.B. Fach, Note).

In der Design-Ansicht des Berichts, muss man dann die Notentabelle in ein List-Elemente packen. Bei diesem List-Element stellt man unter „Row Properties“ eine Gruppierung über die Schüler ein und den Seitewechsel nach jedem Schüler.

Die Tabelle innerhalb bekommt dann automatisch alle Zeilen dieses Schülers. Wenn man in der Tabelle eine „Column Group“ mit Gruppierung über das Fach definiert, dann sieht man für jede Note des jeweiligen Fachs eine Spalte. Bilder dazu folgen, wenn ich mehr Zeit habe …

Streng genommen ist ein List-Element (mittlerweile) ein spezielles Tablix-Element. Beispiel zu beiden Varianten findet man hier:

http://www.gotreportviewer.com/masterdetail/index.html

Cheers,
Ralf