1

I'am trying to bind a ObservableCollection list to a datagrid.

The Values in the list change every 100ms. I want that the grid automatically refreshs if a value changed.

Here is a little demo project to make it work. But without the refresh UI button, nothing works right.

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>

What do I need to change, to make it work?

Philipp Nies
  • 945
  • 4
  • 20
  • 38
  • 3
    DemoItem-Class has to implement the [INotifyPropertyChanged](https://msdn.microsoft.com/de-de/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx) Interface, thats all. Read about it here and on MSDN to understand whats happening. In short: Whenever a property is set you've to call the `PropertyChanged`-Event which is handled by your Collection. And please do me a favor and dont write mix-code with german and english – Nitro.de Jun 29 '16 at 14:06
  • @Nitro.de is correct, DemoItem needs to implement INotifyPropertyChanged. Here's an answer on SO: http://stackoverflow.com/q/22580623/424129 – 15ee8f99-57ff-4f92-890c-b56153 Jun 29 '16 at 14:08
  • `ObservableCollection` notifies only if an element has been added or removed, not if an existing element has been changed. – mechanic Jun 29 '16 at 14:47

2 Answers2

3

ObservableCollection notifies the UI about changes (raises a CollectionChanged event) only if an element has been added or removed, not if an existing element has been changed.

To track changes in collection's elements, like Nitro.de and Ed Plunkett suggested, the element's class should implement INotifyPropertyChanged interface, like this:

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));
        }
    }
}
mechanic
  • 761
  • 6
  • 11
  • OP could implement `protected void OnPropertyChanged([CallerMemberName] string propertyName = null)` to avoid `OnPropertyChanged("PropertyNameHere")`. With the implementation OP could use `OnPropertyChanged()` without any name specified – Nitro.de Jun 30 '16 at 05:49
  • @Nitro.de: that's true, he could, if he used C# 5.0+. He could also use an MVVM framework that has the INPC already implemented in a ViewModelBase class. – mechanic Jun 30 '16 at 14:54
-2

Assuming your collection changes dramatically every 100 ms, I'd try sending Reset notification from the collection so DataGrid would know that the data needs to be refreshed. You need to create an ObservableCollection derivative containing method similar to this one

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

and invoke it whenever you need.

P.S. Make sure you invoke this method on the UI thread, or employ another syncrhonization approach.

Alex Seleznyov
  • 905
  • 6
  • 18
  • To those who downvote - have you ever checked performance difference between tens or hundreds of PropertyChanged events, and single Reset notification when collection items changes very frequently? I did. Once case I ended up with throttled Reset as data were changing a way too frequent. – Alex Seleznyov Jul 06 '16 at 20:43