C# -Die Auflistung wurde geändert – Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden

Eine Meldung ähnlich wie die Überschrift kann man ganz leicht erhalten, wenn man mit Auflistungen in einer Anwendung arbeitet, welche mehrere Threads verwendet und ein Thread lesend und ein anderer schreiben auf die Auflistung zugreift.

Nun ist es so, dass es zu diesem Thema eine ganze Reihe, von mehr oder weniger, guten Ansätzen im Internet zu finden ist (Natürlich überwiegend gute 🙂 ). Wobei eine der entscheidenden Aussagen ist, dass man diese Fehlermeldungen, dann erhält wenn man mit einer Enumeration (foreach Schleife) über die Auflistung geht und dabei neue Elemente der Auflistung hinzufügen oder Elemente löschen möchte.

Also zum Beispiel wie in der folgenden Klasse:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
		foreach (Appointment appointment in this)
		{
			if (appointment.StartDate.Date == dt.Date)
			{
				Remove(appointment);
			}
		}
	}
}

Eigentlich soll, die Methode der Klasse alle Appointments eines bestimmten Datums aus der Auflistung löschen.
Das mach sie auch, und zwar dann, wenn alles in einem Thread abläuft.

Aber nur dann.

Nun wird bei den meisten Lösungen im Internet vorgeschlagen anstelle der Iteration mit foreach einfach eine for Schleife zu verwenden.

Das würde dann so aussehen
(Ich möchte aber gleich hier sagen, dass diese Lösung für das Löschen auch nicht eingesetzt werden kann, da diese egal ob mit oder ohne mehreren Threads nicht richtig arbeitet).

public class AppointmentList : List<Appointment>
{
		for (int i = 0; i < this.Count; i++)
		{
			if (this[i].StartDate.Date == dt.Date)
			{
				this.RemoveAt(i);
			}
		}
	}
}

Und warum geht das nicht ?

Nehmen wir der Einfachheit halber mal an die Auflistung würde 4 Einträge enthalten und alle 4 würden das gleiche Startdatum haben, was bedeuten würde, dass alle 4 Listen Elemente gelöscht werden müssten.

Runde 1:
i == 0 und this.count== 3
Element an Index 0 wird gelöscht
Element mit Index 1 wird nun an Index Position 0 geführt
Element mit Index 2 wird nun an Index Position 1 geführt
Element mit Index 3 wird nun an Index Position 2 geführt
this.count ist nun nur noch 2

Runde 2:
i == 1 und this.count == 2
….

Eigentlich brauche ich gar nicht mehr weiter erläutern, jetzt ist klar, dass das löschen mit der for schleife mit erhöhendem Index (i++) nicht funktionieren kann, wenigstens nicht wenn es um das Löschen von List Elementen geht.

Aber natürlich gibt es auch dafür eine einfache Lösung, und diese sieht so aus:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].StartDate.Date == dt.Date)
                {
                    this.RemoveAt(i);
                }
            }
		}
	}
}

Genau, wir verwenden einfach eine for schleife mit einem herabzählendem Index (i–).
Dies funktioniert auch mit mehreren Threads.

c# – Splitt Container Collapse Panel Advanced Example – WinForm

In einem aktuellen Projekt hat sich der Kunde gewünscht, ein „platzsparendes“ Winform, zur Anzeige von Kalenderinformationen, zu erhalten. Und da der Wunsch des Kunden gleich einem Befehl ist…. 🙂 Voila !

Hier (m)ein Lösungsansatz.

Und da ich denke, dass auch im Zeitalter von WPF, noch immer WinForm Anwendungen erstellt werden, veröffentliche ich diesen Lösungsansatz (der übrigens schon in einigen Projekten eingesetzt wird und nicht ganz neu ist) hier in diesem Beitrag und hoffe dass dies dem einen oder anderen auch Heute noch dabei behilflich sein kann, ein ähnliches Problem so einfacher lösen zu können.
Wer das Beispiel 1 : 1 in einem eigenen Projekt verwenden möchte:

Bitte schön !!!

Hier kurz noch einmal die Aufgabenstellung:
Die Form soll auf der linken Seite ein Calendar Month Control enthalten und auf der rechten Seite ein TabControl enthalten, welches dann ein komplexes Calendar Control (ähnlich wie Outlook Kalender) enthält.
Und genau da kommt wohl auch der Wunsch her, dass man das Calendar Month Control ausblenden und einblenden kann.

Eigentlich ….
gar nicht so aufwendig, aber ….
Wenn man das ganze umsetzen will, stößt man doch recht schnell an die „einfachen“ Grenzen der verfügbaren Controls.

Zur Demonstration, dass es doch nicht so aufwendig ist, habe ich dieses kleine Beispiel erstellt, welches man auch als VS2010 Projekt am Ende des Beitrags herunterladen kann.

Außer, dass man, die benötigten Controls in der richtigen Reihenfolge auf das Form ziehen muss (Siehe im Source Code) benötigt man nur ein paar Zeilen, den ich nachfolgend Kurz zeigen möchte.

namespace WinFormSplittContainerAdvancedExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            splitContainerRight.Panel1Collapsed = true;
        }

        private void btnHideLeftPanel_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = true;
            panelShow.Visible = true;
            splitContainerRight.Panel1Collapsed = false;
        }

        private void btnShowLeftPanel_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = false;
            panelShow.Visible = false;
            splitContainerRight.Panel1Collapsed = true;
        }
    }
}

Und so sieht das ganze im laufenden Betrieb aus:

Hier der Screenshot mit angezeigtem linken Kalender Panel

Und hier der Screenshot mit ausgeblendetem linken Kalender Panel

Und hier der Download: WinFormSplittContainerAdvancedExample (Einfach ZIP File entpacken und mit Visual Studio 2010 öffenen.

Viel Spaß!!