1

I have certain datagrid which I need to "refresh" every... lets say 1 min.

Is a timer the best option?

    public PageMain()
    {
        InitializeComponent();
        DataGridFill();
        InitTimer();
    }

    private void InitTimer()
    {
        disTimer = new Timer(new TimeSpan(0, 1, 0).TotalMilliseconds);
        disTimer.Elapsed += disTimer_Elapsed;
        disTimer.Start();
    }

    void disTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        DataGridFill();
    }

    private void DataGridFill()
    {
        var items = GetItems(1);
        ICollectionView itemsView =
            CollectionViewSource.GetDefaultView(items);

        itemsView.GroupDescriptions.Add(new PropertyGroupDescription("MyCustomGroup"));
        // Set the view as the DataContext for the DataGrid
        AmbientesDataGrid.DataContext = itemsView;
    }

Is there a less "dirty" solution?

apacay
  • 1,702
  • 5
  • 19
  • 41
  • what do you mean by 'refresh'? do you want the data in the grid to be updated every 1 minute? – vlad Oct 16 '12 at 16:35
  • yup, that exactly... here, let me edit and rephrase – apacay Oct 16 '12 at 16:52
  • 5
    The best way to "Refresh" a DataGrid is to bind it to a collection of items, and update the source collection of items every X minutes. This way you never have to reference the DataGrid itself, so your UI logic and application logic stay separated, and if your refresh takes a while you can run it on a background thread without locking up your UI. – Rachel Oct 16 '12 at 17:00
  • The thing is that since this is a Timer it begins an extra thread. so changing the context or the ItemSource throws an InvalidOperationException... Im right now looking into `BackgroundWorker`. I guess it may be helpful. – apacay Oct 16 '12 at 17:10
  • @Rachel You've said *"update the source collection of items every X minutes"* How should I do that? – apacay Oct 16 '12 at 17:15
  • 2
    @apacay Use a `Timer` or a `DispatcherTimer`. You can't update the collection directly from another thread, but you can get your data into a temporary collection on another thread, then update the data in your bound collection on the main thread. – Rachel Oct 16 '12 at 18:05
  • @Rachel could you write an example as an answer? In paralel I'll try what I understood of that. Thanks! – apacay Oct 16 '12 at 19:58
  • Could you please clarify your tags and your title? WPF and Silverlight might yield different answers, whether it's one of them or both. – jv42 Oct 17 '12 at 07:34

2 Answers2

2

The best way to "Refresh" a DataGrid is to bind it to a collection of items, and update the source collection of items every X minutes.

<DataGrid ItemsSource="{Binding MyCollection}" ... />

This way you never have to reference the DataGrid itself, so your UI logic and application logic stay separated, and if your refresh takes a while you can run it on a background thread without locking up your UI.

Because WPF can't update objects created on one thread from another thread, you may want to get your data and store in a temporary collection on a background thread, then update your bound collection on the main UI thread.

For the timing bit, use a Timer or possibly a DispatcherTimer if needed.

var timer = new System.Windows.Threading.DispatcherTimer();
timer.Tick += Timer_Tick;
timer.Interval = new TimeSpan(0,1,0);
timer.Start();


private void Timer_Tick(object sender, EventArgs e)
{
    MyCollection = GetUpdatedCollectionData();
}
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Worth pointing out that you need a collection that supports INotifyPropertyChanged, such as ObservableCollection – weston Oct 16 '12 at 20:24
  • @weston that's not entirely accurate `INotifyPropertyChanged` will work for items in the collection if they change, it also needs to implement `INotifyCollectionChanged` to cater for items being added/removed from the collection itself. You are correct in suggesting `ObservableCollection` though. – Trevor Pilley Oct 16 '12 at 20:33
  • @TrevorPilley good point. Though thinking about it, if you change the whole list, like Rachel is, you can use anything. I.e. if the property MyCollection fires notifications, then it could be any IEnumerable. But I prefer to have a readonly IEnumerable property, with a ObservableCollection behind. – weston Oct 16 '12 at 20:49
  • Yeah, if you replace the entire list that the grid is bound to, the data binding is going to raise the datasource changed event (or whatever the WPF equivalent of that is) which would cause the whole grid to refresh anyway. – Trevor Pilley Oct 16 '12 at 20:52
  • 2
    Typically, once you have created your ItemsSource it is better to update the data in the rows that have changed rather than changing the entire DataSource. Otherwise, you lose the context of the selected row. You also have to deal with the UI penalty for redraw (even if it is subtle). – Xcalibur37 Oct 16 '12 at 22:36
  • You could also explore using a duplex WCF service or some other broadcast mechanism to push updates down to the client view model. – Myles J Oct 17 '12 at 08:43
1

My prefered approach:

public sealed class ViewModel
{
    /// <summary>
    /// As this is readonly, the list property cannot change, just it's content so
    /// I don't need to send notify messages.
    /// </summary>
    private readonly ObservableCollection<T> _list = new ObservableCollection<T>();

    /// <summary>
    /// Bind to me.
    /// I publish as IEnumerable<T>, no need to show your inner workings.
    /// </summary>
    public IEnumerable<T> List { get { return _list; } }

    /// <summary>
    /// Add items. Call from a despatch timer if you wish.
    /// </summary>
    /// <param name="newItems"></param>
    public void AddItems(IEnumerable<T> newItems)
    {            
        foreach(var item in newItems)
        {
            _list.Add(item);
        }
    }

    /// <summary>
    /// Sets the list of items. Call from a despatch timer if you wish.
    /// </summary>
    /// <param name="newItems"></param>
    public void SetItems(IEnumerable<T> newItems)
    {
        _list.Clear();
        AddItems(newItems);
    }
}

Don't like lack of decent AddRange/ReplaceRange in ObservableCollection<T>? Me neither, but here is an descendant to ObservableCollection<T> to add a message efficient AddRange, plus unit tests:

ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?

Community
  • 1
  • 1
weston
  • 54,145
  • 21
  • 145
  • 203