问题
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 GetLocal
returns 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