DataGrid 导致 BeginEdit() 方法 运行 两次
DataGrid causes BeginEdit() method to run twice
我已经为我的 ViewModel 实现了 IEditableObject
并且下面的控件绑定到它。
现在,当我第一次启动包含这些控件的 Window
时,BeginEdit()
方法运行并备份变量。
我的问题是: 当我再次开始编辑 DataGrid
时,BeginEdit()
方法运行并将更改保存到我已经备份的变量中!这破坏了 BeginEdit()
和 CancelEdit()
的目的。现在如果我选择取消Window,我将没有原始数据。我怎样才能避免这种情况?
<ComboBox ItemsSource="{Binding Path=CoatingFactors}"
SelectedItem="{Binding Path=CoatingFactor}">
</ComboBox>
<DataGrid ItemsSource="{Binding Path=CustomCFactors}"
....
</DataGrid>
以下是我如何实现 BeginEdit()
和 CancelEdit()
方法:
private List<CustomCFactorItem> customCFactors_ORIGINAL;
private double coatingFactor_ORIGINAL;
public void BeginEdit()
{
customCFactors_ORIGINAL = customCFactors.ConvertAll(o => o.Clone()).ToList();
coatingFactor_ORIGINAL = coatingFactor;
}
public void CancelEdit()
{
customCFactors = customCFactors_ORIGINAL.ConvertAll(o => o.Clone()).ToList();
coatingFactor = coatingFactor_ORIGINAL;
}
更新:
目前,我正在使用这样的小技巧:
private List<CustomCFactorItem> customCFactors_ORIGINAL;
private double coatingFactor_ORIGINAL;
private int editNum = 0;
public void BeginEdit()
{
if (editNum > 0) return;
editNum++;
customCFactors_ORIGINAL = customCFactors.ConvertAll(o => o.Clone());
coatingFactor_ORIGINAL = coatingFactor;
}
public void EndEdit()
{
editNum = 0;
}
public void CancelEdit()
{
editNum = 0;
customCFactors = customCFactors_ORIGINAL.ConvertAll(o => o.Clone());
coatingFactor = coatingFactor_ORIGINAL;
}
最好不要在UI层与WPFDataGrid
控件打架。如果不编写自己的控件来满足您自己的目的,您根本不会赢得这场战斗。总会有另一个微软 "gotcha" 需要处理。我建议从 ObservableCollection
.
的避风港实施所需的行为
public class ViewModel
{
public ViewModel()
{
CustomCFactors.CollectionChanged += (s, e) => {
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (CustomCFactorItem item in e.NewItems)
item.PropertyChanged += BackupLogicEventHandler;
break;
case NotifyCollectionChangedAction.Remove:
foreach (CustomCFactorItem item in e.OldItems)
item.PropertyChanged -= BackupLogicEventHandler;
break;
}
};
for (int i = 0; i < 10; ++i)
{
CustomCFactors.Add(new CustomCFactorItem("one", "two", "three"));
}
ExecuteBackupLogic();
}
public ObservableCollection<CustomCFactorItem> CustomCFactors { get; set; } = new ObservableCollection<CustomCFactorItem>();
public void BackupLogicEventHandler(object sender, PropertyChangedEventArgs e){
ExecuteBackupLogic();
}
public void ExecuteBackupLogic()
{
Console.WriteLine("changed");
}
}
下面是 CustomCFactorItem
的示例
public class CustomCFactorItem : INotifyPropertyChanged
{
private string _field1 = "";
public string Field1
{
get
{
return _field1;
}
set
{
_field1 = value;
RaisePropertyChanged("Field1");
}
}
private string _field2 = "";
public string Field2
{
get
{
return _field2;
}
set
{
_field2 = value;
RaisePropertyChanged("Field2");
}
}
private string _field3 = "";
public string Field3
{
get
{
return _field3;
}
set
{
_field3 = value;
RaisePropertyChanged("Field1");
}
}
public CustomCFactorItem() { }
public CustomCFactorItem(string field1, string field2, string field3)
{
this.Field1 = field1;
this.Field2 = field2;
this.Field3 = field3;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
这是一个将集合绑定到 DataGrid
的简单视图。请注意,每次进行编辑时都会将输出写入控制台。
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding CustomCFactors}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Field 1" Binding="{Binding Field1, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 2" Binding="{Binding Field2, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 3" Binding="{Binding Field3, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
我已经为我的 ViewModel 实现了 IEditableObject
并且下面的控件绑定到它。
现在,当我第一次启动包含这些控件的 Window
时,BeginEdit()
方法运行并备份变量。
我的问题是: 当我再次开始编辑 DataGrid
时,BeginEdit()
方法运行并将更改保存到我已经备份的变量中!这破坏了 BeginEdit()
和 CancelEdit()
的目的。现在如果我选择取消Window,我将没有原始数据。我怎样才能避免这种情况?
<ComboBox ItemsSource="{Binding Path=CoatingFactors}"
SelectedItem="{Binding Path=CoatingFactor}">
</ComboBox>
<DataGrid ItemsSource="{Binding Path=CustomCFactors}"
....
</DataGrid>
以下是我如何实现 BeginEdit()
和 CancelEdit()
方法:
private List<CustomCFactorItem> customCFactors_ORIGINAL;
private double coatingFactor_ORIGINAL;
public void BeginEdit()
{
customCFactors_ORIGINAL = customCFactors.ConvertAll(o => o.Clone()).ToList();
coatingFactor_ORIGINAL = coatingFactor;
}
public void CancelEdit()
{
customCFactors = customCFactors_ORIGINAL.ConvertAll(o => o.Clone()).ToList();
coatingFactor = coatingFactor_ORIGINAL;
}
更新:
目前,我正在使用这样的小技巧:
private List<CustomCFactorItem> customCFactors_ORIGINAL;
private double coatingFactor_ORIGINAL;
private int editNum = 0;
public void BeginEdit()
{
if (editNum > 0) return;
editNum++;
customCFactors_ORIGINAL = customCFactors.ConvertAll(o => o.Clone());
coatingFactor_ORIGINAL = coatingFactor;
}
public void EndEdit()
{
editNum = 0;
}
public void CancelEdit()
{
editNum = 0;
customCFactors = customCFactors_ORIGINAL.ConvertAll(o => o.Clone());
coatingFactor = coatingFactor_ORIGINAL;
}
最好不要在UI层与WPFDataGrid
控件打架。如果不编写自己的控件来满足您自己的目的,您根本不会赢得这场战斗。总会有另一个微软 "gotcha" 需要处理。我建议从 ObservableCollection
.
public class ViewModel
{
public ViewModel()
{
CustomCFactors.CollectionChanged += (s, e) => {
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (CustomCFactorItem item in e.NewItems)
item.PropertyChanged += BackupLogicEventHandler;
break;
case NotifyCollectionChangedAction.Remove:
foreach (CustomCFactorItem item in e.OldItems)
item.PropertyChanged -= BackupLogicEventHandler;
break;
}
};
for (int i = 0; i < 10; ++i)
{
CustomCFactors.Add(new CustomCFactorItem("one", "two", "three"));
}
ExecuteBackupLogic();
}
public ObservableCollection<CustomCFactorItem> CustomCFactors { get; set; } = new ObservableCollection<CustomCFactorItem>();
public void BackupLogicEventHandler(object sender, PropertyChangedEventArgs e){
ExecuteBackupLogic();
}
public void ExecuteBackupLogic()
{
Console.WriteLine("changed");
}
}
下面是 CustomCFactorItem
的示例
public class CustomCFactorItem : INotifyPropertyChanged
{
private string _field1 = "";
public string Field1
{
get
{
return _field1;
}
set
{
_field1 = value;
RaisePropertyChanged("Field1");
}
}
private string _field2 = "";
public string Field2
{
get
{
return _field2;
}
set
{
_field2 = value;
RaisePropertyChanged("Field2");
}
}
private string _field3 = "";
public string Field3
{
get
{
return _field3;
}
set
{
_field3 = value;
RaisePropertyChanged("Field1");
}
}
public CustomCFactorItem() { }
public CustomCFactorItem(string field1, string field2, string field3)
{
this.Field1 = field1;
this.Field2 = field2;
this.Field3 = field3;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
这是一个将集合绑定到 DataGrid
的简单视图。请注意,每次进行编辑时都会将输出写入控制台。
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding CustomCFactors}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Field 1" Binding="{Binding Field1, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 2" Binding="{Binding Field2, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Field 3" Binding="{Binding Field3, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>