C# – DataGridView und Zwischenablage

Problemstellung

Die Daten eines DataGridView sollen über die Zwischenablage in verschiedene andere Anwendungen (unter anderem auch in Excel) kopiert und eingefügt werden können.

Hierbei soll es wahlweise Möglich sein, beim kopieren in die Zwischenablage, die Spaltenköpfe des DataGridView mit in die Zwischenablage zu kopieren. Außerdem soll es neben der Tastenkombination Strg + C auch über Programmcode (Button und oder Contextmenü) möglich sein, die Daten in die Zwischenablage zu kopieren.

Umsetzung – Teil 1 Spaltenüberschriften einschließen

Ob die Spaltenköpfe beim kopieren der DataGridView Daten mit einbezogen werden kann über die Eigenschaft ClipboardCopyMode des DataGridView gesteuert werden.

Die Eigenschaft kann auf folgende Werte gesetzt werden:

image

Der Standardwert lautet EnableWithoutHeaderText (Kopieren der markierten Daten ohne Spaltenköpfe).

Um sicherzustellen, dass auch die Spaltenköpfe mit in die Zwischenablage kopiert werden, muss der Wert EnableAlwaysIncludeHeaderText gesetzt werden.

DataGridView1.ClipboardCopyMode = EnableAlwaysIncludeHeaderText ;

Umsetzung – Teil 2 Programmatisches kopieren der Zwischenablage

Um per Programmcode die Daten (mit oder ohne Spaltenköpfe) in die Zwischenablage zu kann der folgende Code verwendet werden:

Clipboard.SetDataObject(DataGridView1.GetClipboardContent(), true);

C# – Gebundenes DataGridView – Die Suche geht weiter

Die Anforderung:

In einem gebundenen DataGridView, soll eine Suchfunktion implementiert werden, die es ermöglicht, dass man je nach angewählter Spalte innerhalb der Spalte nach einem in ein Suchfeld einzugebenden Text gesucht werden kann. Allerdings soll nicht nur auf genaue Übereinstimmung, sondern auch nur auf Teil -Übereinstimmung der dem Suchbegriff entsprechende erste Eintrag gefunden werden.

Die Problematik:

Grundsätzlich wird für die Umsetzung der Anforderung von der BindingSource eine Methode Find zur Verfügung gestellt.

Beispiel:

bool found = false;

int i = customerBindingSource.Find(dgCustomer.SortedColumn.DataPropertyName, txtSearch.Text);
if (i > -1)
{
    customerBindingSource.Position = i;
}
else
{
}

Soweit funktioniert das Prima, wenn der Suchtext genau mit dem Inhalt der Zelle entspricht.

Was aber wenn zum Beispiel in einer Spalte die Werte für den Name der erste Eintrag der mir M beginnt gefunden werden soll?

Dann funktioniert die Find Methode nicht, und leider bietet weder die BindingSource noch das DataGridView hierzu eine entsprechende Möglichkeit dies einfach per Methodenaufruf zu realisieren.

Die Eine Lösung:

Schauen wir uns doch mal an, was wir mit dem oben noch leeren Else Zweig anfangen können.

Das wir über die BindingSource nicht weiter kommen haben wir schon festgestellt, also müssen wir uns selbst etwas basteln.

Beispiel:

string searchField = dgCustomer.SortedColumn == null ? 
//Hier einfach den DataPropertyName des gewünschten Standardsuchfeldes wenn keine Spalte markiert ist
dgCustomer.SortedColumn.DataPropertyName;
string searchCellName = dgCustomer.SortedColumn == null ? 
//Hier einfach den Namen des gewünschten Standardsuchfeldes wenn keine Spalte markiert ist
dgCustomer.SortedColumn.Name;
bool found = false;

int i = customerBindingSource.Find(searchField, txtSearch.Text);
if (i > -1)
{
	customerBindingSource.Position = i;
}
else            
{
	foreach (DataGridViewRow row in dgCustomer.Rows)
	{
		if (row.Cells[searchCellName].Value == null)
		{
			continue;
		}

		if (row.Cells[searchCellName].Value.ToString().ToLower().StartsWith(txtSearch.Text.ToLower()))
		{
			i = customerBindingSource.Find(searchField, row.Cells[searchCellName].Value.ToString());
			if (i > -1)
			{
				customerBindingSource.Position = i;
				found = true;
				break;
			}
		}
	}
	if (!found )
	{
		string msg = string.Format(CultureInfo.CurrentUICulture, "{0} {1} konnte nicht gefunden werden", searchField, txtSearch.Text);
		MessageBox.Show(msg);                                                
	}
}

