WPF DataGrid 使用 MVVM 添加、更新和删除
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3649562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF DataGrid Add, Update and Delete using MVVM
提问by fawad
I am looking for a sample code/Article which would demonstrate WPF DataGrid in action with MVVM pattern to add, updated and delete record from database.
我正在寻找一个示例代码/文章,它将演示 WPF DataGrid 使用 MVVM 模式在数据库中添加、更新和删除记录。
I have a specific requirement for allowing user to insert new record using DataGrid not a new child form.
我有一个特定的要求,允许用户使用 DataGrid 而不是新的子表单插入新记录。
If anyone can recommend good resource or provide a sample for that particular task it would be great help for me.
如果有人可以推荐好的资源或为该特定任务提供样本,那对我来说会很有帮助。
回答by Slauma
Here on CodeProject is an article about WPF DataGrid + MVVM pattern:
CodeProject 上有一篇关于 WPF DataGrid + MVVM 模式的文章:
回答by Artur
Edit:Pasted the part, which fits your question. Full article: http://www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples
编辑:粘贴了适合您问题的部分。全文:http: //www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples
This example demonstrates how to use a DataGrid to perform CRUD operations via binding where the database integration is decoupled via a Data Access Layer (DAL).
此示例演示如何使用 DataGrid 通过绑定执行 CRUD 操作,其中数据库集成通过数据访问层 (DAL) 解耦。
The Architecture
架构
This example is a simple CRUD application which allows the user to edit items in the Customers table of the Northwind database. The example has a Data Access Layer, which exposes Find/ Delete/Update methods that operate on simple data objects, and a Presentation Layer that adapts these objects in such a way that they can be bound effectively by the WPF Framework. Because we are only performing CRUD functions, I have not added a Business Logic Layer (BLL); if you are a purist, you could add a pass-through BLL; however, I feel it would add little to this example.
此示例是一个简单的 CRUD 应用程序,它允许用户编辑 Northwind 数据库的客户表中的项目。该示例有一个数据访问层,它公开了对简单数据对象进行操作的 Find/Delete/Update 方法,以及一个表示层,它以可以通过 WPF 框架有效绑定这些对象的方式调整这些对象。因为我们只是执行CRUD功能,所以我没有添加业务逻辑层(BLL);如果您是纯粹主义者,则可以添加直通 BLL;然而,我觉得这对这个例子没什么帮助。
The key classes within this architecture are shown below:
该架构中的关键类如下所示:
The Data Access Layer exposes an interface for managing the lifecycle of the Customer Data Objects. The class which implements this interface uses a typed DataSet as a database integration layer; however, this is hidden from the clients of the DAL. The presence of this layer means that we are not directly coupled to the database schema or the generated dataset schema, i.e., we can change our schema, yet still provide the interface given below to our clients:
数据访问层公开了一个用于管理客户数据对象生命周期的接口。实现该接口的类使用类型化的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; }
}
As you can see, there are no UI framework specific interfaces or classes (such as ObservableCollection) exposed by the DAL. The problem here is how to bind the customers returned by ICustomerDataAccess.GetCustomers to our DataGrid and ensure that changes are synchronised with the database.
如您所见,DAL 没有公开特定于 UI 框架的接口或类(例如 ObservableCollection)。这里的问题是如何将 ICustomerDataAccess.GetCustomers 返回的客户绑定到我们的 DataGrid 并确保更改与数据库同步。
We could bind the DataGrid directly to our customer collection, List; however, we need to ensure that the UpdateCustomer and DeleteCustomer methods on our DAL interface are invoked at the appropriate points in time. One approach that we might take is to handle the events / commands exposed by the DataGrid in order to determine what action it has just performed or intends to perform on the bound customer collection. However, in doing so, we would be writing integration code that is specific to the DataGrid. What if we wanted to change the UI to present a ListView and a number of TextBoxes (details view)? We would have to re-write this logic. Also, none of the DataGrid events quite fit what we want. There are "Ending" events, but no "Ended" events; therefore, the data visible to event handlers is not in its committed state. A better approach would be if we could adapt our collection of Customer objects in such a way that they could be bound to any suitable WPF UI control, with add/edit/remove operations synchronised with the database via our DAL. Handling Delete Operations
我们可以将 DataGrid 直接绑定到我们的客户集合 List;但是,我们需要确保在适当的时间点调用 DAL 接口上的 UpdateCustomer 和 DeleteCustomer 方法。我们可能采取的一种方法是处理 DataGrid 公开的事件/命令,以确定它刚刚对绑定的客户集合执行或打算执行的操作。但是,这样做时,我们将编写特定于 DataGrid 的集成代码。如果我们想更改 UI 以显示一个 ListView 和多个 TextBoxes(详细信息视图)怎么办?我们将不得不重新编写这个逻辑。此外,没有一个 DataGrid 事件完全符合我们的要求。有“Ending”事件,但没有“Ended”事件;因此,事件处理程序可见的数据并不处于已提交状态。更好的方法是,如果我们可以调整我们的 Customer 对象集合,使它们可以绑定到任何合适的 WPF UI 控件,并通过我们的 DAL 与数据库同步添加/编辑/删除操作。处理删除操作
The ObservableCollection class is a good candidate for our data binding needs. It exposes a CollectionChanged event which is fired whenever items are added or removed from the collection. If we copy our customer data into an ObservableCollection and bind this to the DataGrid, we can handle the CollectionChanged event and perform the required operation on the DAL. The following code snippet shows how the CustomerObjectDataProvider (which is defined as an ObjectDataProvider in the XAML) constructs an ObservableCollection of CustomerUIObjects. These UI objects simply wrap their data object counterparts in order to expose the same properties.
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());
}
}
}
When a user deletes a row with the DataGrid control, the CollectionChanged event is fired on the bound collection. In the event handler, we invoke the DAL DeleteCustomer method with the wrapped data object passed as the parameter.
当用户使用 DataGrid 控件删除一行时,会在绑定的集合上触发 CollectionChanged 事件。在事件处理程序中,我们使用作为参数传递的包装数据对象调用 DAL DeleteCustomer 方法。
Handling delete operations is relatively straightforward, but how about updates or insertions? You might think that the same approach can be used, the NotifyCollectionChangedEventArgs.Action property does include Add operations; however, this event is not fired when the items within the collection are updated. Furthermore, when a user adds a new item to the DataGrid, the object is initially added to the bound collection in a non-initialized state, so we would only ever see the object with its default property values. What we really need to do is determine when the user finishes editing an item in the grid. Handling Updates / Inserts
处理删除操作相对简单,但是更新或插入呢?您可能认为可以使用相同的方法,NotifyCollectionChangedEventArgs.Action 属性确实包含添加操作;但是,更新集合中的项目时不会触发此事件。此外,当用户向 DataGrid 添加新项目时,该对象最初以非初始化状态添加到绑定集合中,因此我们只会看到具有默认属性值的对象。我们真正需要做的是确定用户何时完成编辑网格中的项目。处理更新/插入
To determine when a user finishes editing a bound item, we need to delve a little deeper into the binding mechanism itself. The DataGrid is able to perform an atomic commit of the row which is currently being edited; this is made possible if the bound items implement the IEditableObject interface which exposes BeginEdit, EndEdit, and CancelEdit methods. Typically, an object implementing this interface would return to its state at the point when the BeginEdit method was called as a response to the CancelEdit method being invoked. However, in this instance, we are not really concerned about being able to cancel edits; all we really need to know is when the user has finished editing a row. This is indicted when the DataGrid invokes EndEdit on our bound item.
要确定用户何时完成对绑定项目的编辑,我们需要更深入地研究绑定机制本身。DataGrid 能够对当前正在编辑的行执行原子提交;如果绑定的项目实现了公开 BeginEdit、EndEdit 和 CancelEdit 方法的 IEditableObject 接口,则这成为可能。通常,实现此接口的对象将在调用 BeginEdit 方法作为对被调用的 CancelEdit 方法的响应时返回其状态。但是,在这种情况下,我们并不真正关心能够取消编辑;我们真正需要知道的是用户何时完成了一行的编辑。当 DataGrid 在我们的绑定项上调用 EndEdit 时,会指示这一点。
In order to notify the CustomerDataObjectProvider that EndEdit has been invoked on one of the objects in the bound collection, the CustomerUIObject implements IEditableObject as follows:
为了通知 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
When items are added to the CustomerUIObjects collection, this event is handled for all the items in the collection, with the handler simply forwarding the event:
当项目被添加到 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;
}
The CustomerObjectDataProvider can now handle this event to receive the notification of CommitEdit being invoked on any of the bound items. It can then invoke the DAL methods to synchronise the database state:
CustomerObjectDataProvider 现在可以处理此事件以接收在任何绑定项目上调用 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());
}
The above code will handle both insert and update operations.
上面的代码将处理插入和更新操作。
In conclusion, this method adapts the data items and collection provided by the DAL into UI items and collections which are more appropriate for data binding within the WPF Framework. All database synchronisation logic is performed by handling event from this bound collection; therefore, there is no WPF DataGrid specific code.
总之,该方法将 DAL 提供的数据项和集合改编为更适合 WPF 框架内数据绑定的 UI 项和集合。所有数据库同步逻辑都是通过处理来自这个绑定集合的事件来执行的;因此,没有 WPF DataGrid 特定的代码。
回答by Alex Paven
I don't know of any good articles on the subject, but I don't see the problem; as long as you bind to an ObservableCollection or ListCollectionView containing objects whose class has a default constructor (I don't think there are other restrictions), the DataGrid will handle things pretty well. The collection you bind to must have some way of adding new items, which is why you need to bind to an ICollection or IEditableCollectionView - the latter is preferred, as it has specific options for controlling the creation of items - see AddNew
, CanAddNew
etc, which the DataGrid works well with.
我不知道关于这个主题的任何好文章,但我没有看到问题;只要您绑定到 ObservableCollection 或 ListCollectionView 包含的对象的类具有默认构造函数(我认为没有其他限制),DataGrid 就会很好地处理事情。后者是首选,因为它有用于控制项目的创建特定选项- -看你绑定到必须增加新的项目的一些方法,这就是为什么你需要绑定到一个ICollection的或IEditableCollectionView收集AddNew
,CanAddNew
等等,其中, DataGrid 可以很好地使用。