Mat*_*ský 5 data-binding wpf xaml
这个问题是一个"续集"到了这个问题(我已经申请了答案,但它仍然无法工作).
我正在尝试为模块化应用程序创建一个扩展的ToolBar控件,它可以从多个数据源加载其项目(但这不是我现在要解决的问题,现在我希望它在作为常规使用时工作在WPF中找到的ToolBar).
简而言之:我希望ToolBar的项能够绑定到tb:ToolBar的父项.
我有以下XAML代码:
<Window Name="myWindow" DataContext="{Binding ElementName=myWindow}" >
<DockPanel>
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top" DataContext="{Binding ElementName=myWindow}>
<tb:ToolBar.Items>
<tb:ToolBarControl Priority="-3">
<tb:ToolBarControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Maps:</TextBlock>
<ComboBox ItemsSource="{Binding SomeProperty, ElementName=myWindow}">
Run Code Online (Sandbox Code Playgroud)
有关类型的一些信息:
tb:ToolBar是一个UserControl具有Items类型的依赖属性FreezableCollection<ToolBarControl>.
tb:ToolBarControl是一个UserControl与ContentControl模板非常相似的模板.
问题是ComboBox失败中的绑定(与通常的"无法找到与引用绑定的源"),因为其DataContext为null.
为什么?
编辑:问题的核心是"为什么DataContext没有继承?",没有它,绑定无法工作.
EDIT2:
这是XAML tb:ToolBar:
<UserControl ... Name="toolBarControl">
<ToolBarTray>
<ToolBar ItemsSource="{Binding Items, ElementName=toolBarControl}" Name="toolBar" ToolBarTray.IsLocked="True" VerticalAlignment="Top" Height="26">
Run Code Online (Sandbox Code Playgroud)
编辑3:
我发布了一个有效和无效的例子:http://pastebin.com/Tyt1Xtvg
谢谢你的回答.
DataContext我个人不喜欢设置控件的想法。我认为这样做会以某种方式破坏数据上下文继承。请看一下这篇文章。我认为西蒙解释得很好。
至少,尝试删除
DataContext="{Binding ElementName=myWindow}"
Run Code Online (Sandbox Code Playgroud)
从
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top" DataContext="{Binding ElementName=myWindow}>
Run Code Online (Sandbox Code Playgroud)
看看进展如何。
更新
实际上,保留所有现有代码(暂时忽略我之前的建议),只需更改
<ComboBox ItemsSource="{Binding SomeProperty, ElementName=myWindow}">
Run Code Online (Sandbox Code Playgroud)
到
<ComboBox ItemsSource="{Binding DataContext.SomeProperty}">
Run Code Online (Sandbox Code Playgroud)
看看它是否有效。
我认为由于您构造控件的方式, 与和ComboBox处于同一级别/范围。这意味着它们都共享相同的,因此您实际上不需要任何绑定或绑定来尝试找到其父/祖先。tb:ToolBarControltb:ToolBarDataContextElementNameRelativeSource
DataContext="{Binding ElementName=myWindow}如果从 中删除tb:ToolBar,您甚至可以去掉DataContext绑定中的前缀。这确实是您所需要的。
<ComboBox ItemsSource="{Binding SomeProperty}">
Run Code Online (Sandbox Code Playgroud)
更新 2来回答您的编辑 3
这是因为用户Items控件中的集合tb:ToolBar只是一个属性。它不在逻辑和视觉树中,我相信ElementName绑定使用逻辑树。
这就是为什么它不起作用。
添加到逻辑树
我认为要将其添加Items到逻辑树中,您需要做两件事。
首先,您需要覆盖用户控件LogicalChildren中的tb:ToolBar。
protected override System.Collections.IEnumerator LogicalChildren
{
get
{
if (Items.Count == 0)
{
yield break;
}
foreach (var item in Items)
{
yield return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后每当你添加一个新的tb:ToolBarControl你需要调用
AddLogicalChild(item);
Run Code Online (Sandbox Code Playgroud)
试一试。
这有效...
经过一番尝试后,我认为上面向您展示的内容还不够。您还需要将它们添加ToolBarControls到主窗口的名称范围中以启用ElementName绑定。我假设这就是您定义Items依赖属性的方式。
public static DependencyProperty ItemsProperty =
DependencyProperty.Register("Items",
typeof(ToolBarControlCollection),
typeof(ToolBar),
new FrameworkPropertyMetadata(new ToolBarControlCollection(), Callback));
Run Code Online (Sandbox Code Playgroud)
在回调中,您可以将其添加到名称范围中。
private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var toolbar = (ToolBar)d;
var items = toolbar.Items;
foreach (var item in items)
{
// the panel that contains your ToolBar usercontrol, in the code that you provided it is a DockPanel
var panel = (Panel)toolbar.Parent;
// your main window
var window = panel.Parent;
// add this ToolBarControl to the main window's name scope
NameScope.SetNameScope(item, NameScope.GetNameScope(window));
// ** not needed if you only want ElementName binding **
// this enables bubbling (navigating up) in the visual tree
//toolbar.AddLogicalChild(item);
}
}
Run Code Online (Sandbox Code Playgroud)
另外,如果您想要财产继承,您将需要
// ** not needed if you only want ElementName binding **
// this enables tunneling (navigating down) in the visual tree, e.g. property inheritance
//protected override System.Collections.IEnumerator LogicalChildren
//{
// get
// {
// if (Items.Count == 0)
// {
// yield break;
// }
// foreach (var item in Items)
// {
// yield return item;
// }
// }
//}
Run Code Online (Sandbox Code Playgroud)
我已经测试了代码并且运行良好。