DataGrid 中的依赖性 属性 交叉污染

Dependency Property Cross-Contamination in DataGrid

我有一个绑定到集合的 WPF DataGrid。 DataGrid 的列之一是包含我的自定义控件的 DataGridTemplateColumn:

<DataGridTemplateColumn Header="$ Diff" Width="60*">
  <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
      <ui:BoardValueDifference Board1="{Binding Board1}" Board2="{Binding Board2}"/>
    </DataTemplate>
  </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

我的自定义控件有两个依赖属性,Board1 和 Board2,非常标准的东西。

public BoardValueDifference()
{
    InitializeComponent();
}

public static readonly DependencyProperty Board1Property = DependencyProperty.Register("Board1", typeof(PackageBoard),
        typeof(BoardValueDifference), new FrameworkPropertyMetadata(OnDataChanged));

public PackageBoard Board1
{
    get { return (PackageBoard)GetValue(Board1Property); }
    set { SetValue(Board1Property, value); }
}

protected static void OnDataChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
    BoardValueDifference bvd = d as BoardValueDifference;

    if ( bvd != null )
        bvd.CalculateDifference();
}

protected void CalculateDifference()
{
    if ( Board1 != null && Board2 != null) {
        // Calculate value difference between boards
    }
}

我的问题是 Board1 和 Board2 似乎不确定,我正在从其他行获取值。如果我在 OnDataChanged() 中放置一个断点,我会看到它被 Board1 的数据绑定击中,然后被 Board2 的数据绑定击中,但在 Board2 之间将有来自其他行的值,如果我上下滚动我的 DataGrid,这尤其糟糕。

在另一个问题中,我发现了这个 link、Collection-Type Dependency Properties,这不是我的情况所特有的,但看起来很相似。他们建议在构造函数中使用 SetValue() 并将 属性 的默认值设置为新实例。我尝试将其设置为 null,但它似乎完全禁用了我的数据绑定。

SetValue(Board1Property, null);

我的猜测是因为 DependencyProperties 是静态的,并且因为我的 DependencyProperty 是 class 我的 BoardValueDifference 用户控件的实例之间正在进行某种共享。我该如何解决?我认为它可能与 this question 类似,但我对回复的理解不够好,无法解决我的问题。

更新

在我的用户控件中添加了一些日志记录后,我想我明白了发生了什么:

  1. 我曾假设 DataGrid 的每一行都会获得我的 UserControl 的一个新实例,而 Board1 和 Board2 将从空开始。根据我的日志,BoardValueDifference 被重新使用,只是更改了 DependencyProperties。
  2. 在我的代码中,一旦 Board1 和 Board2 不再为空,我就会计算板之间的差异。我意识到我现在不能这样做,但是在我的 UserControl 中保留剩余数据感觉非常草率。我猜你必须把它想得更像是一个静态的 class?

最重要的是,我可以解决这个问题,但如果有人有更好的解决方法或确切知道 DataGrid 中 UserControl 的生命周期应该如何工作,我将不胜感激。

由于行虚拟化,您的用户控件被回收。如果您的 DataGrid 中没有太多行,您可以通过将 EnableRowVirtualization 属性 设置为 false 来禁用此行为。

来自 DataGrid.EnableRowVirtualization 页面的备注部分:

To improve performance, the EnableRowVirtualization property is set to true by default. When the EnableRowVirtualization property is set to true, the DataGrid does not instantiate a DataGridRow object for each data item in the bound data source. Instead, the DataGrid creates DataGridRow objects only when they are needed, and reuses them as much as it can. For example, the DataGrid creates a DataGridRow object for each data item that is currently in view and recycles the row when it scrolls out of view.