Search code examples
c#wpfmvvmdatagridobservablecollection

Conversion between DbSet and ObservableCollection


Building a WPF App and trying to keep things fairly simple. The intention is that I have a datagrid bound to an Observable collection. There is a frequent task running in the background that will periodicially ADD items to the collection and I'd like these to be immediately displayed on the UI datagrid.

The two problems I've got:

  1. WHen the colletion is updated, the datagrid isn't. If my ServiceLayer knew anything about the UserControl I'd just call Datagrid.Refresh(), but it doesn't so I can't. I understand I need some kind of PropertyChanged event to fire and I can raise a custom event in my code, but how do I make the View respond?

My Xaml - I'll format the datagrid properly later.

<UserControl x:Class="FrontEnd.Views.ItemsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:FrontEnd.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             DataContext="{Binding ViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
        <DataGrid x:Name="Items" 
                  HorizontalAlignment="Left" 
                  Height="auto" 
                  VerticalAlignment="Top" 
                  Width="auto" 
                  AutoGenerateColumns="True"
                  ColumnWidth="*"
                  ItemsSource="{Binding ExistingItems}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Title}" Width="Auto"/>
            </DataGrid.Columns>
        </DataGrid>

    </Grid>
</UserControl>



public ObservableCollection<Item> ExistingItems
        {
            get
            {
                return _existingItems;
            }
            private set
            {
                _existingItems = value;
                //Raise PropertyChangedEvent?
            }
        }

The second issue is in the DataLayer - Item has a foreign key to a Group table and when I display the Items I want to show the name of the Group so I had the following in my DataLayer

ICollection items = _context.Items.Include(i => i.Group);
return items;

But now I want to make Items Observable, I can't do this - I could use

ObservableCollection items = _context.Items.Local;
return items;

to return an ObservableCollection that DOESN'T include the Group entity data but then I'd need to separately grab the Group data and do a code-based join in the ViewModel which I'd rather not do.

return (ObservableCollection<Item>)_context.Items.Include(i => i.Group);

unsurprisingly fails at runtime.

I feel I'm missing something obvious with both issues. Can anyone advise please?


Solution

  • You should either implement the INotifyPropertyChanged interface and raise the PropertyChanged event from the setter of the ExistingItems property, or just add the new items to the existing instance of the ObservableCollection<Item> instead of creating a new one:

    ExistingItems.Clear();
    foreach (vew newItem in collectionFromDal)
        ExistingItems.Add(newItem);
    

    Your DAL should return a collection:

    _context.Items.Include(i => i.Group).ToArray();
    

    or

    new ObservableCollection<Item>(_context.Items.Include(i => i.Group));
    

    Ideally, you would create the ObservableCollection<Item> in the view model in the client app.