Threadübergreifender Vorgang aber keine InvalidOperationException

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Fakiz.

    Threadübergreifender Vorgang aber keine InvalidOperationException

    Ich bin gerade dabei ein Programm von c# 7.3 auf 8.0 upzugraden, dabei bin ich über eine Codepassage gestolpert in der Eigenschaften eines ListItems über einen neben Thread geändert werden. Erwartet hätte ich eine InvalidOperationException aber das ganze funktioniert erstaunlicherweise.

    Die Methode erstellt dabei ein Array der zu bearbeiteten Items hierzu werden diese zunächst mittels Linq.Cast umgewandelt und anschließend mit Linq.ToArray in ein Array gepackt. Ich hab ein kleines Testprojekt erstellt das dass Verhalten reproduziert.
    Die Methode die das Verhalten reproduziert sieht so aus:

    C#-Quellcode

    1. private async void ColorLvItemsRandomize()
    2. {
    3. Random rnd = new Random();
    4. KnownColor[] colors = (KnownColor[])Enum.GetValues(typeof(KnownColor));
    5. ListViewItem[] items = ListView1.Items.Cast<ListViewItem>().ToArray();
    6. await Task.Factory.StartNew(() =>
    7. {
    8. foreach (ListViewItem item in items)
    9. {
    10. int index = rnd.Next(0, colors.Length - 1);
    11. item.BackColor = Color.FromKnownColor(colors[index]); // Spätestens hier sollte doch eine InvalidOperationException fliegen.
    12. }
    13. });
    14. }

    Jetzt Frage ich mich und euch wie ist das Möglich?

    PS: Ein bereinigte C# Projektmappe mit dem Testprojekt hab ich auch angehängt.
    Dateien
    @Fakiz Geht doch, ich habe die Property Text gesetzt.

    C#-Quellcode

    1. foreach (ListViewItem item in items)
    2. {
    3. int index = rnd.Next(0, colors.Length - 1);
    4. item.BackColor = Color.FromKnownColor(colors[index]);
    5. item.Text = "bla";
    6. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ja da hast du recht, bei den Eigenschaften: Checked, Focused, Group, ImageIndex, ImageKey, IndentCount, Name, Position, Selected, StateImageIndex, Text und ToolTipText wird eine Exception ausgelöst. Bei den Eigenschten BackColor, Font, ForeColor, Tag und UseItemStyleForSubItems allerdings nicht.

    Ich hab mir hierfür mal eine kleine Methode erstellt die prüfen sollte welche Eigenschaften sich in einem neben Thread setzen lassen. Dabei fliegt eine Exception wenn ich die Eigenschaft Text oder Name ändere. Gucke ich mir den Wert einer dieser Eigenschaften danach an, wurde dieser aber geändert.

    Für die die es selbst versuchen wollen (Bereinigte Projektmappe im Dateianhang):
    Die genutzte Methode

    C#-Quellcode

    1. private async void TryChangeProperties()
    2. {
    3. PropertyInfo[] piListViewItem = typeof(ListViewItem).GetProperties();
    4. ListViewItem item = ListView1.Items.Cast<ListViewItem>().First();
    5. await Task.Factory.StartNew(() =>
    6. {
    7. foreach (PropertyInfo pi in piListViewItem)
    8. {
    9. try
    10. {
    11. if (!pi.CanWrite)
    12. continue;
    13. object value = null;
    14. if (pi.PropertyType == typeof(string)) value = "MyTestStringValue";
    15. else if (pi.PropertyType == typeof(Color)) value = Color.Red;
    16. else if (pi.PropertyType == typeof(Font)) value = new Font("Arial", 14);
    17. else if (pi.PropertyType == typeof(int)) value = 8;
    18. else if (pi.PropertyType == typeof(bool)) value = false;
    19. else if (pi.PropertyType == typeof(Point)) value = new Point(10, 10);
    20. else if (pi.PropertyType == typeof(ListViewGroup)) value = new ListViewGroup();
    21. else value = new object();
    22. pi.SetValue(item, value);
    23. AddLogThreadSafe($"{0,9:Changed}: {pi.Name}, new Value: {pi.GetValue(item)}");
    24. }
    25. catch (Exception ex)
    26. {
    27. AddLogThreadSafe($"Exception: {pi.Name}, {ex.InnerException.Message}");
    28. }
    29. }
    30. });
    31. AddLogThreadSafe($"\r\nName: {item.Name}, Text: {item.Text}");
    32. AddLogThreadSafe($"\r\nName: {ListView1.Items[0].Name}, Text: {ListView1.Items[0].Text}");
    33. }
    34. private void AddLogThreadSafe(string log)
    35. {
    36. if (InvokeRequired)
    37. {
    38. Invoke(new MethodInvoker(() => { AddLogThreadSafe(log); }));
    39. return;
    40. }
    41. TextBox_Log.Text += $"{log}\r\n";
    42. }



    Und der Output der durch die Methode erstellt wird

    Quellcode

    1. Changed: BackColor, new Value: Color [Red]
    2. Exception: Checked, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    3. Exception: Focused, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    4. Changed: Font, new Value: [Font: Name=Arial, Size=14, Units=3, GdiCharSet=1, GdiVerticalFont=False]
    5. Changed: ForeColor, new Value: Color [Red]
    6. Exception: Group, Das Element item0.1 kann nur an einer Stelle hinzugefügt oder eingefügt werden. Entfernen Sie es von der aktuellen Position, oder klonen Sie es. Parametername: item
    7. Exception: ImageIndex, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    8. Exception: ImageKey, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    9. Exception: IndentCount, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    10. Exception: Name, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    11. Exception: Position, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    12. Exception: Selected, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    13. Exception: StateImageIndex, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    14. Changed: Tag, new Value: System.Object
    15. Exception: Text, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    16. Exception: ToolTipText, Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ListView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
    17. Changed: UseItemStyleForSubItems, new Value: False
    18. Name: MyTestStringValue, Text: MyTestStringValue
    19. Name: MyTestStringValue, Text: MyTestStringValue

    Dateien