导致问题的WPF DataGrid旧编辑元素



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>

代码:

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"one_answers"txtChanned _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事件。

这给我带来了问题,因为我有两列——AmountAmountUnitAmount的编辑元素有一个NumericUpDownAmountUnit将指定NumericUpDown的小数位数。NumericUpDown当然有一个CoerceValue方法,它将把值限制在指定的小数点后几位。问题是,所有旧的NumericUpDown编辑元素都在相互拖动并重写Amount值,而此时它们应该被垃圾收集。

所以我的问题是,一旦单元格完成编辑,我如何让旧的编辑元素立即被销毁或垃圾收集?或者,对于这个问题,有什么(简单的)解决方法

我想明白了:)

问题是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);
}
}

最新更新