XAML绑定到数据对象的父级

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>

你应该将你的答案作为一个答案发布,而且不要使用口语化的表达。 - Dan Puzey
2个回答

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

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

  2. 使用相对绑定,但查找GridControl而不是GridControlContentPresenter中的某些名义上内部的内容。 这使您远离GridControl的实现细节,后者可能更有可能以破坏应用程序的方式发生更改,而不是GridControl本身的属性。

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

你可能还想了解WPF / xaml中的可视树和逻辑树。相对绑定中的“祖先”是指可视树中的祖先,也就是像父容器这样的东西,而不是超类或基类(我想你已经发现了)。

网格的Items属性绑定到一个包含许多ItemsClass实例的ObservableCollection。每行网格中显示一个ItemsClass实例。因此,在单元格模板中,我必须绑定到Items[rowindex].IsEditAllowed,但是我从哪里获取rowindex? - 15ee8f99-57ff-4f92-890c-b56153
抱歉,一开始我误解了ItemsClass包含一个ObservableCollection。你还有另一个可能的出路是DataContext。大多数/所有ItemControls将DataContext设置为项容器所绑定到的源集合中的任何项。在没有源规范的情况下,绑定使用DataContext作为它们的源。您可以使用类似Snoop的工具来查看单元格和/或行上的DataContext是什么。 - dtm
是的,我已经解决了所有问题。其中一个选项是遵循可视树。最简单的选项是,发现单元格的DataContext对象是一个类,它在Value属性中具有单元格的值,并且在Row属性中还具有行对象--ItemClass实例。因此,使用它非常简单。 - 15ee8f99-57ff-4f92-890c-b56153

5

这是答案[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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接