如何使用MVVM更新Datagrid?

时间:2017-02-25 12:08:46

标签: c# wpf xaml mvvm datagrid

我已经搜查并且已经到了一堵砖墙。关于如何做到这一点似乎有很多问题和答案,但是我无法找到具体的我可以实现的任何内容(显然是我理解的问题)。

我希望能够更新多个Datagrids,并且假设您不应该为控件命名,我很想知道如何让它工作。

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

所以我有XAML:

<i:Interaction.Triggers>
     <i:EventTrigger EventName="RowEditEnding">
         <i:InvokeCommandAction Command="{Binding CanExecuteChanged}" />
     </i:EventTrigger>
</i:Interaction.Triggers>

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

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

1 个答案:

答案 0 :(得分:3)

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

编辑:在ViewModel中更改了foreach以保持一致性。

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

您似乎对数据绑定的工作方式存在误解。您指的是您在代码中所做的事情,但绑定表达式通常指向视图的DataContext上的属性,MVVM中的属性应设置为您的ViewModel。有一个很好的教程here可以帮助您开始绑定。我还推荐了那个帖子的后续帖子,当我开始使用WPF时,他们给了我很多帮助。

现在了解DataGrid的情况。首先,here是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>

代码背后:

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

            DataContext = new MyViewModel();
        }
    }
}

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

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.
    }
}

我的视图模型中有List<Person>类型的属性。 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();
        }
    }
}

这里要注意的重要一点是 INotifyPropertyChanged ,这个界面对MVVM和WPF数据绑定一般非常重要。正确实现后,只要其中一个属性发生更改,就会导致对象发布PropertyChanged事件。这告诉任何绑定的WPF控件它们应该获取新值,并且还允许ViewModel监视它们的更改。因此,在视图模型中,我们将事件处理程序附加到People上的CollectionChanged事件,然后为添加到集合中的每个项目附加PropertyChanged事件处理程序。

只要在集合中添加,删除或替换项目,就会调用CollectionChanged事件,并从旧项目中删除PropertyChanged处理程序,并将处理程序添加到新项目中。重要的是要记住删除处理程序,或者从集合中删除的项目可能无法正确地进行垃圾回收。

每次person_PropertyChanged个对象的属性发生更改时,都会调用Person方法。在person_PropertyChanged中,我们然后调用方法来更新数据库,传递更新的行(在本例中为Person),如下所示:

//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.
}

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

假设用户更改了&#34; Bob&#34;的person_PropertyChanged列。用户点击进入或离开单元格Age表示&#34; Bob&#34;将从45更改为37.这将导致Age对象引发Person,这将调用Person中的PropertyChanged方法。然后person_PropertyChanged将调用MyViewModel,您可以将代码保存在更新的Person行中。

如果您有任何疑问,请与我们联系!