【问题标题】:Bind ObservableCollection to DataGrid with auto refresh通过自动刷新将 ObservableCollection 绑定到 DataGrid
【发布时间】:2016-11-01 07:00:44
【问题描述】:

我正在尝试将 ObservableCollection 列表绑定到数据网格。

列表中的值每 100 毫秒更改一次。 如果值更改,我希望网格自动刷新。

这里有一个小演示项目,可以让它工作。但如果没有刷新 UI 按钮,则一切正常。

public partial class MainWindow : Window
{
    public ObservableCollection<DemoItem> ItemList = new ObservableCollection<DemoItem>(); 

    public MainWindow()
    {
        InitializeComponent();

        DemoItem di1 = new DemoItem();
        di1.Name = "Spieler 1";
        di1.Zufallszahl = 0;
        di1.Alter = 21;

        DemoItem di2 = new DemoItem();
        di2.Name = "Spieler 2";
        di2.Zufallszahl = 0;
        di2.Alter = 15;

        ItemList.Add(di1);
        ItemList.Add(di2);

        DispatcherTimer dt = new DispatcherTimer();
        dt.Interval = new TimeSpan(0, 0, 0, 0, 100);
        dt.Tick += Dt_Tick;
        dt.Start();
    }

    public ObservableCollection<DemoItem> ObservableDemoItem
    {
        get
        {
            return this.ItemList;
        }
    }

    private void Dt_Tick(object sender, EventArgs e)
    {
        Random rnd = new Random();

        ItemList[0].Zufallszahl = rnd.Next(0, 1000);
        ItemList[1].Zufallszahl = rnd.Next(0, 1000);
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        dataGrid.Items.Refresh();
    }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication1"
    mc:Ignorable="d"
    Title="MainWindow" Height="359.428" Width="539.141">
<Grid>
    <DataGrid x:Name="dataGrid" HorizontalAlignment="Left" 
        Margin="10,10,0,0" SelectionMode="Extended" VerticalAlignment="Top"
        Height="199" Width="497" CanUserAddRows="False" 
        CanUserDeleteRows="False" AutoGenerateColumns="False" 
        DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}" 
        ItemsSource="{Binding ObservableDemoItem}" >
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
            <DataGridTextColumn Header="Alter" Binding="{Binding Alter}" />
            <DataGridTextColumn Header="Aktiv" Binding="{Binding Zufallszahl}" />
        </DataGrid.Columns>
    </DataGrid>
    <Button x:Name="button1" Content="Update UI" HorizontalAlignment="Left" 
        Margin="55,245,0,0" VerticalAlignment="Top" 
        Width="425" Height="61" Click="button1_Click"/>
</Grid>
</Window>

我需要改变什么才能让它发挥作用?

【问题讨论】:

  • DemoItem-Class 必须实现INotifyPropertyChanged 接口,仅此而已。在此处和 MSDN 上阅读以了解正在发生的事情。简而言之:无论何时设置属性,您都必须调用由您的 Collection 处理的 PropertyChanged-Event。请帮我一个忙,不要用德语和英语编写混合代码
  • @Nitro.de 正确,DemoItem 需要实现 INotifyPropertyChanged。这是关于 SO 的答案:stackoverflow.com/q/22580623/424129
  • ObservableCollection 仅在添加或删除元素时通知,而不是在现有元素已更改时通知。

标签: c# wpf datagrid


【解决方案1】:

ObservableCollection 仅在添加或删除元素时通知 UI 更改(引发 CollectionChanged 事件),而不是在现有元素已更改时。

要跟踪集合元素的变化,如建议的Nitro.deEd Plunkett,元素的类应实现INotifyPropertyChanged 接口,如下所示:

using System.ComponentModel;
public class DemoItem : INotifyPropertyChanged
{
    private int _age;
    private int _score;
    private string _name;

    public int Age
    {
        get { return _age; }
        set { if (_age != value) { _age = value; OnPropertyChanged("Age"); } }
    }
    public int Score
    {
        get { return _score; }
        set { if (_score != value) { _score = value; OnPropertyChanged("Score"); } }
    }
    public string Name
    {
        get { return _name; }
        set { if (_name != value) { _name = value; OnPropertyChanged("Name"); } }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【讨论】:

  • OP 可以实现protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 来避免OnPropertyChanged("PropertyNameHere")。通过实现 OP 可以使用 OnPropertyChanged() 而不指定任何名称
  • @Nitro.de:没错,如果他使用 C# 5.0+,他可以。他还可以使用已经在 ViewModelBase 类中实现 INPC 的 MVVM 框架。
【解决方案2】:

假设您的集合每 100 毫秒发生巨大变化,我会尝试从集合发送重置通知,以便 DataGrid 知道数据需要刷新。 您需要创建一个 ObservableCollection 派生类,其中包含与此类似的方法

public void NotifyOnReset()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

并在需要时调用它。

附:确保在 UI 线程上调用此方法,或采用其他同步方法。

【讨论】:

  • 致那些不赞成投票的人——您是否曾经检查过数十或数百个 PropertyChanged 事件之间的性能差异,以及当集合项目频繁更改时的单个重置通知?我做到了。有一次,由于数据更改过于频繁,我最终遇到了限制重置。
猜你喜欢
  • 2021-10-20
  • 2017-12-28
  • 2013-09-07
  • 2014-02-01
  • 2012-12-19
  • 2016-09-13
  • 2011-06-27
  • 2012-04-02
相关资源
最近更新 更多