WPF DataGrid Добавление, обновление и удаление с использованием MVVM - PullRequest
11 голосов
/ 06 сентября 2010

Я ищу пример кода / статьи, который продемонстрировал бы WPF DataGrid в действии с шаблоном MVVM для добавления, обновления и удаления записи из базы данных.

У меня есть особые требования для разрешения пользователю вставлять новую записьиспользование DataGrid, а не новой дочерней формы.

Если кто-то может порекомендовать хороший ресурс или предоставить образец для этой конкретной задачи, это было бы мне очень полезно.

Ответы [ 3 ]

2 голосов
/ 12 сентября 2010

Здесь в CodeProject есть статья о шаблоне WPF DataGrid + MVVM:

http://www.codeproject.com/KB/WPF/MVVM_DataGrid.aspx

1 голос
/ 20 октября 2015

Редактировать : Вставить часть, которая соответствует вашему вопросу. Полный текст статьи: http://www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples

В этом примере показано, как использовать DataGrid для выполнения операций CRUD через привязку, когда интеграция с базой данных отделена от уровня доступа к данным (DAL).

Архитектура

Этот пример - простое приложение CRUD, которое позволяет пользователю редактировать элементы в таблице Customers базы данных Northwind. В этом примере есть уровень доступа к данным, который предоставляет методы поиска / удаления / обновления, которые работают с простыми объектами данных, и уровень представления, который адаптирует эти объекты таким образом, чтобы они могли эффективно связываться с WPF Framework. Поскольку мы выполняем только функции 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 не предоставляет интерфейсы или классы, специфичные для инфраструктуры пользовательского интерфейса (например, ObservableCollection). Проблема заключается в том, как связать клиентов, возвращаемых ICustomerDataAccess.GetCustomers, с нашей DataGrid и обеспечить синхронизацию изменений с базой данных.

Мы могли бы связать DataGrid напрямую с нашей коллекцией клиентов, List; однако нам необходимо убедиться, что методы UpdateCustomer и DeleteCustomer в нашем интерфейсе DAL вызываются в соответствующие моменты времени. Один из подходов, который мы могли бы использовать, - это обрабатывать события / команды, предоставляемые DataGrid, чтобы определить, какое действие он только что выполнил или намеревается выполнить в связанной коллекции клиентов. Однако при этом мы будем писать код интеграции, специфичный для DataGrid. Что если мы захотим изменить пользовательский интерфейс для представления ListView и нескольких TextBoxes (подробное представление)? Мы должны были бы переписать эту логику. Кроме того, ни одно из событий DataGrid не соответствует тому, что мы хотим. Есть «Завершающие» события, но нет «Завершенных»; следовательно, данные, видимые обработчикам событий, не находятся в своем зафиксированном состоянии. Лучшим подходом было бы, если бы мы могли адаптировать нашу коллекцию объектов Customer таким образом, чтобы они могли быть привязаны к любому подходящему элементу управления WPF UI с операциями добавления / редактирования / удаления, синхронизированными с базой данных через наш DAL. Обработка операций удаления

Класс ObservableCollection является хорошим кандидатом для наших потребностей в связывании данных. Он предоставляет событие CollectionChanged, которое вызывается всякий раз, когда элементы добавляются или удаляются из коллекции. Если мы скопируем данные наших клиентов в ObservableCollection и свяжем их с DataGrid, мы сможем обработать событие CollectionChanged и выполнить необходимую операцию над DAL. В следующем фрагменте кода показано, как CustomerObjectDataProvider (который определен как ObjectDataProvider в XAML) создает ObservableCollection of CustomerUIObjects. Эти объекты пользовательского интерфейса просто оборачивают свои аналоги объектов данных, чтобы предоставить те же свойства.

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 включает операции Add; однако это событие не вызывается при обновлении элементов в коллекции. Кроме того, когда пользователь добавляет новый элемент в DataGrid, объект изначально добавляется в связанную коллекцию в неинициализированном состоянии, поэтому мы только когда-либо увидим объект со значениями его свойств по умолчанию. Что нам действительно нужно сделать, это определить, когда пользователь заканчивает редактирование элемента в сетке. Обработка обновлений / вставок

Чтобы определить, когда пользователь заканчивает редактирование связанного элемента, нам нужно немного углубиться в сам механизм связывания. DataGrid может выполнять атомарную фиксацию строки, которая в данный момент редактируется; это становится возможным, если связанные элементы реализуют интерфейс IEditableObject, который предоставляет методы BeginEdit, EndEdit и CancelEdit. Обычно объект, реализующий этот интерфейс, возвращается в свое состояние в тот момент, когда метод BeginEdit вызывается в ответ на вызываемый метод CancelEdit. Тем не менее, в этом случае мы не очень заинтересованы в возможности отмены изменений; все, что нам действительно нужно знать, - это когда пользователь закончил редактирование строки. На это указывает, когда 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;
}

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());
}

Приведенный выше код будет обрабатывать операции вставки и обновления.

В заключение, этот метод адаптирует элементы данных и коллекции, предоставляемые DAL, в элементы и коллекции пользовательского интерфейса, которые больше подходят для привязки данных в рамках WPF Framework. Вся логика синхронизации базы данных выполняется обработкой события из этой связанной коллекции; следовательно, нет специального кода WPF DataGrid.

1 голос
/ 06 сентября 2010

Я не знаю ни одной хорошей статьи на эту тему, но не вижу проблемы;пока вы привязываете к ObservableCollection или ListCollectionView, содержащему объекты, у класса которых есть конструктор по умолчанию (я не думаю, что есть другие ограничения), DataGrid будет обрабатывать вещи довольно хорошо.Коллекция, к которой вы привязываете, должна иметь какой-то способ добавления новых элементов, поэтому вам необходимо привязать ее к ICollection или IEditableCollectionView - последний вариант предпочтительнее, так как он имеет определенные параметры для управления созданием элементов - см. AddNew, CanAddNew и т. Д., С которым хорошо работает DataGrid.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...