如何使用 MVVM/PRISM 模式在数据网格上触发 OnPropertyChanged 事件

问题描述 投票:0回答:1

我有一个绑定到视图模型的 DataGrid,它有一个

ItemSource
ObservableCollection
ObservableCollection
又通过 EF /
DbContext
绑定到 SQL Server 数据库。

我可以添加和删除当前使用 Prism

DelegateCommand
(继承自
ICommand
)命令和委托按钮的行。

我想要做的是编辑 DataGrid,当行/单元格失去焦点时自动将值保存回数据库。

我认为这个算法走在正确的轨道上,但是当编辑数据网格内容时,

PropertyChanged
事件没有被触发。

public class StudentEntity(
    string studentId,
    string firstName,
    string lastName,
    int cohortYear,
    string unitCode,
    string? email) : INotifyPropertyChanged
{
    [MaxLength(50)] public Guid Id { get; set; } = Guid.NewGuid();
    [MaxLength(10)] public string StudentId { get; set; } = studentId;
    [MaxLength(50)] public string FirstName { get; set; } = firstName;
    [MaxLength(50)] public string LastName { get; set; } = lastName;
    [MaxLength(100)] public string? Email { get; set; } = email;

    public int CohortYear { get; set; } = cohortYear;

    [MaxLength(10)] public string UnitCode { get; set; } = unitCode;

    public DateTime? CreatedDt { get; set; }= DateTime.UtcNow;

    public event PropertyChangedEventHandler? PropertyChanged;
        
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            // this code does not execute when datagrid values are changed
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        
    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
    {
        // this code does not execute when datagrid values are changed
        if (EqualityComparer<T>.Default.Equals(field, value)) 
            return false;

        field = value;

        OnPropertyChanged(propertyName);
        return true;
    }
}

摘自视图模型:

public class StudentsViewModel : AbstractViewModel // AbstractViewModel inherits from Prism :BindableBase
{
    public ObservableCollection<StudentEntity> StudentsObservableCollection;
    public DelegateCommand<StudentEntity> DeleteStudentCommand { get; }

    public StudentsViewModel()
    {
        StudentsObservableCollection.Clear();
        StudentsObservableCollection.CollectionChanged += StudentsObservableCollection_CollectionChanged;
        DeleteStudentCommand = new DelegateCommand<StudentEntity>(DeleteStudentDelegate);
    }

    private void StudentsObservableCollection_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            var c = e.NewItems.Count;

            foreach (INotifyPropertyChanged added in e.NewItems)
            {
                // this code is triggered when new students added to the Students collection
                added.PropertyChanged += Student_PropertyChanged;
            }
        }
        
        if (e.OldItems == null) 
            return;

        foreach (INotifyPropertyChanged removed in e.OldItems)
        {
            removed.PropertyChanged -= Student_PropertyChanged;
        }
    }
    
    private void Student_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        // this code does not execute on datagrid change
        if (sender is not StudentEntity row)
        {
            return;
        }
        
        SaveData(row);
    }
    
    private async void SaveData(StudentEntity student)
    {
        // save data goes here.
    }
    
    private async void DeleteStudentDelegate(StudentEntity student)
    {
        // delete code goes here (works)
    }
}

XAML:

<UserControl x:Class="ScriptGen.Views.Students.StudentDataGrid"
             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"
             mc:Ignorable="d"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
             
  <d:UserControl.DataContext>
    <d:DesignInstance Type="viewModels:StudentsViewModel" />
  </d:UserControl.DataContext>
  <DataGrid
    CanUserResizeColumns="True"
    AutoGenerateColumns="False"
    Name="StudentsDataGrid"
    DataContext="{Binding }"
    ItemsSource="{Binding StudentsObservableCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <DataGrid.Columns>
      <DataGridTextColumn Header="Student ID" Binding="{Binding StudentId}" />
      <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" />
      <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" />
      <DataGridTextColumn Header="Year" Binding="{Binding CohortYear}" />
      <DataGridTextColumn Header="Unit Code" Binding="{Binding UnitCode}" />
      <DataGridTextColumn Header="Email" Binding="{Binding Email}" />
      <DataGridTemplateColumn>
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <Button
              Cursor="Hand"
              Style="{StaticResource MaterialDesignFlatLightButton}"
              Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                              Path= DataContext.DeleteStudentCommand}"
              CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
              ToolTip="Delete this student's record">
              <materialDesign:PackIcon Kind="Delete" />
            </Button>
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
      </DataGridTemplateColumn>
    </DataGrid.Columns>
  </DataGrid>
  </UserControl>
c# mvvm datagrid prism
1个回答
0
投票

[MaxLength(50)] 公共字符串名字 { get;放; } = 名字;

我看到这里没有人打电话给

SetField
,我不希望
PropertyChanged
被加注。

话虽这么说,Entity Framework Core 可以使用实现

INotifyPropertyChanged
的实体,请查看微软的文档

public class StudentEntity( string firstName ) : INotifyPropertyChanged
{
    [MaxLength(50)] 
    public string FirstName 
    { 
        get => firstName; 
        set => SetField( ref firstName, value );
    };

    // TODO add more properties

    // TODO implement INotifyPropertyChanged
}
© www.soinside.com 2019 - 2024. All rights reserved.