1

最近,我一直在测试类型绑定,该绑定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。是否有任何解决方法可以使其线程安全而不改变按钮单击方法,即可能在视图模型中使线程安全。

4

2 回答 2

5

抓取 UI SynchronizationContextSynchronizationContext.Current例如,在应用程序启动时从 UI 线程使用),并将其存储在某个静态变量中(我称之为uiSynchronizationContext)。

然后在你OnPropertyChanged做类似的事情:

protected virtual void OnPropertyChanged(string propertyName)
{
   uiSynchronizationContext.Post(
        o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
       ,null
     );
}

或者,如果您希望该操作是同步的(与启动发送/发布操作的线程同步,它在 UI 线程上始终是“同步的”),则可以使用Send而不是Post

我特别不喜欢在多线程时进行直接数据绑定(我更喜欢在 UI 线程上使用带有更改的图形对象的计时器进行轮询),但这应该可以解决问题。

我承认没有测试过

于 2016-04-18T08:02:14.473 回答
-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!"; ));
    });
}
于 2016-04-18T07:45:21.990 回答