Ich Denke der Code ist selbsterklärend und bedarf keiner weiteren Erklärung.

Wenn ich mich damit täuschen sollte, dann einfach per Kommentar die Fragen stellen, oder noch besser gleich die Antworten geben :-).

Windows Communication Foundation (WCF) – Welche Bindungen gibt es denn

Beim stöbern in den Tiefen der MSDN Dokumentationen bin ich mehr durch Zufall, als durch das Ergebnis einer Suchmaschine auf den Beitrag „Konfigurieren der vom System bereitgestellten Bindungen“ gestoßen.

Da ich bisher nichts (auf jeden Fall nichts auf Deutsch) finden konnte, wo die verschiedenen Bindungen die vom Framework zur Verfügung gestellt werden, kurz und prägnant aufgeführt und erläutert wurden, war ich gerade dabei und wollte das in einem eigenen Beitrag machen und da…

Ja da habe ich diesen Beitrag gefunden.

Hier nun kurz (sozusagen) als Suchmaschinen Hilfe für die MSDN Seite die verfügbaren Bindings kurz aufgelistet und dann der Link zu der Erläuterung auf MSDN.

Vom .NET Framework zur Verfügung gestellte Bindings für die Windows Communication Foundation

  • BasicHttpBinding
  • WSHttpBinding
  • WS2007HttpBinding
  • WSDualHttpBinding
  • WSFederationHttpBinding
  • WS2007FederationHttpBinding
  • NetTcpBinding
  • NetNamedPipeBinding
  • NetMsmqBinding
  • NetPeerTcpBinding
  • MsmqIntegrationBinding

Die MSDN Beschreibung lautet : Konfigurieren der vom System bereitgestellten Bindungen

DataGridView – Beim Drag and Drop Zeilen oder Spaltenkopf erkennen – C#

Wer sich mit Drag und Drop innerhalb des DataGridView beschäftigt wird unter Umständen ermitteln wollen, ob die Maus auf einem Spalten oder Zeilenkopf gedrückt und festgehalten wurde. Zu diesem Zweck verwende ich die nachfolgende Methode „IsCellOrRowHeader“.

Hier die C# Methode IsCellOrRowHeader:

private bool IsCellOrRowHeader(int x, int y)
{
    DataGridViewHitTestType dgt = dgTodo.HitTest(x, y).Type;
    return (dgt == DataGridViewHitTestType.Cell ||
                    dgt == DataGridViewHitTestType.RowHeader);
}

Ich glaube ich hatte die Methode als Grundgerüst mal in Internet gefunden, heute wo ich diesen Beitrag verfasse, habe ich die Suchmaschine angeworfen um einen Link auf den ursprünglichen Autor bzw, den Beitrag zu finden. Ich konnte aber die Seite nicht mehr finden, wenn also jemand dieses Beitrag liest und weiß wo der Ursprung dieser Methode herkommt, dann hinterlasst doch bitte einen Kommentar.

Eine VB Variante dieser Funktion habe ich gerade doch noch gefunden.

Hier der Link: IsCellOrRowHeader in VB

Erweiterungsmethoden für Basis aller Objekte implementieren – C#

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");
            }
        }

Erweiterungsmethoden am Beispiel einer isNumeric Methode für die String Klasse – C#

Erweiterungsmethoden sind statische Methoden die in statischen Klassen definiert werden und als ersten Parameter das Schlüsselwort this mit nachfolgendem Type der zu erweiternden Klasse an die Methode übergibt. Diese Erweiterungsmethoden werden dann als Instanz Methode (wie z.B. die ToString() Methode) des jeweiligen Objekts verwendet.

Vor einiger Zeit hatte ich einen Beitrag geschrieben der sich mit der C# Implementierung der unter VB verfügbaren Funktion isNumeric beschäftigte.

