2

我想我遗漏了一些简单的东西,但我基本上是在做一个多步骤的方法来获取我的数据。让我通过一个更简单的版本来举例说明我正在做的事情。

  1. 我有一个实现 INotifyPropertyChanged 的​​可观察集合
  2. Observable Collection 属于“POCO”类,它是一个简单的 POCO 类,由以下两个属性组成:

    PersonID int { get; set; }
    Name string { get; set; }
    
  3. 我有一个实体到 sql 数据模型,它映射一个简单的数据库表,该表包含 POCO 类中的相同元值,让我们说一个简单的例子,它具有三个行值:

    PersonID, Name
    1,  Brett
    2,  Emily
    3,  Test
    
  4. 可观察集合在 ModelView 中连接,如下所示:

    ObservableCollection<POCO> _Pocos;
    POCOEntities ee = new POCOEntities();
    
    public ObservableCollection<POCO> POCOs
    {
        get
        {
            if (_Pocos == null)
            {
                List<POCO> mes = this.GetPOCOs();
                _Pocos= new ObservableCollection<POCO>(mes);
            }
    
            return _Pocos;
        }
        set
        {
            _Pocos = value;
    
            OnPropertyChanged("POCOs");
        }
    }
    
    List<POCO> GetPOCOs()
    {
        return ee.vPOCO.Select(p => new POCOView()
            {
                PersonId = p.PersonID,
                Name = p.Name
            }).ToList();
    }
    
  5. 我也有一个像这样连接的当前项目。

    POCO _CurrentPOCO;
    
    public POCO CurrentPOCO
    {
        get { return _CurrentPOCO; }
        set
        {
            _CurrentPOCO = value;
    
            OnPropertyChanged("CurrentPOCO");
        }
    }
    
  6. 4 和 5 是 ModelView 的核心,我将它们连接到数据网格的视图,如下所示:

     <DataGrid x:Name="datagrid" 
          ItemsSource="{Binding POCOs}" 
          CurrentItem="{Binding CurrentPOCO}" />
    
  7. 这是我没有得到的部分,如何近乎实时地更新数据库的实体模型?集合连接得很好并且正在更新,我如何告诉数据库发生了什么?如果我设置了一个像“CellEditEnding”或“SelectionChanged”这样的事件并尝试从我的实体模型中实现一个更新过程,它会在 ModelView 中爆炸。如果我只坚持它背后的代码可以工作,有点,但似乎没有捕捉到“之后”改变的价值。

即使使用 ICommand 属性并在 MVVM 中实现中继命令。这些方法都行不通。所以我很好奇我是不是想多了,他们的某种类型的界面你可以烤进去,这将为你刷新数据库。我可以处理插入文档,然后使用一种方法来填充或刷新数据网格,但我希望能够更改数据网格视图中的值并直接更新数据库。

摘要:以最简单的方式,我只想在更改 datagridview 和 observablecollection 更改时更新数据库,以便两者相互同步。

4

4 回答 4

2

这里有两类变化:

  1. 集合更改,即添加和/或删除项目。要跟踪这些更改,请让您的 VM 监听ObservableCollection'CollectionChanged事件并使用NewItemsOldItems属性来确定要从数据库中添加和/或删除哪些数据。
  2. 您的 POCO 实例之一的属性发生更改,例如您更改了一个人的姓名。像这样的更改不会触发CollectionChanged事件,因为集合本身仍然是相同的。

对于#2,我将为 POCO 类实现一个简单的 Viewmodel 来处理对其属性的更新。毕竟,您的 POCO 应被视为业务对象,不应直接暴露给视图。每个 PocoVM 都持有对单个 POCO 实例的引用。

编辑

我或多或少地添加了我在实验中使用的所有代码,除了存根数据库,因为我不知道你在使用什么以及它是如何工作的。只要它返回项目列表并且您可以告诉它更新单个项目,这并不重要。

XAML 与您的相同,除了我添加了另一个网格(只读)以显示更改被我的MysticalDBLayer接受后。我也去掉了,CurrentItem因为我们将使用PocoVM来跟踪我们正在编辑的项目。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto"/>
        <RowDefinition />
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock>Input-Grid</TextBlock>
    <DataGrid Grid.Row="1"
              ItemsSource="{Binding POCOs}"/>
    
    <TextBlock Grid.Row="2">Readonly-Grid</TextBlock>
    <DataGrid Grid.Row="3"
              ItemsSource="{Binding POCOs, Mode=OneWay}"
              IsReadOnly="True"/>
</Grid>

查看模型(XAML 的 DataContext) 这就是该文件的全部内容。数据库连接可能会根据您使用的内容而有所不同,但基本上我有一个可观察的集合,我以与您相同的方式填充,除了我为每个 POCO 创建一个新的 PocoVM(视图模型)并将新的 PocoVM 添加到ObservableCollection而不是 POCO本身

