这是答案[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类,我们就会遇到麻烦。但这本来就是真的。更好的答案是,尽管它太针对DevExpress了,没有任何实际意义:TextBox本身的DataContext是dxg:EditGridCellData,其具有与GridCellContentPresenter相同的RowData属性。我只需使用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;
}