【问题标题】:WPF DataGrid old editing elements causing problemsWPF DataGrid 旧编辑元素导致问题
【发布时间】:2014-01-10 07:42:33
【问题描述】:

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”和“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 值,而此时它们应该被垃圾回收。

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

【问题讨论】:

    标签: c# wpf wpfdatagrid


    【解决方案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);
        }
    }
    

    【讨论】:

    • 我解决了几个小时的问题,然后发现我错过了这一行 var child = (VisualTreeHelper.GetChild(cp, 0) as UIElement);
    猜你喜欢
    • 1970-01-01
    • 2011-07-28
    • 2011-01-19
    • 2011-10-19
    • 2010-09-13
    • 1970-01-01
    • 2012-12-04
    • 2011-11-27
    • 1970-01-01
    相关资源
    最近更新 更多