如何使用 MVVM 更新 Datagrid?

Kyl*_*Ren 4 c# wpf xaml datagrid mvvm

我已经搜索并来到了一堵砖墙。关于如何做到这一点似乎有很多问题和答案,但我找不到任何我可以具体实施的东西(显然我的理解有问题)。

我希望能够更新几个 Datagrids 并脱离你不应该命名你的控件的前提,我不知道如何让它工作。

所以到目前为止我能够想出的是使用System.Windows.Interactivity程序集。但我不知道如何实现背后的代码(我当然可以展示我试图让它工作的所有代码,但不幸的是它只会使帖子变得混乱,所以我不包括它)。我尽可能多地研究实施ICommand.

所以我有 XAML:

<i:Interaction.Triggers>
     <i:EventTrigger EventName="RowEditEnding">
         <i:InvokeCommandAction Command="{Binding CanExecuteChanged}" />
     </i:EventTrigger>
</i:Interaction.Triggers>
Run Code Online (Sandbox Code Playgroud)

但我似乎无法获得背后的代码,以便能够在RowEditEnding完成时通知并能够使用新数据更新数据库。

因此,考虑到 MVVM 模型,我该如何触发事件以便更新数据库?

Bra*_*mer 10

EDIT2:将 People 改为 ObservableCollection 而不是 List。添加了 CollectionChanged 事件以处理从集合中的 Person 对象附加/删除 PropertyChanged 事件。

编辑:为了一致性在 ViewModel 中更改了 foreach。

首先,Model-View-ViewModel(MVVM) 意味着你应该有尽可能少的代码,实际上最好没有。相反,UI 的逻辑应该在 xaml(视图)中完成,而数据的逻辑应该在模型中完成,并将数据组织成可呈现的形式应该通过视图模型完成,这是一个单独的文件对 WPF 或 UI 一无所知。

您似乎也对数据绑定的工作方式有误解。您指的是您在后面的代码中所做的事情,但绑定表达式通常指向您的视图上的属性DataContext,在 MVVM 中应将其设置为您的 ViewModel。这是一个很好的教程在这里,可以让你开始结合。我还推荐了该帖子的后续帖子,当我开始使用 WPF 时,它们对我帮助很大。

现在来看看 DataGrid 的情况。首先,这里有一个关于 WPF DataGrid 的好教程。接下来,您声明要在更新行后更新数据库。以下是如何以 MVVM 风格执行此操作的示例:

假设你有一个像这样的 DataGrid 视图:

<UserControl x:Class="MyProject.MyView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid ItemsSource="{Binding People, Mode=OneWay}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
                <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
                <DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

后面的代码:

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

            DataContext = new MyViewModel();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,后面的代码几乎是空的。只是默认代码,加上DataContext = new MyViewModel();. 正如我之前提到的,您的视图的 DataContext 应该是您的视图模型。

MyViewModel 看起来像这样:

public class MyViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Impl
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    private ObservableCollection<Person> m_people;
    public ObservableCollection<Person> People
    {
        get { return m_people; }
        private set
        {
            if (value == m_people)
                return;

            m_people = value;
            OnPropertyChanged();
        }
    }

    public MyViewModel()
    {
        m_people = new ObservableCollection<Person>();
        m_people.CollectionChanged += m_people_CollectionChanged;
        m_people.Add(new Person() { FirstName = "Bob", LastName = "Brown", Age = 45 });
        m_people.Add(new Person() { FirstName = "Sarah", LastName = "Smith", Age = 25 });
    }

    private void m_people_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count > 0)
        {
            foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
            {
                item.PropertyChanged += people_PropertyChanged;
            }
        }
        if (e.OldItems != null && e.OldItems.Count > 0)
        {
            foreach (INotifyPropertyChanged item in e.OldItems.OfType<INotifyPropertyChanged>())
            {
                item.PropertyChanged -= people_PropertyChanged;
            }
        }
    }
    //Property Changed will be called whenever a property of one of the 'Person'
    //objects is changed.
    private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var row = sender as Person;
        SaveData(row);
    }

    private void SaveData(Person row)
    {
        //Save the row to the database here.
    }
}
Run Code Online (Sandbox Code Playgroud)

List<Person>我的视图模型中有一个 type 属性。 Person看起来像这样:

public class Person : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Impl
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    private string m_firstName;
    public string FirstName
    {
        get { return m_firstName; }
        set
        {
            if (value == m_firstName)
                return;

            m_firstName = value;
            OnPropertyChanged();
        }
    }

    private string m_lastName;
    public string LastName
    {
        get { return m_lastName; }
        set
        {
            if (value == m_lastName)
                return;

            m_lastName = value;
            OnPropertyChanged();
        }
    }

    private int m_age;
    public int Age
    {
        get { return m_age; }
        set
        {
            if (value == m_age)
                return;

            m_age = value;
            OnPropertyChanged();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里要注意的重要一点是INotifyPropertyChanged,这个接口通常对 MVVM 和 WPF 数据绑定非常重要。如果实施得当,它会导致对象在PropertyChanged其属性之一发生更改时发布事件。这会告诉任何绑定的 WPF 控件它们应该获取新值,并且还允许您的 ViewModel 监视它们的更改。因此,在视图模型中,我们将事件处理程序附加到CollectionChangedPeople上的事件,然后负责PropertyChanged为添加到集合中的每个项目附加事件处理程序。

CollectionChanged每当在集合中添加、删除或替换项目时,都会调用该事件,并将PropertyChanged从旧项目中删除处理程序并将处理程序添加到新项目中。重要的是要记住删除处理程序,否则从集合中删除的项目可能不会被正确地垃圾收集。

person_PropertyChanged每次Person对象之一的属性更改时都会调用该方法。在person_PropertyChanged我们然后调用方法来更新数据库,经过更新的行(人在这种情况下),如下图所示:

//Property Changed will be called whenever a property of one of the 'Person'
//objects is changed.
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    var row = sender as Person;
    SaveData(row);
}

private void SaveData(Person row)
{
    //Save the row to the database here.
}
Run Code Online (Sandbox Code Playgroud)

我上面显示的网格中的每一行代表一个人对象。每当用户更改网格中某个单元格的Person值时,该行所代表的对象的相应属性也将被更新,这将触发一个PropertyChanged事件,并调用person_PropertyChanged.

假设用户将Age“Bob”的列更改为 37。在用户点击 Enter 或移出单元格后,代表“Bob”AgePerson对象的 Age 属性将从 45 更改为 37。这将导致该Person对象升高PropertyChanged,这将调用person_PropertyChanged的方法MyViewModelperson_PropertyChanged然后将调用SaveData哪个是您将代码保存到数据库的更新的 Person 行的位置。

如果您有任何问题,请告诉我!