Abou Chleih

{the magic lies between the brackets}

Menü Schließen

Schlagwort: C# (Seite 2 von 2)

[C#] Serialisieren von Listen (eigener Objekte)

In manchen Fällen kann es sinnvoll sein Listen und deren Inhalt zu speichern.
In meinem Fall wurde die Liste mit Objekten einer eigens erstellen Klasse gefüllt, dadurch ist es sehr schwierig diese Liste mit dem XmlSerializer zu serialisieren/speichern.

Für solche Fälle eignet sich der BinaryFormatter

Um die Daten zu serialisieren benötigen wir folgende Namespaces:

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization; //Kann fürdas Exception-Handling genutzt werden
//bspw. SerializationException

Zusätzlich benötigt man natürlich auch das zu sichernde Objekt – in meinem Fall eine Liste mit Objekten vom Typ „Receiver“ mit Namen receiverList

List<Receiver> receiverList = new List<Receiver>();

Meine Receiver-Klasse sieht wie folgt aus:

    [Serializable()]
    public class Receiver
    {
        public string Name { get; set; }
        public string MACAddress { get; set; }
        public IPAddress IP { get; set; }
        public string Info { get; set; }

        public Receiver(string pName, string pMAC, string pIPAddress, string pInfo)
        {
            Name = pName;
            MACAddress = pMAC;
            if (pIPAddress != null && pIPAddress != "")
            {
                IP = IPAddress.Parse(pIPAddress);
            }
            Info = pInfo;
        }
    }

Und schon haben wir den ersten wichtigen Punkt: Die Klasse muss als serialisierbar(engl. Serializable) gekennzeichnet sein, daher muss das Serializable-Attribut [Serializable()] über dem Header der Klasse angebracht werden, eine paramterlosen Konstruktor, wie bei XmlSerializer benötigt man nicht

Nun zum eigentlichen Speichern der Liste, man könnte natürlich auch jedes Objekt einzeln speichern, jedoch halte ich das für aufwendiger und man benötigt die genaue Anzahl der zu serialisierenden Objekte. Deshalb speichern wir die Collection selbst und schreiben sie komplett in einen FileStream (Datenstrom).

        private void SaveReceiverList()
        {
            FileStream stream;
            stream = new FileStream(@"receiverList.dat", FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, receiverList);
            stream.Close();
        }

Zum Laden der Liste benutzen wir folgenden Code:

        private void LoadReceiverList()
        {
            FileStream fs = new FileStream(@"receiverList.dat", FileMode.Open); ;
            BinaryFormatter formatter = new BinaryFormatter();
            receiverList = (List<Receiver>)formatter.Deserialize(fs);
        }

Bisher wurde, zum Nutzen der Übersicht, kein Exception-Handling eingebaut, dies sollte man aber unbedingt nachholen. Gerade beim Lesen und Schreiben von Dateien kommt es immer mal wieder zu Ausnahmen (bspw. UnauthorizedAccessException)

MySQL DataGridView variabel füllen

Wer öfter mit MySQL und dessen Connector arbeitet, möchte sicher einmal die Daten geordnet in einem DataGridView anzeigen. Damit dies aber variabel ist, möchte man die Header natürlich nicht selbst anlegen.

Wie geht man also vor, sodass es möglichst einfach ist?
Zuerst einmal benötigen wir eine DataTable. Um diese zu benutzen, müssen wir den Namespace System.Data mittels

using System.Data;

hinzufügen.

Danach brauchen wir eine Methode, welche natürlich die Daten bezieht und dann zurückgibt. Und genau hier brauchen wir etwas spezielles. Nämlich den MySQLDataAdapter, welcher die Möglichkeit bietet, die empfangenen Daten direkt in eine DataTable zu speichern. Die Methode gibt diese dann zurück

        public DataTable executeSQLQuery()
        {
            DataTable queryTable = new DataTable();
            MySqlConnection connection = new MySqlConnection("SERVER=localhost;UID=user;PASSWORD=passw0rd;DATABASE=db;");
            MySqlCommand command = connection.CreateCommand();
            command.CommandText = "SELECT * FROM table";
            MySqlDataAdapter adapter = new MySqlDataAdapter(command.CommandText, connection);

            try
            {
                adapter.Fill(queryTable);//Öffnet die Verbindung, füllt die DatenTabelle und schließt die Verbindung
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            return queryTable;
        }

Und wie bekommen wir nun die Daten in das DataGridView-Steuerelement?
Ganz einfach mittels DataSource:

dataGridView.DataSource = executeSQLQuery();

Threadübergreifende Zugriffe(Invoke)

Zu Threading verweise ich auf: Threading mit :Net 2.0

Oftmals ist es nötig aus externen Threads heraus auf die GUI zuzugreifen. Da diese meist in dem Hauptthread der Anwendung läuft bzw. in einem eigenständigen GUI-Thread, wird der direkte Zugriff auf das Control während des Debuggings(nicht bei einer kompilierten Anwendung!) mit einer InvalidOperationException quittiert.

Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement richTextBox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

In .NET gilt nämlich, dass nur der Thread, welcher das Control erstellt hat, auf dieses zugreifen darf. Hier werde ich nun zwei mögliche Arten beschreiben, die es ermöglichen das Control zu verändern.

1. Möglichkeit

Umleitung in den Thread, welcher das Control erstellt hat.

Dazu brauchen wir einen Delegaten

Zuerst einmal wird der Import des Namespaces
System.Threading

Danach erstellen wir ein nicht initialisiertes Objekt eines Threads:

Thread demoThread;

Gefolgt von einer Methode, welche das Control anspricht.

private void DemoThreadMethod()
        {
            for (int i = 0; i < 100; i++)
            {
                // Sicher auf das Steuerelement zugreifen
                // Dazu wird ein s.g. generischer Delegat benötigt,
                //in welchem ein voller GUI-Zugriff möglich ist
                if (this.resultLabel.InvokeRequired)
                {
                    MethodInvoker UpdateLabel = delegate
                    {
                        resultLabel.Text = i.ToString();
                    };
                    Invoke(UpdateLabel);
                }
                else
                {
                    // Der Zugriff erfolgt in demselben Thread
                    this.resultLabel.Text = i.ToString();
                }

                // Kleine Pause zur Veranschaulichung der Threadaktivität
                Thread.Sleep(50);
            }
        }

Zu guter Letzt müssen wir nur noch den Thread initialisieren:

            demoThread = new Thread(DemoThreadMethod);
            demoThread.Start(); 

Um den Unterschied zwischen threaded und unthreaded sehen möchte, muss einfach die Methode DemoThreadMethod direkt aufrufen.
Da dadurch das Label vom Mainthread aus geändert werden soll, gibt InvokeRequired „false“ zurück.

Beim Beenden des Programms muss darauf geachtet werden, dass der Thread in welchem der Invoke ausgeführt wird, beendet wird. Ansonsten kommt es zu einem ObjectDisposedException, da der Thread noch läuft, das Objekt(der Invoke des Labels), auf welches er aber zugreift bereits durch das Beenden verworfen wurde.

2. Möglichkeit

Über die Komponente BackgroundWorker.

Der BackgroundWorker selbst bietet bereits eine Methode um die GUI zu aktualisieren: Die ReportProgress-Methode

Um diese zu verwenden muss die Eigenschaft WorkerReportsProgress auf ‚true‘ gesetzt werden, ansonsten tritt eine InvalidOperationException auf.

Die Methode ruft das ProgressChanged-Event auf, welche eine Methode aufruft, welche sich im Thread befindet, der den BackgroundWorker aufgerufen hat.

Folgender Code ruft die oben beschrieben Vorgehensweise auf.
Zuerst benötigen wir zwei Eventhandler, welche unsere Methoden aufrufen. Diese platziert man am besten im Konstruktor

backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);

Folgende Methode wird aufgerufen, wenn der BackgroundWorker mittels RunWorkerAsync gestartet wird.

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
	for (int i = 0; i <= maxAmount; i++)
	{
		if (backgroundWorker.CancellationPending)
		{
			e.Cancel = true;
		}
		int percentage = (int)((i/(double)maxAmount) * 100);
		this.backgroundWorker.ReportProgress(percentage); //Ruft das ProgressChanged-Event auf, welches die unten stehende Methode aufruft (definiert beim EventHandler)
 }
 e.Result = "Finished";
}

