15e*_*153 3 data-binding wpf xaml binding
我定义了一个网格列.父网格从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>
Run Code Online (Sandbox Code Playgroud)
设置此绑定有两种可能更简单的方法.
1)命名网格.然后您的绑定看起来像这样(假设dxg:GridControl有一个名为"Items"的属性,并且您已将ItemClass的实例分配给该属性):
<TextBox IsEnabled="{Binding Path=Items.IsEditAllowed, ElementName=MyGridControl />
Run Code Online (Sandbox Code Playgroud)
2)使用相对绑定,但寻找GridControl,而不是名义上内部的GridControl工作方式(即GridControlContentPresenter).这使您远离GridControl的实现细节,这可能更有可能以破坏应用程序的方式进行更改,而不是GridControl本身的属性.
<TextBox IsEnabled="{Binding Path=Items.IsEditAllowed, RelativeSource={RelativeSource AncestorType={x:Type dxg:GridControl}}}" />
Run Code Online (Sandbox Code Playgroud)
您可能还想阅读WPF/xaml中的Visual Tree和Logical Tree.相对绑定中的"祖先"是指可视树中的祖先,即父容器之类的东西,而不是超类或基类(我认为,正如您所发现的那样).
Here's the answer[1]. FindAncestor finds ancestors in the runtime XAML tree, not in arbitrary C# objects. It cannot walk up to the ItemClass instance from the member we're bound to. But we do know that somebody above us in the XAML tree bound us to that member, and he was bound to the ItemClass instance itself. So whoever that is, we find him, and then we've got the ItemClass.
So let's add debug tracing to the binding, and we'll see what the XAML situation looks like at runtime. No doubt there are other and probably smarter ways to do that, but I happen to know this one without any research.
First add this to the namespaces at the top of the XAML file:
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Run Code Online (Sandbox Code Playgroud)
...and then to the binding itself, add this:
diag:PresentationTraceSources.TraceLevel=High
Run Code Online (Sandbox Code Playgroud)
Like so:
<TextBox Text="{Binding Value, Mode=TwoWay}"
IsEnabled="{Binding Path=IsEditAllowed, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ItemClass}, AncestorLevel=1}, diag:PresentationTraceSources.TraceLevel=High}"
/>
Run Code Online (Sandbox Code Playgroud)
At runtime, when the TextEdit's IsEnabled property tries to get a value from the binding, the binding walks up through the XAML tree looking for an ancestor of the specified type. It keeps looking until it finds one or runs out of tree, and if we put tracing on it, it traces the type of everything it finds the whole way up. We've told it to look for garbage that it'll never find, so it will give us a trace of the type of every ancestor back to the root of the tree, leaf first and root last. I get 75 lines of ancestors in this case.
I did that, and found a few likely candidates. I checked each one, and the winner turned out to be dgx:GridCellContentPresenter, which has a RowData property. RowData has a lot of properties, and RowData.Row is the row's instance of ItemClass. dxg:GridCellContentPresenter belongs to the DevExpress grid library we're using; in another vendor's grid class, there would presumably be some equivalent.
Here's the working binding:
<TextBox Text="{Binding Value, Mode=TwoWay}"
IsEnabled="{Binding Path=RowData.Row.IsEditAllowed, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dxg:GridCellContentPresenter}, AncestorLevel=1}}"
/>
Run Code Online (Sandbox Code Playgroud)
If DevExpress, the vendor, rewrites their GridControl class, we'll be in trouble. But that was true anyhow.
...
[1] Better answer, though it's too DevExpress specific to be of any real interest: The DataContext of the TextBox itself turns out to be dxg:EditGridCellData, which has a RowData property just like GridCellContentPresenter does. I can just use IsEnabled="{Binding Path=RowData.Row.IsEditAllowed}".
However, what I really wanted to do all along was not to present a grid full of stupid disabled textboxes, but rather to enable editing on certain rows in the grid. And the DevExpress grid lets you do that through the ShowingEditor event.
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">
Run Code Online (Sandbox Code Playgroud)
.cs:
private void grdItems_TableView_ShowingEditor(object sender, ShowingEditorEventArgs e)
{
e.Cancel = !(e.Row as ItemClass).IsEditAllowed;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15914 次 |
| 最近记录: |