Search code examples
c#wpfdatagridobjectdataprovider

How to refresh a DataGrid that has been populated by a DataProvider every 1min


I have the following xaml, as you can see the DataGrids are being populated via DataProviders.

<Window x:Class="MobileDeviceAuthenticator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MobileDeviceAuthenticator"
        Title="Device Authorization" Height="381" Width="879" AllowDrop="True">
    <Window.Resources>
        <!-- create an instance of our DataProvider class -->
        <ObjectDataProvider x:Key="MobileManagerDataProvider" ObjectType="{x:Type local:MobileManagerDataProvider}"/>
        <!-- define the method which is invoked to obtain our data -->
        <ObjectDataProvider x:Key="MOBILE_MANAGER" ObjectInstance="{StaticResource MobileManagerDataProvider}" MethodName="GetDevices"/>
        <!-- create an instance of our DataProvider class -->
        <ObjectDataProvider x:Key="MobileRequestDataProvider" ObjectType="{x:Type local:MobileRequestDataProvider}"/>
        <!-- define the method which is invoked to obtain our data -->
        <ObjectDataProvider x:Key="MOBILE_REQUESTS" ObjectInstance="{StaticResource MobileRequestDataProvider}" MethodName="GetDevices"/>
    </Window.Resources>
    <Grid Name="GridContainer" >
        <Grid>
            <DataGrid Name="dgAuthorization" 
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch" 
                      DataContext="{Binding Source={StaticResource MOBILE_MANAGER}}" 
                      ItemsSource="{Binding}" 
                      AutoGenerateColumns="False" >
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Path=DESCRIPTION}" Header="Description" />
                    <DataGridTextColumn Binding="{Binding Path=DEVICE_TYPE}" Header="Device Type" IsReadOnly="True" />
                    <DataGridTextColumn Binding="{Binding Path=DEVICE_ID}" Header="Device ID" IsReadOnly="True" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
        <Grid>
            <DataGrid Name="dgRequest" 
                      SelectionMode="Single" 
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      DataContext="{Binding Source={StaticResource MOBILE_REQUESTS}}"
                      ItemsSource="{Binding}"  
                      AutoGenerateColumns="False" >
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Path=DESCRIPTION}" Header="Description" />
                    <DataGridTextColumn Binding="{Binding Path=DEVICE_TYPE}" Header="Device Type" IsReadOnly="True" />
                    <DataGridTextColumn Binding="{Binding Path=DEVICE_ID}" Header="Device ID" IsReadOnly="True" />
                    <DataGridTextColumn Binding="{Binding Path=REQUEST_DATE}" Header="Request Date" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Grid>
</Window>

Here is the DataProvider class code

public class MobileManagerDataProvider
{
    private MobileManagerDataSetTableAdapters.MOBILE_MANAGERTableAdapter mmAdapter;
    private MobileManagerDataSet mmDataSet;

    public MobileManagerDataProvider()
    {
        mmDataSet = new MobileManagerDataSet();
        mmAdapter = new MobileManagerDataSetTableAdapters.MOBILE_MANAGERTableAdapter();
        mmAdapter.Fill(mmDataSet.MOBILE_MANAGER);

        mmDataSet.MOBILE_MANAGER.MOBILE_MANAGERRowChanged += new MobileManagerDataSet.MOBILE_MANAGERRowChangeEventHandler(AuthenticationRowModified);
        mmDataSet.MOBILE_MANAGER.MOBILE_MANAGERRowDeleted += new MobileManagerDataSet.MOBILE_MANAGERRowChangeEventHandler(AuthenticationRowModified);
    }

    public DataView GetDevices()
    {
        return mmDataSet.MOBILE_MANAGER.DefaultView;
    }

    void AuthenticationRowModified(object sender, MobileManagerDataSet.MOBILE_MANAGERRowChangeEvent e)
    {
        mmAdapter.Update(mmDataSet.MOBILE_MANAGER);
    }
}

In the code-behind I'd like to setup a Timer ticking away every minute refreshing the DataGrids.

The Timer is easy enough, however to refresh the data is eluding me. I've tried some of the following statements.

ObjectDataProvider dataProvider = this.TryFindResource("MobileManagerDataProvider") as ObjectDataProvider;
dataProvider.Refresh();

dgAuthorization.Items.Refresh();

CollectionViewSource.GetDefaultView(dgAuthorization.ItemsSource).Refresh();

To no avail, how can I achieve this?


Solution

  • this is a complete working example, every time you select an item (not same item twice) it will add a new random value as a new line in the DataGrid

    I've responded with an example binding new data to the DataGrid every time you select an item in it, you can expand that case, so each time your collection changes, it will reflect in your DataGrid automatically, so whenever your data changes you just pass to the GridData property they will be updated in the view

    MainWindow.xaml

    <Window x:Class="BindingDataGrid.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:BindingDataGrid"
            Title="MainWindow" Height="350" Width="525">
    
            <Window.DataContext>
                <local:MainViewModel />
            </Window.DataContext>
        <Grid>
            <DataGrid  ColumnWidth="*" 
                        IsReadOnly="True"
                        AutoGenerateColumns="False"
                        SelectionMode="Single" 
                        HorizontalContentAlignment="Center" 
                        ItemsSource="{Binding GridData}"
                        SelectedItem="{Binding SelectedData, UpdateSourceTrigger=PropertyChanged}"
                        CanUserResizeRows="False"
                        ScrollViewer.CanContentScroll="True"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        ScrollViewer.HorizontalScrollBarVisibility="Auto">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Column 01" Binding="{Binding Column1}"   Width="*"/>
                    <DataGridTextColumn Header="Column 02" Binding="{Binding Column2}"   Width="*"/>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    MainWindow.xaml.cs Nothing here, just remove unused references

    using System.Windows;
    
    namespace BindingDataGrid  //`BindingDataGrid` is my namespace
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    }
    

    all references you need in your MainViewModel

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    

    MainViewModel

    public class MainViewModel: INotifyPropertyChanged
    {
        Random _rnd = new Random();
        private MyGridData _selectedData;
        private ObservableCollection<MyGridData> _gridData;
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public MainViewModel()
        {
            var newData = new List<MyGridData>
            {
                new MyGridData { Column1 = "AAAAAA 01", Column2 = "aaaaaaaa 02" },
                new MyGridData { Column1 = "BBBBBB 01", Column2 = "bbbbbbbb 02" },
                new MyGridData { Column1 = "CCCCCC 01", Column2 = "cccccccc 02" },
            };
            GridData = new ObservableCollection<MyGridData>(newData);
        }
    
        public MyGridData SelectedData
        {
            get { return _selectedData; }
            set
            {
                if (value == _selectedData) //if same item selected
                    return;
                _selectedData = value;
                OnPropertyChanged();
    
                DoSomethingWhenValueChanged(); // Will add a new line
            }
        }
        public ObservableCollection<MyGridData> GridData
        {
            get { return _gridData; }
            set
            {
                if (value == _gridData)
                    return;
                _gridData = value;
                OnPropertyChanged();
            }
        }
    
        private void DoSomethingWhenValueChanged()
        {
            var newData = GridData.ToList();
            newData.Add(new MyGridData { Column1 = (_rnd.Next(100)).ToString(), Column2 = (_rnd.Next(1000)).ToString() }); //Add new random item
    
            GridData = new ObservableCollection<MyGridData>(newData);  //update your DataGrid
        }
    }
    
    public class MyGridData
    {
        public string Column1 { get; set; }
        public string Column2 { get; set; }
    }