Data Binding And Cross-Thread Exception

时间:2015-07-29 00:14:55

标签: c# winforms data-binding thread-safety

Trying to get clear about flaw in this code:

Scenario 1: This scenario uses data binding and causes the very well known cross-thread exception in the var rand_x = CGFloat(arc4random_uniform(maxXValue)) var rand_y = CGFloat(arc4random_uniform(maxYValue)) method in the NotifyPropertyChanged() class.

Scenario 2: This scenario solves the problem by subscribing to the PriceSimulator event of PropertyChanged, eliminates the cross-thread issue but has to avoid data binding altogether.

Assuming Scenario 1 was the intended scenario and assuming one has no knowledge of the inner workings of PriceSimulator and just wanted to bind to the PriceSimulator property, what is the core issue here?

Form1.cs:

Price

PriceSimulator.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"); 
    }
}

1 个答案:

答案 0 :(得分:1)

您必须在PriceSimulator类中拥有当前上下文:

private readonly SynchronizationContext _context = SynchronizationContext.Current;

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

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