c# - 通过单独的任务更新 BindingSource 中的元素

标签 c# multithreading bindingsource

我有一个类,比如 Person,它有一个 Id 和一个名字。此类正确实现了 INotifyPropertyChanged

补充:有些人要求类 Person。

我真正的问题是一个更复杂的类,我已将它简化为一个相当简单的 POCO 以确定它不是因为我的类。

最初:

public class Person
{
    public int Id {get; set;}
    public string Name {get; set;}
}

对于更新,它需要实现 INofityChanged。完整代码在本题末尾

StackOverflow: How to properly implement INotifyPropertyChanged

  • 我有一个 System.Windows.Forms.Form
  • 此表单有一个 BindingSource。
  • 绑定(bind)源的 DataSource 属性设置为我的类 Person
  • 我有一个绑定(bind)到 BindingSource 的 DataGridView
  • 我已经向绑定(bind)源添加了几个 Person 实例
  • 正确显示添加的人员。
  • 如果我以编程方式更改绑定(bind)源中的 Person,更改后的值会正确显示。

到目前为止一切顺利。如果在单独的线程中更改 Person,就会出现问题。

我经常收到带有消息的 InvalidOperationException

BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.

我想这与更新是在可等待的异步任务中完成这一事实有关。我知道在更新用户界面项之前,您应该检查是否 InvokeRequired 并采取相应行动。

private void OnGuiItemChanged()
{
    if (this.InvokeRequired)
    {
       this.Invoke(new MethodInvoker(() => { OnGuiItemChanged(); }));
    }
    else
    {
        ... // update Gui Item
    }
}

但是,当使用绑定(bind)源时,更改是在绑定(bind)源内部处理的。所以我无法检查 InvokeRequired

那么如何在非 UI 线程中更新也存储在绑定(bind)源中的项目?

根据要求:类 Person 的实现和我的表单的一些代码

class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int id = 0;
    private string name = null;
    public int Id
    {
        get { return this.id; }
        set { this.SetField(ref this.id, value); }
    }

    public string Name
    {
        get { return this.name; }
        set { this.SetField(ref this.name, value); }
    }

    protected void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            RaiseEventPropertyChanged(propertyName);
        }
    }

    private void RaiseEventPropertyChanged(string propertyName)
    {
        var tmpEvent = this.PropertyChanged;
        if (tmpEvent != null)
        {
            tmpEvent(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

形式的一些代码:

private void Form1_Load(object sender, EventArgs e)
{
    for (int i = 0; i < 10; ++i)
    {
        var person = new Person()
        {
            Id = i,
            Name = "William " + i.ToString(),
        };
        this.bindingSource1.Add(person);
    }
}

private void buttonStart_Click(object sender, EventArgs e)
{
    this.cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    Task.Run(() => ChangePersonsAsync(this.cancellationTokenSource.Token));
}

private async Task ChangePersonsAsync(CancellationToken token)
{
    try
    {
        while (!token.IsCancellationRequested)
        {
            foreach (var p in this.bindingSource1)
            {
                Person person = (Person)p;
                person.Id = -person.Id;
            }
            await Task.Delay(TimeSpan.FromSeconds(0.01), token);
        }
    }
    catch (TaskCanceledException)
    {
    }
}

最佳答案

正如您所提到的,更改是在 BindingSource 类中处理的,因此我看到的最简单的方法是将其替换为以下内容

public class SyncBindingSource : BindingSource
{
    private SynchronizationContext syncContext;
    public SyncBindingSource()
    {
        syncContext = SynchronizationContext.Current;
    }
    protected override void OnListChanged(ListChangedEventArgs e)
    {
        if (syncContext != null)
            syncContext.Send(_ => base.OnListChanged(e), null);
        else
            base.OnListChanged(e);
    }
}

只需确保它是在 UI 线程上创建的。

关于c# - 通过单独的任务更新 BindingSource 中的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32885552/

相关文章:

c# - 如何对绑定(bind)到自定义对象集合的 DataGridView 进行排序?

c# - 使用反射区分重载方法的泛型和非泛型版本

javascript - 使用CEFSharp编辑&lt;textarea&gt;

c# - 异常抛出 : encapsulate them or not?

c# - java 的 transient 关键字的 C# 等价物是什么?

java - protected block -notifyAll()vs interrupt()

java - 线程作为 GC 根

java类连续2-3分钟后调用rest api

VB.Net 将 datagridview Comboboxcolumn 绑定(bind)到 datagridviewTextboxColumn

c# - 在 Entity Framework 中使用 bindingSource 过滤器