最近,我一直在测试使用类型的绑定来实现INotifyPropertyChanged
并从工作线程更新属性抛出跨线程问题。
以下是示例代码:
public class MyViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler hanlder = PropertyChanged;
if(hanlder != null)
hanlder(this, new PropertyChangedEventArgs(propertyName));
}
}
以上viewmodel已与Windows窗体中的标签文本绑定,并从工作线程更新标签值。
从工作线程更新label1文本会导致交叉线程问题:
public partial class MainForm : Form
{
private MyViewModel _myViewModel = new MyViewModel();
public MainForm()
{
InitializeComponent();
Btn1.Click += Btn1_Click;
label1.DataBindings.Add("Text", _myViewModel, "Name");
}
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
});
}
}
到目前为止,我可以相信它是由于从工作线程更新UI。是否有任何工作可以使线程安全,而无需更改按钮单击方法,即可能在视图模型中使线程安全。
答案 0 :(得分:5)
抓住用户界面SynchronizationContext
(例如,当应用启动时,从UI线程使用SynchronizationContext.Current
),并将其存储在某个地方的某个静态变量中(我已经调用过)它uiSynchronizationContext
)。
然后在你的OnPropertyChanged
做类似的事情:
protected virtual void OnPropertyChanged(string propertyName)
{
uiSynchronizationContext.Post(
o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
,null
);
}
如果您希望该操作同步(与启动发送/发布操作的线程同步,它始终是"同步&),您可以使用Send
而不是Post
#34;在UI线程上)
我特别不喜欢在多线程时进行直接数据绑定(我更喜欢使用带有更改的某个图形对象在UI线程上使用计时器进行轮询),但这应该可以解决问题。
我很欣赏它没有经过测试
答案 1 :(得分:-1)
您是否尝试使用CheckForIllegalCrossThreadCalls?
像这样:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
CheckForIllegalCrossThreadCalls = false;
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
CheckForIllegalCrossThreadCalls = true;
});
}
我不是说这是最好的方式,但是当我使用线程
时,它对我有用编辑:
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Invoke((MethodInvoker)(() => _myViewModel.Name = "Updating from worker thread!"; ));
});
}