Folgende Methode wird im Thread aufgerufen, welche den BackgroundWorker gestartet hat. Meistens also im Thread, in welchem sich die GUI befindet

void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    BackgroundWorker_progressBar.Value = e.ProgressPercentage;
}

C#/MySQL Alle MySQL Datenbanken auslesen

Ich wollte heute folgende Aufgabe lösen:
Lese alle erstellten MySQL-Datenbanken auf dem Host aus und füge sie einer comboBox hinzu. Im Endeffekt wird dies durch folgenden Code erledigt:

database_comboBox.Items.Clear();
string connectionstring = "SERVER=" + host_textBox.Text + ";" + "UID=" + User_textBox.Text + ";" + "PASSWORD=" + password_textBox.Text+ ";";
MySqlConnection con = new MySqlConnection(connectionstring);
MySqlCommand cmd = con.CreateCommand();
cmd.CommandText = "show databases";
try
{
     con.Open();
     MySqlDataReader reader = cmd.ExecuteReader();
     while (reader.Read())
     {
           string row = "";
           for (int i = 0; i < reader.FieldCount; i++)
                    row += reader.GetValue(i).ToString();
           database_comboBox.Items.Add(row);
     }
     database_comboBox.DroppedDown = true;
}
catch (MySqlException ex)
{
     MessageBox.Show(ex.Message,ex.Source,MessageBoxButtons.OK,MessageBoxIcon.Error);
}

