【问题标题】:Winforms Databinding object containing a List<T>包含 List<T> 的 Winforms 数据绑定对象
【发布时间】:2010-11-10 08:07:29
【问题描述】:

我遇到了一个我知道一定很常见的情况,所以我希望解决方案很简单。我有一个包含 List 对象的对象。它还有一些属性可以反映 List 中对象的聚合数据(实际上是 BindingList,所以我可以绑定到它)。在我的表单上,我有一个用于列表的 DataGridView,以及用于聚合数据的其他一些字段。当 DataGridView 中的值发生更改时,我不知道如何触发聚合数据的刷新。

我尝试在 List 中对象的属性发生更改时引发 PropertyChanged 事件,但这似乎不会刷新聚合数据的显示。如果我访问一个聚合属性(例如,在消息框中显示它),主窗体上的文本框会被刷新。

这里有一些简化的代码来说明我想要做什么:

namespace WindowsFormsApplication1 {
public class Person {

    public int Age {
        get;
        set;
    }

    public String Name {
        get;
        set;
    }
}

public class Roster : INotifyPropertyChanged {

    public BindingList<Person> People {
        get;
        set;
    }

    public Roster () {
        People = new BindingList<Person>();
    }

    private int totalage;
    public int TotalAge {
        get {
            calcAges();
            return totalage;
        }
        set {
            totalage = value;
            NotifyPropertyChanged("TotalAge");
        }
    }

    private void calcAges () {
        int total = 0;
        foreach ( Person p in People ) {
            total += p.Age;
        }
        TotalAge = total;
    }

    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged ( String info ) {
        if ( PropertyChanged != null ) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}
}

【问题讨论】:

标签: c# winforms data-binding


【解决方案1】:

calcAges 方法和TotalAge 属性看起来很可疑。

首先,TotalAge 应该是只读的。如果允许它是公开可写的,那么改变构成时代的组件的逻辑是什么?

其次,每次获取值时,都会触发PropertyChanged事件,这样不好。

您的 Roster 类应该如下所示:

public class Roster : INotifyPropertyChanged {

    public Roster ()
    {
        // Set the binding list, this triggers the appropriate
        // event binding which would be gotten if the BindingList
        // was set on assignment.
        People = new BindingList<Person>();
    }

    // The list of people.
    BindingList<Person> people = null;

    public BindingList<Person> People 
    {
        get 
        { 
            return people; 
        }
        set 
        { 
            // If there is a list, then remove the delegate.
            if (people != null)
            {
                // Remove the delegate.
                people.ListChanged -= OnListChanged;
            }

            /* Perform error check here */ 
            people = value;

            // Bind to the ListChangedEvent.
            // Use lambda syntax if LINQ is available.
            people.ListChanged += OnListChanged;

            // Technically, the People property changed, so that
            // property changed event should be fired.
            NotifyPropertyChanged("People");

            // Calculate the total age now, since the 
            // whole list was reassigned.
            CalculateTotalAge();
        }
    }

    private void OnListChanged(object sender, ListChangedEventArgs e)
    {
        // Just calculate the total age.
        CalculateTotalAge();
    }

    private void CalculateTotalAge()
    {
        // Store the old total age.
        int oldTotalAge = totalage;

        // If you can use LINQ, change this to:
        // totalage = people.Sum(p => p.Age);

        // Set the total age to 0.
        totalage = 0;

        // Sum.
        foreach (Person p in People) {
            totalage += p.Age;
        }

        // If the total age has changed, then fire the event.
        if (totalage != oldTotalAge)
        {
            // Fire the property notify changed event.
            NotifyPropertyChanged("TotalAge");
        }
    }

    private int totalage = 0;

    public int TotalAge 
    {
        get 
        {
            return totalage;
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged ( String info ) {
        if ( PropertyChanged != null ) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

现在,当列表项中的属性发生更改时,父对象将触发属性更改事件,并且绑定到它的任何内容也应该更改。

【讨论】:

  • Erf,gg,我晚了几秒钟 ;-)
  • 不要忘记在 Person 类中实现 INotifyPropertyChanged。并且您应该考虑取消绑定 ListChanged 上的事件以避免内存泄漏...
  • @Julien Poulin:好点,更改了代码以反映。此外,从技术上讲,这不是泄漏,因为在收集名册实例时它会全部被清除(永远不会收集真正的泄漏)。但是,这是应该做的事情。
  • 嗨 casperOne - 这可以解决问题。我不明白需要引发事件的是列表本身,而不是列表中的对象。非常感谢!
【解决方案2】:

我相信您可能正在寻找类似的东西

ITypedList

还有一个Google Search 的 ITypedList 会引导您访问一些关于如何实现的不错的博客。

当我使用 ORM 时,我通常必须执行其中的一些操作才能获得良好的数据网格绑定和呈现。

【讨论】:

  • @joshlrogers:很抱歉投了反对票,但这不是绑定到的数据类型的问题,而是绑定和通知没有正确发生。 ITypedList 不会向通知体验添加任何 INotifyPropertyChanged 尚未公开的内容。
  • 嗯.....我已经重读了问题和你的答案,我想我只是错过了一些东西,也许是早上太早了。我假设他正在修改他的一个聚合中的一个对象的属性。所以换句话说,他正在修改 BindingList 中 Person 的一个属性,所以他实际上并没有修改集合本身,只是集合的一个对象。因此,如果他通过使用 ITypedList 修改他绑定网格的方式,他可以专门捕获对那些底层属性的更改并通知。也许我只是没有仔细考虑或使问题过于复杂。
猜你喜欢
  • 2010-09-12
  • 2012-09-15
  • 1970-01-01
  • 2020-05-07
  • 2017-03-05
  • 2014-05-12
  • 1970-01-01
  • 2010-10-10
  • 2011-09-09
相关资源
最近更新 更多