[英]INotifyPropertyChanged and Threading
I have a base class implementing INotifyPropertyChanged
: 我有一个实现INotifyPropertyChanged
的基类:
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
I have a derived class with a property Latitude
like so: 我有一个带有Latitude
属性的派生类,如下所示:
private double latitude;
public double Latitude
{
get { return latitude; }
set { latitude = value; OnNotifyChanged("Latitude"); }
}
My derived class also has a method Fly
that manipulates Latitude
. 我的派生类也有一个方法Fly
来操纵Latitude
。
I also have a Form with a TextBox bound to Latitude
of my derived class: 我还有一个Form,其TextBox绑定到我的派生类的Latitude
:
txtLat.DataBindings.Clear();
txtLat.DataBindings.Add("Text", bindSrc, "Latitude");
A thread is used to kick off Fly
like so: 一个线程用于启动Fly
如下所示:
Thread tFly = new Thread(f.Fly);
tFly.IsBackground = true;
tFly.Start();
When Latitude
changes, an exception is thrown: 当Latitude
更改时,会抛出异常:
DataBinding cannot find a row in the list that is suitable for all bindings.
This seems to be an odd issue with thread affinity. 这似乎是线程亲和力的一个奇怪问题。 Ultimately, the code is trying to do the update from a non-UI thread - I'm unclear why it isn't just displaying the cross-thread exception, though - I wonder whether this is actually a catch-all exception handler. 最终,代码试图从非UI线程进行更新 - 我不清楚为什么它不仅仅是显示跨线程异常 - 我不知道这是否实际上是一个全能的异常处理程序。 If I remove the BindingSource
(and bind directly to the object, which is valid) you do get a cross-thread exception (which I expected). 如果我删除BindingSource
(并直接绑定到对象,这是有效的),你会得到一个跨线程异常(我期望)。
Personally , I would be inclined to handle this manually, ie subscribe to the event with a method that does an Invoke
to the UI thread and updates the Text
manually. 就个人而言 ,我倾向于手动处理,即使用Invoke
UI线程的方法订阅事件并手动更新Text
。 However, I'm just checking if some previous cross-threaded binding code might help... 但是,我只是检查一些以前的跨线程绑定代码是否有帮助......
Here's an example using Invoke
: 这是使用Invoke
的示例:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class FlightUav : INotifyPropertyChanged
{
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
private double _latitude;
public double Latitude
{
get { return _latitude; }
set { _latitude = value; OnNotifyChanged("Latitude"); }
}
public void Fly()
{
for (int i = 0; i < 100; i++)
{
Latitude++;
Thread.Sleep(10);
}
}
[STAThread]
static void Main()
{
using (Form form = new Form())
{
FlightUav currentlyControlledFlightUav = new FlightUav();
currentlyControlledFlightUav.PropertyChanged += delegate
{ // this should be in a *regular* method so that you can -= it when changing bindings...
form.Invoke((MethodInvoker)delegate
{
form.Text = currentlyControlledFlightUav.Latitude.ToString();
});
};
using (Button btn = new Button())
{
btn.Text = "Fly";
btn.Click += delegate
{
Thread tFly = new Thread(currentlyControlledFlightUav.Fly);
tFly.IsBackground = true;
tFly.Start();
};
form.Controls.Add(btn);
Application.Run(form);
}
}
}
}
Here's an example using a (modified) version of some old threading code of mine: 以下是使用我的一些旧线程代码的(修改)版本的示例:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class FlightUav : INotifyPropertyChanged
{
protected void OnNotifyChanged(string pName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
private double _latitude;
public double Latitude
{
get { return _latitude; }
set { _latitude = value; OnNotifyChanged("Latitude"); }
}
public void Fly()
{
for (int i = 0; i < 100; i++)
{
Latitude++;
Thread.Sleep(10);
}
}
[STAThread]
static void Main()
{
using (Form form = new Form())
{
FlightUav currentlyControlledFlightUav = new FlightUav();
BindingSource bindSrc = new BindingSource();
var list = new ThreadedBindingList<FlightUav>();
list.Add(currentlyControlledFlightUav);
bindSrc.DataSource = list;
form.DataBindings.Clear();
form.DataBindings.Add("Text", list, "Latitude");
using (Button btn = new Button())
{
btn.Text = "Fly";
btn.Click += delegate
{
Thread tFly = new Thread(currentlyControlledFlightUav.Fly);
tFly.IsBackground = true;
tFly.Start();
};
form.Controls.Add(btn);
Application.Run(form);
}
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
ctx = SynchronizationContext.Current;
}
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.