WPF DataGrid(.NET 4.5.1)不会自动销毁旧的编辑元素。请考虑以下小例子:
XAML:
<Window ....
xmlns:wa="clr-namespace:WpfApplication1">
....
<DataGrid Name="dataGrid" AutoGenerateColumns="False" Height="200" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="testCol">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding TestText}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<wa:MyTextBox Text="{Binding TestText}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
CODE:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<TestClass> tclist = new List<TestClass>();
tclist.Add(new TestClass());
tclist.Add(new TestClass());
this.dataGrid.ItemsSource = tclist;
}
public class TestClass
{
public string TestText { get; set; }
}
public class MyTextBox : TextBox
{
internal static int IntPool = 0;
private int _ID = -1;
public MyTextBox()
{
IntPool++;
_ID = IntPool;
System.Diagnostics.Trace.WriteLine("_ID = " + _ID.ToString());
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
System.Diagnostics.Trace.WriteLine("txtChanged _ID = " + _ID.ToString());
}
}
运行应用程序并开始编辑第一个单元格时,跟踪窗口中会出现“_ID = 1”。键入一个字母,然后出现“txtChanged _ID = 1”。单击DataGrid外部以结束编辑。重复这些步骤,完成编辑后,您会注意到“txtChanged _ID = 2”和“txtChanged _ID = 1”都会添加到跟踪窗口中。示例跟踪看起来像:
_ID = 1 // editing element created
txtChanged _ID = 1 // typed the letter A
_ID = 2 // editing element created
txtChanged _ID = 2 // TextBox empty string replaced with letter A via binding
txtChanged _ID = 2 // typed the letter B
txtChanged _ID = 1 // this old editing element still firing!!!
重复这一点,你会看到多个旧的编辑元素继续触发它们的TextChanged事件。
这给我带来了问题,因为我有两列 - Amount
和AmountUnit
。 Amount
为其编辑元素提供了 NumericUpDown ,AmountUnit
将指定 NumericUpDown 的小数位数。 NumericUpDown 当然有一个 CoerceValue 方法,它将值约束到指定的小数位。问题是,所有旧的 NumericUpDown 编辑元素都会踩到每个人的拖尾并重新写入Amount
值,而这些值应该在此时被垃圾收集。
所以我的问题是,如何在单元格完成编辑后立即销毁旧的编辑元素或进行垃圾回收?或者这个问题的解决方法是什么(很简单)?
答案 0 :(得分:1)
我明白了:)
问题是DataGridTemplateColumn在卸载后不会自动清除其元素的绑定。也许,因为你可以定义非常复杂的元素,自动完成它会非常困难和耗时。
使用反射器查看DataGridTextColumn,我得出结论,我必须手动完成,因为DataGridTextColumn也会在内部清除绑定。
我现在遇到的问题是,没有一种清除绑定的标准方法有效:
// For instance, I Added a MyTextBox_Unloaded event handler and tried calling:
private void MyTextBox_Unloaded(object sender, RoutedEventArgs e)
{
// none of these worked
(sender as MyTextBox).ClearValue(MyTextBox.TextProperty); // this is what DataGridTextColumn internally uses
BindingOperations.ClearAllBindings((sender as MyTextBox));
(sender as MyTextBox).SetValue(MyTextBox.TextProperty, null);
(sender as MyTextBox).Text = null;
BindingOperations.ClearBinding((sender as MyTextBox), MyTextBox.TextProperty);
}
// I also tried, this:
private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
var cp = e.EditingElement as ContentPresenter;
var child = (VisualTreeHelper.GetChild(cp, 0) as UIElement);
BindingOperations.ClearBinding(cp.BindingGroup.BindingExpressions[0].Target, cp.BindingGroup.BindingExpressions[0].TargetProperty);
(child as MyTextBox).ClearValue(MyTextBox.TextProperty);
BindingOperations.ClearAllBindings(child as MyTextBox);
cp.BindingGroup.BindingExpressions.Clear();
}
但最后我偶然发现了这个,这确实有效:
private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.Column is DataGridTemplateColumn)
{
BindingOperations.ClearAllBindings(e.EditingElement as ContentPresenter);
}
}