0

I know there is a lot of questions that can look like this, but i don't realy find anyone answer to my problem here and in another forums.

So, I'm relatively new to WPF and I'm testing data binding, but I'm getting a trouble that data don't get values updated when values of a ObservableCollection get changed.

I will put my exemple were.

Main.xaml

<Window x:Class="TestWPF.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:TestWPF"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid x:Name="MyGrid">

</Grid>
</Window>

Main.xaml.cs

using System.Windows;

namespace TestWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Stand stand = new Stand("Best Seller Stand");
        stand.cars.Add(new Car()
        {
            ID = "1",
            Brand = "BMW",
            CarNumber = 165,
            HaveRadio = true
        });
        stand.cars.Add(new Car()
        {
            ID = "2",
            Brand = "Toyota",
            CarNumber = 421,
            HaveRadio = true
        });
        stand.cars.Add(new Car()
        {
            ID = "4",
            Brand = "FIAT",
            CarNumber = 312,
            HaveRadio = false
        });
        stand.cars.Add(new Car()
        {
            ID = "3",
            Brand = "Ferrari",
            CarNumber = 12,
            HaveRadio = true
        });

        MyGrid.Children.Add(stand.GetCatalog());
    }
  }
}

Car.cs

using System;

namespace TestWPF
{
public class Car : IComparable, IComparable<int>
{
    public string ID { get; set; }
    public string Brand { get; set; }
    public int CarNumber { get; set; }
    public bool HaveRadio { get; set; }
    public void GerateRandomCarNumber()
    {
        CarNumber = new Random().Next(int.MinValue, int.MaxValue);
    }
    public int CompareTo(int other)
    {
        return CarNumber.CompareTo(other);
    }

    public int CompareTo(object obj)
    {
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    }
  }
}

Stand.cs

using System.Collections.Generic;
using System.Linq;

namespace TestWPF
{
public class Stand
{
    public Stand(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
    public SortedSet<Car> cars { get; set; } = new SortedSet<Car>();
    public Car BestChoice
    {
        get
        {
            return cars.First();
        }
    }
    public StandCatalog Catalog { get; set; } = null;
    public StandCatalog GetCatalog()
    {
        if (Catalog == null)
            Catalog = new StandCatalog(this);
        return Catalog;
    }
  }
}

StandCatalog.xaml (UserControl)

<UserControl x:Class="TestWPF.StandCatalog"
         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:TestWPF"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Vertical">
    <Label Name="StandName" Content="{Binding Model.Name}" Margin="10"/>
    <Label Name="CarBrand"  Content="{Binding Model.BestChoice.Brand}" Margin="10"/>
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding CatalogCar}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
            <DataGridTextColumn Header="Brand" Binding="{Binding Brand}"/>
            <DataGridTextColumn Header="Car Number" Binding="{Binding CarNumber}"/>
            <DataGridCheckBoxColumn Header="Have Radio" Binding="{Binding HaveRadio}"/>
        </DataGrid.Columns>
    </DataGrid>
    <Button Content="Gerate Random Number" Click="btn_GerateRandomNumber"/>
</StackPanel>
</UserControl>

StandCatalog.xaml.cs

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace TestWPF
{
/// <summary>
/// Interaction logic for StandCatalog.xaml
/// </summary>
public partial class StandCatalog : UserControl
{
    public Stand Model { get; init; }
    public ObservableCollection<Car> CatalogCar { get; set; }
    public StandCatalog()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public StandCatalog(Stand model) : this()
    {
        Model = model;
        CatalogCar = new ObservableCollection<Car>(Model.cars);
    }

    private void btn_GerateRandomNumber(object sender, RoutedEventArgs e)
    {
        foreach (var item in Model.cars)
        {
            item.GerateRandomCarNumber();
        }
    }
  }

}

So I get this aplication: When Open

But when I click on the button to gerate random number, the datagrid don't refresh and the label (Name="CarBrand") don't change either... Doesn't data binding refresh the UI when the elements changed its value?

I know that the value changed because when i reorder the datagrid I get this: enter image description here

Can anyone help me?

Another question, I'm using the class Stand as a Model of the StandCatalog (view/controller), what is the best way to use the SortedSet and the ObservableCollection together? Or should I use a SortedSet in the model?

  • You have not implemented INotifyPropertyChanged for your properties. The UI does not get notified when the property changed because there is no feedback. Implement INotifyPropertyChanged and call PropertyChanged in all your property setters and it should work. – Shakti Prakash Singh Oct 28 '21 at 01:34

1 Answers1

0

Option 1: INotifyPropertyChanged

The Car class should implement the INotifyPropertyChanged interface to inform targets when a property changes.

public class Car : IComparable, IComparable<int>, INotifyPropertyChanged
{
    private int _carNumber;

    public string ID { get; set; }
    public string Brand { get; set; }

    public int CarNumber
    {
        get => _carNumber;
        set
        {
            if (_carNumber == value) return;

            _carNumber = value;
            OnPropertyChanged();
        }
    }

    public bool HaveRadio { get; set; }

    public void GerateRandomCarNumber() { CarNumber = new Random().Next(int.MinValue, int.MaxValue); }
    public int CompareTo(int other) { return CarNumber.CompareTo(other); }

    public int CompareTo(object obj)
    {
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

Option 2: DependencyProperty

Define CarNumber property as a DependencyProperty. The infrastructure will handle the changes.

public class Car : DependencyObject, IComparable, IComparable<int>
{
    public static readonly DependencyProperty
        CarNumberProperty = DependencyProperty.Register("CarNumber", typeof(int), typeof(Car));

    public string ID { get; set; }
    public string Brand { get; set; }

    public int CarNumber
    {
        get => (int)GetValue(CarNumberProperty);
        set => SetValue(CarNumberProperty, value);
    }

    public bool HaveRadio { get; set; }

    public void GerateRandomCarNumber() { CarNumber = new Random().Next(int.MinValue, int.MaxValue); }
    public int CompareTo(int other) { return CarNumber.CompareTo(other); }

    public int CompareTo(object obj)
    {
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    }
}
Hadi Fooladi Talari
  • 1,180
  • 1
  • 13
  • 36
  • 1
    [See here](https://stackoverflow.com/q/291518/1136211) why Option 2 is typically not used in view models. – Clemens Oct 28 '21 at 06:44