3

我定义了一个网格列。父网格从 ItemClass 类型的 ObservableCollection 获取其项目。ItemClass 有两个属性:String Foo 和 bool IsEditAllowed。

此列绑定到属性 Foo。有一个用于编辑单元格的控制模板。我想将 ItemClass.IsEditAllowed 属性绑定到模板中 TextBox 的 IsEnabled 属性。

问题是如何绑定它。这可以做到吗?下面的 XAML 在调试跟踪中让我“找不到与引用绑定的源”。

网格将允许我通过一些“自定义”事件将 ItemClass 本身绑定到字段,然后我可以绑定到它的任何属性。这很好,但看起来很笨拙。但如果这是唯一的方法,那就是唯一的方法。

<dxg:GridColumn
                 Header="Foo Column"
                 FieldName="Foo">
    <dxg:GridColumn.EditTemplate>
        <ControlTemplate>
            <TextBox Text="{Binding Value, Mode=TwoWay}"
                     IsEnabled="{Binding Path=IsEditAllowed, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ItemClass}, AncestorLevel=1}}" />
        </ControlTemplate>
    </dxg:GridColumn.EditTemplate>
</dxg:GridColumn>
4

2 回答 2

8

有两种可能更简单的方法来设置此绑定。

1) 命名网格。然后您的绑定可能看起来像这样(假设 dxg:GridControl 有一个名为“Items”的属性,并且您已将 ItemClass 的实例分配给该属性):

<TextBox IsEnabled="{Binding Path=Items.IsEditAllowed, ElementName=MyGridControl />

2) 使用相对绑定,但查找 GridControl 而不是 GridControl 工作方式名义上内部的东西(即 GridControlContentPresenter)。这使您远离了 GridControl 的实现细节,与 GridControl 本身的属性相比,这些实现细节更有可能以破坏您的应用程序的方式发生变化。

<TextBox IsEnabled="{Binding Path=Items.IsEditAllowed, RelativeSource={RelativeSource AncestorType={x:Type dxg:GridControl}}}" />

您可能还想阅读WPF/xaml 中的 Visual Tree 和 Logical Tree。相对绑定中的“祖先”指的是可视化树中的祖先,即父容器之类的东西,而不是超类或基类(我认为你已经发现)。

于 2013-05-10T16:05:24.920 回答
4

这是答案[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;
}
于 2013-05-09T13:56:17.267 回答