数据绑定和跨线程异常

Data Binding And Cross-Thread Exception

试图弄清楚这段代码中的缺陷:

场景 1: 此场景使用数据绑定并在 PriceSimulator 中的 NotifyPropertyChanged() 方法中导致众所周知的 跨线程异常 class.

场景二: 此方案通过订阅 PriceSimulatorPropertyChanged 事件解决了问题,消除了跨线程问题,但必须完全避免数据绑定。

假设场景 1 是预期的场景,并且假设不了解 PriceSimulator 的内部工作原理,只想绑定到 Price 属性,核心是什么问题在这里?

Form1.cs:

public partial class Form1 : Form
{
    PriceSimulator simul;
    Action labelAction;

    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        labelAction = new Action(SetLabel);

        simul = new PriceSimulator(5, 1000);

        //Scenario 1:
        //Use data binding and get Cross-Thread exception
        //label1.DataBindings.Add("Text", simul, "Price");

        //Scenario 2:
        //This works fine
        //Subscribe to PropertyChanged event
        simul.PropertyChanged += task_PropertyChanged;

        simul.Start();
    }

    //Scenario 2:
    void task_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (label1.InvokeRequired)
            Invoke(labelAction);
        else SetLabel();
    }
    private void SetLabel()
    {
        label1.Text = simul.Price.ToString("C2"); 
    }
}

PriceSimulator.cs:

public class PriceSimulator : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged; 

    private int max, delay, priceValue;
    private Timer timer;

    public PriceSimulator(int max, int delay)
    {
        this.max = max;
        this.delay = delay;
    }
    public void Start()
    {
        timer = new Timer(CallbackProc, null, delay, delay);
    }
    private void CallbackProc(object obj)
    {
        if (++Price >= max)
            timer.Dispose();
    }
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        try
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        catch (Exception ex)
        {
            timer.Dispose();
            System.Windows.Forms.MessageBox.Show(ex.Message);
        }
    }
    public int Price 
    {
        get
        {
            return priceValue;
        }
        set
        {
            if (priceValue != value)
            {
                priceValue = value;
                NotifyPropertyChanged();
            }
        }
    }
}

您的 PriceSimulator 中必须有当前上下文 class:

private readonly SynchronizationContext _context = SynchronizationContext.Current;

现在您有了上下文,可以使用它来更新 UI:

 _context.Post(delegate
                {
                    if (++Price >= max)
                       timer.Dispose();
                }, null);