WPF (MVVM) обновление всего DataGrid

Есть программа, которая должна выводить некоторые данные в виде таблицы (DataGrid). Т.к. данных планируется много и все они будут зависеть друг от друга, решил использовать MVVM. Так же, чтобы не реализовывать INotifyPropertyChanged и ICommand, подключил Prism.

Есть класс Line, который представляет из себя набор строк:

public class Line
{
    public string ?number { get; set; }
    public string ?type { get; set; }
    public string ?radius { get; set; }
    public string ?tickness { get; set; }
   
}

В модели все данные хранятся в ObservableCollection _lines. По умолчанию там уже есть одна пустая строка. Для взаимодействия с ViewModel ссылку на _lines хранит ReadOnlyObservableCollection lines.

public class M1Model : BindableBase
{
    public readonly ReadOnlyObservableCollection<Line> lines;

    public M1Model()
    {
        lines = new ReadOnlyObservableCollection<Line>(_lines);
    }

    private ObservableCollection<Line> _lines = new ObservableCollection<Line>()
    {
            new Line() {}
    };

    // Добавление строки
    public void AddRow(int index)
    {
        _lines.Add(new Line());

        for (int i = 0; i < _lines.Count; i++)
        {
            _lines[i].number = i.ToString();
        }
    }

    public void RemoveRow(int index)
    {
        if (_lines.Count > 1)
        {
            _lines.RemoveAt(index);
        }
    }
}

В ViewModel для работы с _lines есть ReadOnlyObservableCollection allLines.

public class M1VM : BindableBase
{
    M1Model _model = new M1Model();

    private string _number;
    private string _radius;
    private string _tickness;
    public ReadOnlyObservableCollection<Line> allLines => _model.lines;

    public DelegateCommand<int?> AddRow { get; }
    public DelegateCommand<int?> RemoveRow { get; }

    public M1VM()
    {
        _model.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };
        

        AddRow = new DelegateCommand<int?>(i => { if (i.HasValue) _model.AddRow(i.Value); });
        RemoveRow = new DelegateCommand<int?>(i => { if (i.HasValue) _model.RemoveRow(i.Value); });
    }


    public string number
    {
        get => _number;
        set
        {
            _number = value;
            RaisePropertyChanged();
        }
    }

    public string radius
    {
        get => _radius;
        set
        {
            _radius = value;
            RaisePropertyChanged();
        }
    }

    public string tickness
    {
        get => _tickness;
        set
        {
            _tickness = value;
            RaisePropertyChanged();
        }
    }
}

По личным нуждам всю логику прописывал не для окна, а для Control. Вот его XAML

<UserControl ...>
    <UserControl.DataContext>
        <local:M1VM/>
    </UserControl.DataContext>

    <Grid >
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding allLines}" AutoGenerateColumns="False">

            <DataGrid.InputBindings>
                <KeyBinding Key="Insert" Command="{Binding AddRow}" CommandParameter="{Binding ElementName=dataGrid, Path=SelectedIndex}"/>
                <KeyBinding Key="Delete" Command="{Binding RemoveRow}" CommandParameter="{Binding ElementName=dataGrid, Path=SelectedIndex}"/>
            </DataGrid.InputBindings>

            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" Header="№" Width="40" TextBlock.TextAlignment="Center" TextBlock.FontSize="16"/>
                <DataGridTextColumn Binding="{Binding type, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" Header="Тип поверхности" Width="110" TextBlock.TextAlignment="Center" TextBlock.FontSize="16"/>
                <DataGridTextColumn Binding="{Binding radius, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Радиус" Width="*" TextBlock.TextAlignment="Center" TextBlock.FontSize="16"/>
                <DataGridTextColumn Binding="{Binding tickness, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Толщина" Width="*" TextBlock.TextAlignment="Center" TextBlock.FontSize="16"/>
                <DataGridTextColumn Header="Оптическая сила" Width="*" TextBlock.TextAlignment="Center" TextBlock.FontSize="16"/>
            </DataGrid.Columns>

        </DataGrid>
    </Grid>
    
</UserControl>

Проблема заключается в следующем: когда я добавляю новую строку через метод AddRow у меня обновляется нумерация всех строк (свойство number). Т.е. в первом столбце первой строки должен появится 0, во второй строке - 1. Но, во второй строке единица есть, а вот первая ячейка не обновляется до тех пор, пока ты ее не выделишь двойным кликом.

Мне кажется, проблема заключается в том, что при обновлении коллекции не происходит обновления строк. А PropertyChanged обновляет только конкретную ячейку (свойство). Но как обновить целиком DataGrid я так и не смог разобраться.


Ответы (1 шт):

Автор решения: Cost

Мне кажется, проблема заключается в том, что при обновлении коллекции не происходит обновления строк

ObservableCollection, как и ReadOnlyObservableCollection сообщают только об изменении самой коллекции, то есть о том были ли добавлены в коллекцию новые элементы или были ли какие-либо элементы удалены. Об изменениях свойств самих элементов они не сообщают.

Для оповещения об изменении значения в ячейке DataGrid Вам нужно реализовать INotifyPropertyChanged в классе Line. Ну или создать класс LineViewModel: BindableBase, чтобы не загромождать класс модели.

P.S. Свойства number, radius и tickness класса M1VM у Вас не используются. Привязка ячеек DataGrid происходит к элементам коллекции allLines: классу Line.

→ Ссылка