Search code examples
c#wpfxamldata-bindingdatagrid

WPF ObservableCollection binding to DataGrid not refreshing UI


I currently facing the issue that my DataGrid binding is not refreshing the UI. My ViewModel and Object inherit from INotifyPropertyChanged.

Here is my code:

XAML:

<DataGrid Grid.Row="2" DataContext="{StaticResource MainViewModel}" ItemsSource="{Binding TestCollection, Mode=OneWay}" AutoGenerateColumns="True"/>

ViewModel:

 public class MainViewModel: ViewModelBase
{
    private ObservableCollection<ProductDisplayItem> _testCollection;

    public ObservableCollection<ProductDisplayItem> TestCollection
    {
        get => _testCollection;
        set => SetProperty(ref _testCollection, value);
    }

    private async void SendSearch()
    {
        //MyCode
        .....

        IEnumerable<ProductDisplayItem> displayItems = DisplayItemHelper.ConvertToDisplayItems(products);

        TestCollection = new ObservableCollection<ProductDisplayItem>(displayItems);
    }
}

My Object:

    public class ProductDisplayItem: ViewModelBase
{
    private string _mfrPartNumber;
    private double _unitPrice;
    private int _stock;

    public string MfrPartNumber
    {
        get => _mfrPartNumber;
        set => SetProperty(ref _mfrPartNumber, value);
    }

    public double UnitPrice
    {
        get => _unitPrice;
        set => SetProperty(ref _unitPrice, value);
    }

    public int Stock
    {
        get => _stock;
        set => SetProperty(ref _stock , value);
    }

    public ProductDisplayItem()
    {
        
    }

    public ProductDisplayItem(string mfrp, double unitPrice, int stock)
    {
        MfrPartNumber = mfrp;
        UnitPrice = unitPrice;
        Stock = stock;
    }

}

And my ViewModelBase:

    public abstract class ViewModelBase: IDisposable, INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;
        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }


    public void Dispose()
    {
    }
}

I also tried to add the items to the ObservableCollection instead of creating a new one, but with the same result. I hope anyone can help me with that.

Thanks in advance


Solution

  • The most common cause of such errors is confusion about ViewModel instances: UI elements are bound to one instance, and you are modifying a collection in another instance.

    Since WPF MVVM usually provides for using the main ViewModel in only one instance, try using Singleton.

    Fresh topic with a similar question: Is it a correct approach to create static viewModel in MVVM?

    First implementation option from there:

    1) If:

    • in general, in principle, under no circumstances is it assumed that a ViewModel can have several instances at the assembly level in which it is created;
    • if this does not create any security problems, since the static instance can be accessed by everyone;
    • if static values are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.

    Then in this case it is worth using Singleton.

    Example:

    public class MainWindowViewModel : ViewModelBase
    {
        // The only instance available outside of this class.
        public static MainWindowViewModel Instanse { get; }
            = new MainWindowViewModel();
    
        // All constructors must be MANDATORY HIDDEN.
        private MainWindowViewModel()
        {
            // Some code
        }
    
        // Some code
    }
    

    To get this instance in XAML, x: Static is used.
    You can get the entire instance, or create a binding to a separate property.

    <SomeElement
        DataContext="{x:Static vm:MainWindowViewModel.Instance}"/>
    <SomeElement
        Command="{Binding ButtonCommandEvent,
                          Source={x:Static vm:MainWindowViewModel.Instance}}"/>