我有一个
BindingList<T>
它绑定到 datagridview。我的类中的一个属性需要很长时间才能计算,因此我对该操作进行了线程化。计算后,我引发 OnPropertyChanged() 事件以通知网格该值已准备好。
至少,理论上是这样。但由于 OnPropertyChanged 方法是从不同线程调用的,因此我在网格的 OnRowPrePaint 方法中遇到了一些奇怪的异常。
谁能告诉我如何触发 OnPropertyChanged 事件在主线程中执行?我无法使用 Form.Invoke,因为 MyClass 类不知道它在 Winforms 应用程序中运行。
public class MyClass : INotifyPropertyChanged
{
public int FastMember {get;set;}
private int? slowMember;
public SlowMember
{
get
{
if (slowMember.HasValue)
return slowMember.Value;
else
{
Thread t = new Thread(getSlowMember);
t.Start();
return -1;
}
}
}
private void getSlowMember()
{
Thread.Sleep(1000);
slowMember = 5;
OnPropertyChanged("SlowMember");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangingEventHandler eh = PropertyChanging;
if (eh != null)
{
eh(this, e);
}
}
}
人们有时会忘记事件处理程序是MultiCastDelegate
因此,拥有有关每个订阅者的所有信息,我们需要优雅地处理这种情况,而不会造成不必要的调用+同步性能损失。我多年来一直使用这样的代码:
using System.ComponentModel;
// ...
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
foreach (EventHandler h in handler.GetInvocationList())
{
var synch = h.Target as ISynchronizeInvoke;
if (synch != null && synch.InvokeRequired)
synch.Invoke(h, new object[] { this, e });
else
h(this, e);
}
}
}
它的作用很简单,但我记得当时我几乎想破了脑袋,试图找到最好的方法来做到这一点。
它首先“抓取”本地属性上的事件处理程序以避免任何竞争条件。
如果处理程序不为空(至少存在一个订户),它将准备事件参数,然后迭代此多播委托的调用列表。
调用列表具有目标属性,它是事件的订阅者。如果这个订阅者实现了 ISynchronizeInvoke(所有 UI 控件都实现了它),我们就会检查它的 InvokeRequired 属性,如果确实如此,我们只需传递委托和参数来调用它。以这种方式调用它会将调用同步到 UI 线程中。
否则,我们只需直接调用事件处理程序委托。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)