How to correctly handle the CollectionChanged event of datagrid?

扶醉桌前 提交于 2019-12-11 10:38:34

问题


I initially asked a question related to dataGrid insert/delete/save operations with Entity Framework here However I was not able to totally get the suggested approach to work. Here's my approach so far:

ViewModel constructor

public DeviceDatabaseViewModel()
{
    LoadData();
}

LoadData

private void LoadData()
{
    _context.Devices.Load();
    _devices = _context.Devices.GetLocal();

    _devices.CollectionChanged += Device_CollectionChanged;

    DeviceCollectionView = (CollectionView)new CollectionViewSource { Source = _devices }.View;
}

Event

private bool _isAddedbyApp = false;

private void Device_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    var device = new Device();
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            foreach (var item in e.NewItems)
            {
                var entity = item as Device;
                if (entity == null) continue;
                if (_isAddedbyApp) continue;

                _isAddedbyApp = true;
                _context.Devices.Add(entity);
                _isAddedbyApp = false;
            }
            break;
        case NotifyCollectionChangedAction.Remove:
            foreach (var item in e.OldItems)
            {
                var entity = item as Device;
                if (entity == null) continue;
                if (_isAddedbyApp) continue;

                _isAddedbyApp = true;
                _context.Devices.Remove(entity);
                _isAddedbyApp = false;
            }
            break;
        //Reset = Clear
        case NotifyCollectionChangedAction.Reset:

            break;
    }
}

xaml

<DataGrid 
          x:Name="DeviceListDataGrid" 
          Margin="0,25,0,0"
          Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="6"
          AutoGenerateColumns="False" 
          EnableRowVirtualization="True" 
          AlternatingRowBackground="LightBlue" 
          AlternationCount="2" 
          RowDetailsVisibilityMode="VisibleWhenSelected"
          ItemsSource="{Binding DeviceDatabaseViewModel.DeviceCollectionView}" 
          SelectedItem="{Binding DeviceDatabaseViewModel.SelectedDevice}">
    <DataGrid.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="DarkCyan"/>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="DarkCyan"/>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="DeviceIdentification" Header="Id" Width="200*" Binding="{Binding DeviceId, UpdateSourceTrigger=PropertyChanged}"  />
        <DataGridTextColumn x:Name="DeviceName" Header="Name" Width="200*" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  />
        <DataGridTextColumn x:Name="DeviceDescriptionColumn" Header="Description" Width="200*" Binding="{Binding Description, UpdateSourceTrigger=PropertyChanged}"/>
        <DataGridTextColumn x:Name="DeviceSupplier" Header="Supplier" Width="150*" Binding="{Binding Supplier, UpdateSourceTrigger=PropertyChanged}"/>
        <DataGridTextColumn x:Name="DeviceCategory" Header="Category" Width="150*" Binding="{Binding Category, UpdateSourceTrigger=PropertyChanged}"/>
    </DataGrid.Columns>
</DataGrid>

The GetLocalreturns ObservableCollection from the Entity Framework database which also is implementing the INotifyPropertyChanged.

However I'm unable to find the correct place for the SaveChanges method of Entity Framework, and additionally the structure seems to be a bit off?

EDIT

If I have placed the SaveChanges at the end of the event CollectionChanged my program hangs. Actually I had for some Debug reasons a MessageBox inside the SaveChanges() method, after disabling it things seems to be more or less in the right direction..

EDIT 2 Adding the Device entity definitions. Link to the file, because its rather long..

MessageBox is resulting with this exception:

{"An ItemsControl is inconsistent with its items source.\n See the inner exception for more information."}

{"Information for developers (use Text Visualizer to read this):\r\nThis exception was thrown because the generator for control 'System.Windows.Controls.DataGrid Items.Count:28' with name 'DeviceListDataGrid' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected:\r\n Accumulated count 27 is different from actual count 28. [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]\r\n At index 26: Generator's item '{NewItemPlaceholder}' is different from actual item 'FxEditorDatabaseStructure.Core.Domain.Device'.\r\n\r\nOne or more of the following sources may have raised the wrong events:\r\n
System.Windows.Controls.ItemContainerGenerator\r\n
System.Windows.Controls.ItemCollection\r\n
System.Windows.Data.ListCollectionView\r\n
System.Collections.ObjectModel.ObservableCollection`1[[FxEditorDatabaseStructure.Core.Domain.Device, FxEditorDatabaseStructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]\r\n(The starred sources are considered more likely to be the cause of the problem.)\r\n\r\nThe most common causes are (a) changing the collection or its Count without raising a corresponding event, and (b) raising an event with an incorrect index or item parameter.\r\n\r\nThe exception's stack trace describes how the inconsistencies were detected, not how they occurred. To get a more timely exception, set the attached property 'PresentationTraceSources.TraceLevel' on the generator to value 'High' and rerun the scenario. One way to do this is to run a command similar to the following:\n
System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator, System.Diagnostics.PresentationTraceLevel.High)\r\nfrom the Immediate window. This causes the detection logic to run after every CollectionChanged event, so it will slow down the application.\r\n"}

Information for developers (use Text Visualizer to read this): This exception was thrown because the generator for control 'System.Windows.Controls.DataGrid Items.Count:28' with name 'DeviceListDataGrid' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected: Accumulated count 27 is different from actual count 28. [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).] At index 26: Generator's item '{NewItemPlaceholder}' is different from actual item 'FxEditorDatabaseStructure.Core.Domain.Device'.


回答1:


There are serveral thing:

First, you can bind directly to _devices, or your top ObservableCollection variable, there is no need to have create DeviceCollectionView.

ItemsSource="{Binding DeviceDatabaseViewModel._devices}"

Remember to make your _devices public access.

Secondly, there is a problem in Device_CollectionChanged function, if you apply above change, you can do like this:

private bool _isAddedbyApp = false;

private void Device_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            foreach (var item in e.NewItems)
            {
                MessageBox.Show(item.GetType().ToString()); // if not = "Device" then tell me because there something you bind don't correct
                var entity = item as Device;
                if (entity != null && !_isAddedbyApp)
                {
                    if (MessageBox.Show("Add?", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                    {
                        // add entity to database
                        // No need to call _devices.Add() here, you collection
                        // will auto be updated.
                    }
                    else
                    {
                        // remove this item
                        _devices.Remove(entity);
                    }
                }
            }
            break;
    }
}

Third, you still don't know how to use _isAddedbyApp variable.

Only use it when your app (not user) add device.

_isAddedbyApp = true;   // You start add items.
_context.Add(...);
_isAddedbyApp = false;  // You finished add items.


来源:https://stackoverflow.com/questions/35766369/how-to-correctly-handle-the-collectionchanged-event-of-datagrid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!