In diesem Beitrag hatte ich eine public static Methode in einer Helper Klasse geschrieben, ich dachte es wäre anschaulich wenn wir nun diese Implementierung als Erweiterungsmethode der String Klasse vornehmen.

Hier zuerst noch einmal die Klassische Implementierung vor C# 3.0

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

}

Diese konnte man wie folgt verwenden:

string o = "100";
if (MathHelper.IsNumeric(o))
    Console.WriteLine("Ja die Eingabe ist Numerisch");
else
    Console.WriteLine("Keine numerischen Eingabe");

So und nun zur Implementierung der isNumeric Funktionalität als Erweiterungsmethode der String Klasse:

Nun kann man mit dem C# 3.0 Feature der Erweiterungsmethoden (Extension Methods) diese aber auf viel elegantere weise lösen. Ich möchte dies hier an einem Beispiel der String Klasse demonstrieren. Wir erweitern also unsere String Klasse durch eine Erweiterungsmethode isNumeric.

Hierzu müssen wir zuerst eine Statische Klasse erstellen, in welcher wir dann die Erweiterungsmethode(n) deklarieren können.

public static class StringExtensions
{
    public static bool isNumeric(this string s)
    {
        double retNum;
        bool isNum = Double.TryParse(s, System.Globalization.NumberStyles.Any,
                                     System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
        return isNum;

    }
}

Verwendet wird das dann so:

string o = "100";
if (o.isNumeric())
    Console.WriteLine("Ja die Eingabe ist Numerisch");
else
    Console.WriteLine("Keine numerischen Eingabe");

ip2long – long2ip – C# Ersatz für PHP Funktionen

In einem Projekt musste ich mit Daten arbeiten welche durch eine PHP Anwendung erstellt wurden (werden).

Unter anderem wird dabei die IP Adresse des Besuchers in einer Datenbank gespeichert.

Bei der Speicherung der IP wird diese aber nicht als String sondern als long Wert gespeichert.

PHP bietet hierzu folgende Funktionen an:

  • ip2long
  • long2ip

Obwohl, oder gerade weil ich kein PHP Guru bin, stellt sich mir nun die Aufgabe diese Long Werte wieder in IP Adressen zurück zu übersetzen.

Und eben nicht mit PHP sondern mit C#.

Hierzu habe ich mir 2 statische Methoden in einer IP-Helper Klasse erstellt die genau diese Funktionalität der PHP Funktionen in C# nachempfindet.

Nachfolgend meine beiden C# Methoden:

public static long ip2long(string ipAddress)
{
    System.Net.IPAddress ip;
    
    if (System.Net.IPAddress.TryParse(ipAddress,out ip))
    {                
            return (((long) ip.GetAddressBytes()[0] << 24) | (ip.GetAddressBytes()[1] << 16) |
                    (ip.GetAddressBytes()[2] << 8) | ip.GetAddressBytes()[3]);
    }
    return -1;
}

public static string long2ip(long ipAddress)
{
    System.Net.IPAddress ip;
    if (System.Net.IPAddress.TryParse(ipAddress.ToString(), out ip))
    {
        return ip.ToString();
    }
    return "";
}

Eventuell braucht das ja auch noch jemand anderes !

DataGridView – Fehlender CheckedChanged Event für Checkboxen – Winforms – C#

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;

        }

    }

Lambda Expressions – C#

Lambda Expressions in C# umfassen sowohl Ausdrücke (expressions) als auch Anweisung’s Blöcke (statement blocks).

Egal welche Art von Lambda Expressions verwendet werden, der Lambda Operator => wird jeweils dazu verwendet um die Expression zu beschreiben.

Was ist der Unterschied zwischen einem Ausdruck und einem Anweisung’s Block.

Exemplarische Lambda Expression Ausdrücke

x => x * 5

(int x) => x * 5

o => o.FirstName == "Peter"

(Order o) => o.FirstName == "Peter"

Exemplarische Lambda Expression Anweisung’s Blöcke

y => {return 5 * x)

Wo überall setzt man Lambda Expressions ein?

Ein großes Einsatzgebiet von Lambda Expressions sind zum Beispiel zusammen mit den ebenfalls in C# 3.0 neuen Extension Methods.

DayOfWeek To Day – C#

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());
}