class VM
{
    ObservableCollection<PocoVM> _pocoCollection = new ObservableCollection<PocoVM>();

    public ObservableCollection<PocoVM> POCOs
    {
        get
        {
            if (_pocoCollection.Count == 0)
            {
                _pocoCollection = new ObservableCollection<PocoVM>();
                IEnumerable<POCO> pocos = MysticalDBLayer.GetItems();
                foreach (POCO poco in pocos)
                {
                    _pocoCollection.Add(new PocoVM(poco));
                }
            }
            return _pocoCollection;
        }
    }
}

最后是 PocoVM 每次您尝试更新其中一个单元格的值时(只有 name 将被更新:可以使用此代码,因为Number只有一个 getter),相应的 setter 将在此类中被调用。在这里,您可以对数据库进行写入,并根据是否成功采取行动。

class PocoVM : INotifyPropertyChanged
{
    private POCO _dataInstance = null;

    public PocoVM(POCO dataInstance)
    {
        _dataInstance = dataInstance;
    }

    public uint Number
    {
        get { return _dataInstance.Number; }
    }

    public string Name
    {
        get { return _dataInstance.Name; }
        set
        {
            if (string.Compare(value, _dataInstance.Name, StringComparison.CurrentCultureIgnoreCase) == 0)
                return;

            if (!MysticalDBLayer.UpdatePoco(_dataInstance, new POCO(_dataInstance.Number, value)))
                return;

            _dataInstance.Name = value;
            OnPropertyChanged("Name");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string property)
    {
        if (PropertyChanged == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
}
于 2013-02-14T14:36:10.260 回答
0

如果我理解正确,您只需要使用ObservableCollection的CollectionChanged事件。

事件参数包含操作(添加/删除...),您也可以获得NewItemsOldItems

因此,您可以跟踪集合的更改并与您的数据库进行同步。我希望我能帮助你。

于 2013-02-11T05:21:06.450 回答
0

创建自己的ObservableCollection覆盖InsertItem()RemoveItem()方法:

public class CustomerCollection : ObservableCollection<Customer>
{
    private DataContext _db;

    public DataContext Db
    {
      get { return _db; }
    }

    public CustomerCollection(IEnumerable<Customer> customers, DataContext context)
      : base(customers)
    {
      _db = context;
    }

    protected override void InsertItem(int index, Customer item)
    {
      this.Db.AddToCustomers(item);
      this.Db.SaveChanges();
      base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
      this.Db.DeleteObject(this[index]);
      this.Db.SaveChanges();
      base.RemoveItem(index);
    }

}
于 2013-02-11T05:30:10.983 回答
-1

这类似于我刚刚在WPF datagrid with MVVM中回答的问题。(因为我是这个网站的新手,所以我会复制答案)

您应该使用ListCollectionView

这是一个示例,展示了如何执行此操作:

1) 创建一个名为ListCollectionViewTest的新 WPF 项目

2)在 MainWindow.xaml.cs 中剪切&粘贴以下内容(应该在 ViewModel 但我太懒了)

    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;

    namespace ListCollectionViewTest
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            private List<Employee> equivalentOfDatabase = new List<Employee>()
                        {
                            new Employee() { FirstName = "John", LastName = "Doe", IsWorthyOfAttention = true },
                            new Employee() { FirstName = "Jane", LastName = "Doe", IsWorthyOfAttention = true },
                            new Employee() { FirstName = "Mr.", LastName = "Unsignificant", IsWorthyOfAttention = false },
                        };

            public ListCollectionView TestList { get; set; }
            public MainWindow()
            {
                DataContext = this;

                // This is all the magic you need -------
                TestList = new ListCollectionView(equivalentOfDatabase);
                TestList.Filter = x => (x as Employee).IsWorthyOfAttention;

                InitializeComponent();
            }

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                MessageBox.Show(equivalentOfDatabase.Aggregate("Employees are: \n\r", (acc, emp) => acc + string.Format("    - {0} {1}\n\r", emp.FirstName, emp.LastName), x => x));
            }
        }

        public class Employee
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public bool IsWorthyOfAttention { get; set; }
        }
    }

3)在 MainWindow.xaml 中剪切并粘贴:

    <Window x:Class="ListCollectionViewTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">

        <StackPanel>
            <DataGrid ItemsSource="{Binding TestList}"
                      RowHeight="22"
                      AutoGenerateColumns="True">
            </DataGrid>
            <Button Content="Show All Employees in DB" Click="Button_Click" />
        </StackPanel>        
    </Window>
于 2013-02-13T19:42:38.840 回答