这是答案[1]。FindAncestor在运行时 XAML 树中查找祖先,而不是在任意 C# 对象中。它不能从我们绑定的成员走到 ItemClass 实例。但我们确实知道 XAML 树中我们上面的某个人将我们绑定到该成员,并且他绑定到了 ItemClass 实例本身。所以不管是谁,我们找到他,然后我们就有了 ItemClass。
因此,让我们将调试跟踪添加到绑定中,我们将看到 XAML 情况在运行时的样子。毫无疑问,还有其他可能更聪明的方法可以做到这一点,但我碰巧知道这个没有任何研究。
首先将其添加到 XAML 文件顶部的命名空间:
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
...然后到绑定本身,添加:
diag:PresentationTraceSources.TraceLevel=High
像这样:
<TextBox Text="{Binding Value, Mode=TwoWay}"
IsEnabled="{Binding Path=IsEditAllowed, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ItemClass}, AncestorLevel=1}, diag:PresentationTraceSources.TraceLevel=High}"
/>
在运行时,当 TextEdit 的 IsEnabled 属性尝试从绑定中获取值时,绑定会向上遍历 XAML 树以查找指定类型的祖先。它一直在寻找,直到找到一个或用完一棵树,如果我们对其进行跟踪,它会一直跟踪它找到的所有内容的类型。我们已经告诉它寻找它永远找不到的垃圾,所以它会给我们一个追溯到树根的每个祖先的类型的踪迹,首先是叶子,最后是根。在这种情况下,我得到了 75 行祖先。
我这样做了,并找到了一些可能的候选人。我检查了每一个,结果获胜者是 dgx:GridCellContentPresenter,它有一个 RowData 属性。RowData 有很多属性,RowData.Row 是行的ItemClass 的实例。dxg:GridCellContentPresenter 属于我们正在使用的 DevExpress 网格库;在另一个供应商的网格类中,可能会有一些等价物。
这是工作绑定:
<TextBox Text="{Binding Value, Mode=TwoWay}"
IsEnabled="{Binding Path=RowData.Row.IsEditAllowed, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dxg:GridCellContentPresenter}, AncestorLevel=1}}"
/>
如果供应商 DevExpress 重写了他们的 GridControl 类,我们就有麻烦了。但无论如何,那是真的。
...
[1] 更好的答案,虽然它太具体到 DevExpress,没有任何真正的兴趣:TextBox 本身的 DataContext 原来是 dxg:EditGridCellData,它具有 RowData 属性,就像 GridCellContentPresenter 一样。我可以使用 IsEnabled="{Binding Path=RowData.Row.IsEditAllowed}"。
然而,我一直以来真正想做的不是呈现一个充满愚蠢的禁用文本框的网格,而是启用对网格中某些行的编辑。DevExpress 网格允许您通过 ShowingEditor 事件来做到这一点。
XAML:
<dxg:GridControl Name="grdItems">
<dxg:GridControl.View>
<dxg:TableView
NavigationStyle="Cell"
AllowEditing="True"
ShowingEditor="grdItems_TableView_ShowingEditor"
/>
</dxg:GridControl.View>
<!-- ... Much XAML ... -->
</dxg:GridControl Name="grdItems">
。CS:
private void grdItems_TableView_ShowingEditor(object sender, ShowingEditorEventArgs e)
{
e.Cancel = !(e.Row as ItemClass).IsEditAllowed;
}