Im connectionstring werden der Host und die Credentials angegeben. In der Regel wird auch die Datenbank angegeben(in unserem Fall natürlich nicht)
MySQL-Command wird logischerweise der SQL-Query angegeben.

C# Jeden Eintrag in einem DataGridView durchlaufen

Folgender Code durchläuft jede Zeile eines DataGridViews und in jeder Zeile alle Zellen und vergleicht deren Wert mit einem String.

            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                for (int i = 0; i < dataGridView1.ColumnCount; i++)
                {
                    if ((string)row.Cells[i].Value == "String(Wert)"){
                        row.DefaultCellStyle.ForeColor = Color.Red; // Ändert die Farbe der kompletten Zeile auf Rot
                        row.Cells[i].xxx // Ändere etwas an der spezifischen Zelle
                    }
                    else{
                        row.DefaultCellStyle.ForeColor = Color.Green; // Ändert die Farbe der kompletten Zeile auf Grün
                    }
                }
            }

In der if-Abfrage ist ein Cast nötig, da ansonsten ein Object mit einem String verglichen wird.

[C#] Verknüpfung auf dem Desktop anlegen

Eine Ordner-Verknüpfung auf dem Desktop(oder in sonstigen Ordnern) mittels C# und dem .NET Framework zu erstellen ist recht simple.

Dazu fügen wir einfach eine Referenz für

Windows Script Host Object Model

hinzu.
Im Visual Studio geht das ganz einfach mittels Rechtsklick auf das
Projekt -> Verweis hinzufügen -> COM

Nun beanspruchen wir die Ressource mittels:

using IWshRuntimeLibrary;

und erstellen eine Methode mit folgendem Inhalt

string deskDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

            WshShell shell = new WshShell();
            IWshShortcut link = (IWshShortcut)shell.CreateShortcut(deskDir + "\\Verknüpfung.lnk");
            link.IconLocation = Pfad + "\\Icon.ico";
            link.TargetPath = Pfad;
            link.Save();

[ratings]

C#/.NET Taskbar PopUp

Erstellt eure Form und benutzt diesen Code um die Form am unteren, rechten Rand hochfahren zu lassen.

public void Animate()
    {
        this.Location = new Point(SystemInformation.VirtualScreen.Width - this.Width, SystemInformation.VirtualScreen.Height);
        for (int i = 0; i < this.Height; i++)
        {
            this.Location = new Point(SystemInformation.VirtualScreen.Width - this.Width, SystemInformation.VirtualScreen.Height - i);
        }
    }

© 2020 Abou Chleih. Alle Rechte vorbehalten.

Thema von Anders Norén.