INotifyPropertyChanged的和线程 [英] INotifyPropertyChanged and Threading
问题描述
我有一个基类实施 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"); }
}
我的派生类中也有一个方法飞
的操纵纬度
。
我也有必然我的派生类的纬度
一个TextBox表单:
I also have a Form with a TextBox bound to Latitude
of my derived class:
txtLat.DataBindings.Clear();
txtLat.DataBindings.Add("Text", bindSrc, "Latitude");
一个线程用于揭开序幕飞
像这样:
A thread is used to kick off Fly
like so:
Thread tFly = new Thread(f.Fly);
tFly.IsBackground = true;
tFly.Start();
在纬度
的变化,则抛出异常:
When Latitude
changes, an exception is thrown:
数据绑定不能适合于所有绑定的列表中找到一行。
推荐答案
这似乎是一个奇怪的问题与线程关联。最终,code正试图从一个非UI线程做更新 - 我不清楚为什么它不只是显示跨线程例外,虽然 - 我不知道这是否实际上是一个包罗万象的异常处理程序。如果我删除的BindingSource
(和直接绑定到对象,这是有效的),您做得到一个跨线程异常(这是我预期)
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. If I remove the BindingSource
(and bind directly to the object, which is valid) you do get a cross-thread exception (which I expected).
的个人的,我会倾向于手动处理这个问题,即订阅事件有,做一个调用
到UI线程的方法和手动更新文本。不过,我只是检查,如果一些previous跨线程绑定code可以帮助...
Personally, I would be inclined to handle this manually, i.e. subscribe to the event with a method that does an Invoke
to the UI thread and updates the Text
manually. However, I'm just checking if some previous cross-threaded binding code might help...
下面是一个使用一个例子调用
:
Here's an example using 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);
}
}
}
}
下面是一个使用我的一些旧线程code的(修改)版本为例:
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);
}
}
这篇关于INotifyPropertyChanged的和线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!