使用MVVM的WPF DataGrid添加、更新和删除的资源

10
我正在寻找一个示例代码/文章,以演示使用MVVM模式在WPF DataGrid中执行从数据库添加、更新和删除记录的操作。
我有一个特定的需求,允许用户使用DataGrid插入新记录而不是使用新的子窗体。
如果有人能够推荐好的资源或为这个特定任务提供样例,那将是对我很大的帮助。
3个回答

2

1
你链接的项目不支持添加或更新,只能通过删除按钮进行删除,而不能使用DataGrid的删除行功能。我也非常沮丧地发现很难找到关于如何在DataGrid中使用MVVM进行CRUD操作的真实案例!我开始觉得MVVM是一个神话。 - SurfingSanta

1

编辑:粘贴了适合您问题的部分。 完整文章:http://www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples

本示例演示如何使用DataGrid通过绑定执行CRUD操作,其中数据库集成通过数据访问层(DAL)解耦。

架构

此示例是一个简单的CRUD应用程序,允许用户编辑Northwind数据库中Customers表中的项目。该示例具有数据访问层,该层公开查找/删除/更新方法,这些方法对简单数据对象进行操作,并且具有呈现层,该层以适当的方式调整这些对象,以便它们可以被WPF框架有效地绑定。因为我们只执行CRUD功能,所以我没有添加业务逻辑层(BLL);如果您是纯粹主义者,则可以添加一个经过的BLL;但是,我觉得它对这个示例的帮助很小。

此架构中的关键类如下所示:

数据访问层提供了一个接口来管理客户数据对象的生命周期。实现此接口的类使用类型化的 DataSet 作为数据库集成层,但是这对于 DAL 的客户端是隐藏的。有了这个层,我们不会直接耦合到数据库模式或生成的数据集模式,也就是说,我们可以更改我们的模式,但仍然向我们的客户提供以下接口:
public interface ICustomerDataAccessLayer
{
    /// Return all the persistent customers
    List<CustomerDataObject> GetCustomers();

    /// Updates or adds the given customer
    void UpdateCustomer(CustomerDataObject customer);

    /// Delete the given customer
    void DeleteCustomer(CustomerDataObject customer);
}
public class CustomerDataObject
{
    public string ID { get; set; }

    public string CompanyName { get; set; }

    public string ContactName { get; set; }
}

正如您所看到的,DAL没有暴露任何UI框架特定的接口或类(例如ObservableCollection)。问题在于如何将ICustomerDataAccess.GetCustomers返回的客户绑定到我们的DataGrid,并确保更改与数据库同步。
我们可以直接将DataGrid绑定到我们的客户集合List;然而,我们需要确保在适当的时间调用DAL接口上的UpdateCustomer和DeleteCustomer方法。我们可能采取的一种方法是处理DataGrid公开的事件/命令,以确定它刚刚执行或打算执行的操作对绑定的客户集合有何影响。但是,在这样做时,我们将编写特定于DataGrid的集成代码。如果我们想要更改UI以呈现ListView和一些TextBoxes(详细信息视图),我们将不得不重新编写此逻辑。此外,没有任何DataGrid事件完全符合我们的要求。有“Ending”事件,但没有“Ended”事件;因此,事件处理程序可见的数据不处于其提交状态。更好的方法是,如果我们可以以这样的方式调整我们的Customer对象集合,使它们可以绑定到任何适当的WPF UI控件,并通过我们的DAL实现添加/编辑/删除操作与数据库同步。处理删除操作。
ObservableCollection类是我们数据绑定需求的一个很好的选择。它公开了一个CollectionChanged事件,每当向集合中添加或删除项时就会触发该事件。如果我们将客户数据复制到ObservableCollection中,并将其绑定到DataGrid,我们可以处理CollectionChanged事件并在DAL上执行所需操作。下面的代码片段显示了CustomerObjectDataProvider(在XAML中定义为ObjectDataProvider)如何构造一个CustomerUIObjects的ObservableCollection。这些UI对象只是简单地包装它们的数据对象对应项以公开相同的属性。
public CustomerObjectDataProvider()
{
    dataAccessLayer = new CustomerDataAccessLayer();
}

public CustomerUIObjects GetCustomers()
{
    // populate our list of customers from the data access layer
    CustomerUIObjects customers = new CustomerUIObjects();

    List<CustomerDataObject> customerDataObjects = dataAccessLayer.GetCustomers();
    foreach (CustomerDataObject customerDataObject in customerDataObjects)
    {
        // create a business object from each data object
        customers.Add(new CustomerUIObject(customerDataObject));
    }

    customers.CollectionChanged += new
      NotifyCollectionChangedEventHandler(CustomersCollectionChanged);

    return customers;
}

void CustomersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Remove)
    {
        foreach (object item in e.OldItems)
        {
            CustomerUIObject customerObject = item as CustomerUIObject;

            // use the data access layer to delete the wrapped data object
            dataAccessLayer.DeleteCustomer(customerObject.GetDataObject());
        }
    }
}

