简体   繁体   English

INotifyPropertyChanged和线程

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM