C# BindingList通过控制器绑定到网格数据源,然后从另一个线程跨线程异常更新
去年我一直在研究JavaScript和Angular,目前正在Winforms中进行一个项目。我理解UI控件InvokeRequired的跨线程问题,但我以前没有使用BindingList,所以为了进行实验,我有一个基本的应用程序,它有一个表单、控制器和实现INotifyPropertyChanged的POCO。表单被注入控制器,控制器有一个绑定列表。为了模拟处理数据的外部线程,我在控制器内部有一个Start方法,它创建一个线程,运行一个循环并更新库存列表。显然,这将引发属性更改事件,该事件将通知网格及其数据源,但是我得到了跨线程异常。我想知道的是,这方面的常见做法是什么?我过去的经验是从GUI后台工作程序启动线程,因此代码通常已经在表单中,并且可以访问“this”来调用所需的invoker。但是,当从控制器使用BindingList时,我没有这样的功能 Form1控制器 从属对象C# BindingList通过控制器绑定到网格数据源,然后从另一个线程跨线程异常更新,c#,winforms,model-view-controller,bindinglist,C#,Winforms,Model View Controller,Bindinglist,去年我一直在研究JavaScript和Angular,目前正在Winforms中进行一个项目。我理解UI控件InvokeRequired的跨线程问题,但我以前没有使用BindingList,所以为了进行实验,我有一个基本的应用程序,它有一个表单、控制器和实现INotifyPropertyChanged的POCO。表单被注入控制器,控制器有一个绑定列表。为了模拟处理数据的外部线程,我在控制器内部有一个Start方法,它创建一个线程,运行一个循环并更新库存列表。显然,这将引发属性更改事件,该事件将通
假设这是我应该要求的,我如何将更改的属性从BindingList封送回UI线程?我根据您的代码构建了一个示例,但没有收到跨线程异常;股票正在更新中。你确定你的问题是绑定列表吗?谢谢你这么快回答。我意识到我忘了提到我正在使用DevExpress。键入该注释后,我使用WinForms datagridview,它工作正常,但如果我使用DevXPress XtraGrid,则会出现x线程异常:在DevXPress.Data.v13.2.dll中出现“System.InvalidOperationException”类型的未处理异常。其他信息:检测到跨线程操作。要抑制此异常,请设置DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection=true。我将尝试使用该标志集,看看会发生什么情况。OK,我也可以使用DevExpress网格进行尝试。在没有实际查看的情况下,我关心的是设置该标志可能会使它吞下异常,而不是实际处理跨线程问题。但是值得一试。确定设置DevExpress异常中的属性意味着DevExpress网格现在可以工作。然而,这和使用普通winforms网格不应该引发线程异常吗,因为属性更改发生在我在控制器中生成的线程中?这只是一个假设,因为我没有做任何修补,但我猜,它们附加到BindingList的ListChanged事件的事件处理程序在开始时调用了所需的检查;Windows网格可能内置了它,而DevExpress网格显然有一个用于打开和关闭它的属性,因为DX具有所有属性。
public class Form1Controller
{
public bool IsRunning { get; private set; }
private readonly Random _Random = new Random();
public Form1Controller()
{
Stocks = new BindingList<Stock>();
Stocks.Add(new Stock()
{
Name = "Microsoft",
Ticker = "MSFT",
Price = 25,
Volume = 100000,
LastTraded = DateTime.Now
});
Stocks.Add(new Stock()
{
Name = "Google",
Ticker = "GOOG",
Price = 450,
Volume = 100000,
LastTraded = DateTime.Now
});
Stocks.Add(new Stock()
{
Name = "Apple",
Ticker = "AAPL",
Price = 750,
Volume = 100000,
LastTraded = DateTime.Now
});
Stocks.Add(new Stock()
{
Name = "Oracle",
Ticker = "ORCL",
Price = 80,
Volume = 100000,
LastTraded = DateTime.Now
});
}
public BindingList<Stock> Stocks { get; set; }
public void Start()
{
IsRunning = true;
new Thread(() =>
{
while (IsRunning)
{
var index = _Random.Next(0, Stocks.Count);
Debug.WriteLine(index);
var stock = Stocks[index];
stock.Price += 1;
stock.Volume += 500;
stock.LastTraded = DateTime.Now;
Thread.Sleep(100);
}
}).Start();
}
public void Stop()
{
IsRunning = false;
}
}
public class Stock : DependencyObject
{
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, () => Name); }
}
private string ticker;
public string Ticker
{
get { return ticker; }
set { SetField(ref ticker, value, () => Ticker); }
}
private int price;
public int Price
{
get { return price; }
set { SetField(ref price, value, () => Price); }
}
private long volume;
public long Volume
{
get { return volume; }
set { SetField(ref volume, value, () => Volume); }
}
private DateTime lastTraded;
public DateTime LastTraded
{
get { return lastTraded; }
set { SetField(ref lastTraded, value, () => LastTraded); }
}
}
public class DependencyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
}
public partial class Form1 : Form
{
private readonly Form1Controller _Controller = new Form1Controller();
public Form1(Form1Controller controller)
{
InitializeComponent();
_Controller = controller;
_MainGrid.DataSource = _Controller.Stocks;
}
private void _StartButton_Click(object sender, EventArgs e)
{
_Controller.Start();
}
private void _StopButton_Click(object sender, EventArgs e)
{
_Controller.Stop();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_Controller.Stop();
}
}