当用户使用DataGrid控件删除一行时,绑定的集合将触发CollectionChanged事件。在事件处理程序中,我们使用封装的数据对象作为参数调用DAL DeleteCustomer方法。
处理删除操作比较简单,但更新或插入呢?您可能认为可以使用相同的方法,因为NotifyCollectionChangedEventArgs.Action属性包括添加操作;然而,当集合中的项目被更新时,此事件不会被触发。此外,当用户向DataGrid添加新项时,该对象最初以未初始化状态添加到绑定集合中,因此我们只能看到具有其默认属性值的对象。我们真正需要做的是确定用户何时完成网格中的项目编辑。 处理更新/插入
为了确定用户何时完成对绑定项的编辑,我们需要深入了解绑定机制本身。DataGrid能够对当前正在编辑的行执行原子提交;这是可能的,如果绑定的项实现了IEditableObject接口,该接口公开了BeginEdit、EndEdit和CancelEdit方法。通常,实现此接口的对象会在调用BeginEdit方法作为响应取消编辑方法被调用时返回到其开始编辑时的状态。然而,在这种情况下,我们并不真正关心能否取消编辑;我们所需要知道的是用户何时完成了一行的编辑。当DataGrid在我们的绑定项上调用EndEdit时,这就表明用户已经完成了编辑。
为了通知CustomerDataObjectProvider,其中一个绑定集合中的对象已经调用了EndEdit,CustomerUIObject实现了IEditableObject,如下所示:
public delegate void ItemEndEditEventHandler(IEditableObject sender);

public event ItemEndEditEventHandler ItemEndEdit;

#region IEditableObject Members

public void BeginEdit() {}

public void CancelEdit() {}

public void EndEdit()
{
    if (ItemEndEdit != null)
    {
        ItemEndEdit(this);
    }
}

#endregion

当向CustomerUIObjects集合添加项目时,此事件将为集合中的所有项目处理,处理程序仅简单地转发事件:

public class CustomerUIObjects : ObservableCollection<CustomerDataObject>
{
    protected override void InsertItem(int index, CustomerUIObject item)
    {
        base.InsertItem(index, item);

        // handle any EndEdit events relating to this item
        item.ItemEndEdit += new ItemEndEditEventHandler(ItemEndEditHandler);
    }

    void ItemEndEditHandler(IEditableObject sender)
    {
        // simply forward any EndEdit events
        if (ItemEndEdit != null)
        {
            ItemEndEdit(sender);
        }
    }

    public event ItemEndEditEventHandler ItemEndEdit;
}

顾客对象数据提供程序现在可以处理此事件以接收有关任何绑定项上调用CommitEdit的通知。然后,它可以调用DAL方法来同步数据库状态:
public CustomerUIObjects GetCustomers()
{
    // populate our list of customers from the data access layer
    CustomerUIObjects customers = new CustomerUIObjects();

    List<CustomerDataObject> customerDataObjects = dataAccessLayer.GetCustomers();
    foreach (CustomerDataObject customerDataObject in customerDataObjects)
    {
        // create a business object from each data object
        customers.Add(new CustomerUIObject(customerDataObject));
    }

    customers.ItemEndEdit += new ItemEndEditEventHandler(CustomersItemEndEdit);
    customers.CollectionChanged += new
      NotifyCollectionChangedEventHandler(CustomersCollectionChanged);

    return customers;
}

void CustomersItemEndEdit(IEditableObject sender)
{
    CustomerUIObject customerObject = sender as CustomerUIObject;

    // use the data access layer to update the wrapped data object
    dataAccessLayer.UpdateCustomer(customerObject.GetDataObject());
}

上述代码将处理插入和更新操作。
总之,该方法将DAL提供的数据项和集合适应为更适合WPF框架内数据绑定的UI项和集合。所有数据库同步逻辑都是通过处理此绑定集合的事件来执行的;因此,没有WPF DataGrid特定的代码。

1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。仅有链接的答案如果链接页面发生更改可能会变得无效。 - IngoAlbers

1

我不知道关于此主题有什么好的文章,但我不认为会有问题;只要绑定到包含具有默认构造函数的对象的 ObservableCollection 或 ListCollectionView(我认为没有其他限制),DataGrid 就会处理得非常好。您绑定到的集合必须有一种添加新项的方法,这就是为什么您需要绑定到 ICollection 或 IEditableCollectionView - 最好使用后者,因为它具有特定的选项来控制项目的创建 - 请参见AddNewCanAddNew等,这些 DataGrid 能够很好地处理。


在我的情况下,问题是确定哪一行被更改/删除。 - Musaab
我不确定我理解你的意思。DataGrid中新项目的工作方式是有一个NewItemPlaceholder(默认情况下具有与任何其他datagrid行相同的结构),当它被聚焦时,会创建一个新的临时数据项,并在提交时(失去焦点,输入键等)将该项插入集合中。如果我没记错的话,所有东西都是硬编码的,所以也许你不能像你想要的那样进行太多更改,但你应该尝试一下,对我来说通常足够了。 - Alex Paven
让我们忘记新项目,来谈论现有项目,我怎样才能删除一行?删除意味着从网格和数据库中删除它,这就是我的问题。 - Musaab
1
这取决于您使用的数据访问技术,但是这里有一个简单的想法:假设您使用对象(而不是 DataTables),并且您有一个从数据库加载项目填充的 ObservableCollection,请将处理程序连接到集合的 CollectionChanged 事件。在处理程序中检查 eventargs 以查看它是哪种操作;如果删除了一个项目,则在 eventargs 的 OldItems 列表中有该项目;从列表中获取(唯一的)对象,将其转换为您的特定类型,并像通常从代码中执行的那样从数据库中删除它。 - Alex Paven
我打算试一试,虽然我听说使用 EventArgs 是一种不好的 MVVM 实践方法,无论如何还是感谢。 - Musaab
那可能是指使用控件公开的事件,尽管在我看来,即使有一些抽象,这也是可以接受的 - 当然,您也可以将其抽象化并将这些事件挂钩到ICommands,但对于某些事件而言,这可能是不切实际的。人们通常将MVVM过度解读; 它只是一组指南。我所说的所有事件都发生在ViewModel内部,因此该方法对MVVM没有任何影响。 - Alex Paven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接