WPF DataGrid旧编辑元素导致问题

时间:2013-12-21 15:41:02

标签: c# wpf wpfdatagrid

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事件。

这给我带来了问题,因为我有两列 - AmountAmountUnitAmount为其编辑元素提供了 NumericUpDown AmountUnit将指定 NumericUpDown 的小数位数。 NumericUpDown 当然有一个 CoerceValue 方法,它将值约束到指定的小数位。问题是,所有旧的 NumericUpDown 编辑元素都会踩到每个人的拖尾并重新写入Amount值,而这些值应该在此时被垃圾收集。

所以我的问题是,如何在单元格完成编辑后立即销毁旧的编辑元素或进行垃圾回收?或者这个问题的解决方法是什么(很简单)?

1 个答案:

答案 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);
    }
}
相关问题