数据绑定和跨线程异常
本文关键字:异常 线程 数据绑定 | 更新日期: 2023-09-27 18:05:19
试图弄清楚这个代码中的缺陷:
场景1:此场景使用数据绑定,并在PriceSimulator
类的NotifyPropertyChanged()
方法中导致众所周知的跨线程异常。
场景2:这个场景通过订阅PriceSimulator
的PropertyChanged
事件解决了这个问题,消除了跨线程问题,但必须完全避免数据绑定。
假设场景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();
}
}
}
}
你必须在你的pricessimulator类中有当前上下文:
private readonly SynchronizationContext _context = SynchronizationContext.Current;
现在你有了上下文,你可以用它来更新UI:
_context.Post(delegate
{
if (++Price >= max)
timer.Dispose();
}, null);