Hallo Zusammen,
Ich habe eine Klasse die vom Interface INotifyPropertyChanged erbt.
Die Instanzen dieser Klasse laufen nicht im Main Thread sondern in einem
eigene. Auf dem Form habe ich ein DataGridView welches gebunden ist auf
ein Dictionary wobei dessen Inhalt die Instanzen der beschrieben Klasse
verwaltet. Jetzt bekomme eine exception:> Fehlermeldung:
"Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement MyGrid erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."
Hat jemand eine Idee was zu tun ist?
Vermutlich ist es das Zeug mit dem InvokeRequired hab jedoch keine
Ahnung wie ich das mit dem Grid machen soll.
Gruß Kostas
Hallo Zusammen,
dieses Problem wurde mit freundlicher Unterstützung von herbivore gelöst.
Wenn Objekte an ein DataGridView gebunden sind, so dürfen diese nur
im gleichen Thread also im Main thread geändert werden (Wertzuweisung)
Ich hatte jedoch die Aktualisierung der Objekte in einen anderen Thread.
Um das Grid zu aktualisieren ist es notwendig das ChangeEvent an den
Main Thread zu senden. Und das geht so:
public event PropertyChangedEventHandler PropertyChanged;
private static SynchronizationContext _sync = new System.Windows.Forms.WindowsFormsSynchronizationContext();
private void NotifyPropertyChanged(String info)
{
_sync.Post(_NotifyPropertyChanged, info);
}
public void _NotifyPropertyChanged(Object info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs((String)info));
}
}
Gruß Kostas
Dankeschön herbivore.
Ich habe so ziemlich das selbe Problem aber verstehe die Lösung dafür nicht so ganz...
public class MyEventArgs : EventArgs, INotifyPropertyChanged
{
public String _Message;
public MyEventArgs() { }
public MyEventArgs(String Message)
{
this.Message = Message;
}
public String Message
{
get { return _Message; }
set
{
if (value != _Message)
{
_Message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void _OnPropertyChanged(Object propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Convert.ToString(propertyName)));
}
}
public SynchronizationContext _sync = new WindowsFormsSynchronizationContext();
private void OnPropertyChanged(String info)
{
_sync.Post(_OnPropertyChanged, info);
}
}
Das ist meine verwendete Klasse, diese binde ich in der GUI an einem DataGridView.
DataGridView.DataSource = BindingList<MyEventArgs>
Dennoch wird mir der Fehler geschmissen "Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement...."
Kann mir evtl. wer erklären wieso?
Mit freundlichen Grüßen
WindowsFormsSynchronizationContext muss natürlich im UI-Thread erstellt werden.. und nicht erst im Thread welcher den Event feuern will...
Überhaupt eine sehr eigenwillige Konstruktion hast Du da gebaut (EventArgs mit INotifyPropertyChange Halloooo ??)
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
Danke erst einmal für die schnelle Antwort =) -
Ich habe es jedoch auch versucht, wenn ich es in der Main-UI Erstelle doch auch ohne erfolg..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class LogData : INotifyPropertyChanged
{
public LogData(string text) { Message = text; }
private string _Message = string.Empty;
public string Message
{
get { return _Message; }
set{
if (value != _Message){
_Message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void _OnPropertyChanged(Object propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(Convert.ToString(propertyName)));
}
protected void OnPropertyChanged(Object propertyName)
{
if (PropertyChanged != null)
Program.MyForm._sync.Post(_OnPropertyChanged, propertyName);
}
}
public partial class Form1 : Form
{
public WindowsFormsSynchronizationContext _sync = new WindowsFormsSynchronizationContext();
public Form1() { InitializeComponent(); }
private BindingList<LogData> logmessages = new BindingList<LogData>();
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
logmessages.Add(new LogData("Message"));
});
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = logmessages;
}
}
}
Oder habe ich hier noch einen Fehler gemacht?
PS: Das
EventArgs
muss ich noch entfernen... stammt noch von der alten Lösung.
Hallo,
zu allererst solltest du die Anpassung rückgängig machen, dass du dein Formular statisch in der Program.cs definierst. Richtiger wäre es, wenn du den SynchronizationContext an das LogData-Objekt übergibst.
Eins noch, die Exception die dir um die Ohren geworfen wird kommt nicht vom den LogData-Einträgen, sondern von der BindingList. - Du fügst in einem anderen Thread (Task) Daten in die Liste ein.
Schau dir mal folgenden Link an, der dürfte dir helfen. WinForms: Multithreaded BindingList
Wissen ist nicht alles. Man muss es auch anwenden können.
PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |
Hallo CSharpFreak,
generell liegt es gar nicht in der Zuständigkeit der Klasse bzw. des Events, die Ausführung an den GUI-Thread zu delegieren, sondern im Normalfall in der Zuständigkeit des EventHandlers, der den GUI-Zugriff ausführen will.
Im speziellen Fall des DataBinding gilt, dass alle Zugriffe auf die gebundenen Daten aus dem GUI-Thread erfolgen müssen. Dann ist gar kein Delegieren (im EventHandler) mehr nötig bzw. ist es schon vor dem Zugriff erfolgt. Wenn der Zugriff auf die gebundenen Daten nicht aus dem GUI-Thread erfolgt, dann kann mehr schiefgehen, als nur eine Cross-Thread-Exception bei INotifyPropertyChanged. (Vielen Dank an Programmierhans, für den Hinweis, dass man der Verwendung von DataBinding keinen Einfluss auf die Implementierung des verwendeten EventHandlers hat.
Das im ersten Absatz Gesagte wurde in Eleganteste Art aus Worker-Thread auf Controls zugreifen [generell Kontrollfluss zwischen Threads] ausführlich diskutiert, ein Thread, der aus dem genannten FAQ-Beitrag heraus verlinkt ist. Das im zweiten Absatz Gesagte steht direkt im FAQ-Beitrag. Überhaupt steht alles, was du zu dem Thema wissen musst, in der FAQ. Bitte beachte [Hinweis] Wie poste ich richtig? Punkt 1.1 (und 1.1.1).
herbivore