绑定到装饰元素的祖先
情况如下:
<DataTemplate x:Key="ItemTemplate"
DataType="local:RoutedCustomCommand">
<Button Command="{Binding}"
Content="{Binding Text}"
ToolTip="{Binding Description}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource SomeConverter}">
<!-- Converter simply checks flags matching
and returns corresponding Visibility -->
<Binding Path="VisibilityModes" />
<!-- VisibilityModes is a property of local:RoutedCustomCommand -->
<Binding Path="CurrentMode"
RelativeSource="{RelativeSource AncestorType=local:CustomControl}" />
<!-- CurrentMode is a property of local:CustomControl -->
</MultiBinding>
<Button.Visibility>
</Button>
</DataTemplate>
<local:CustomControl>
<!-- ... -->
<ToolBar ...
Width="15"
ItemTemplate={StaticResource ItemTemplate}
... />
<!-- Take a look at Width - it's especially is set to such a value
which forces items placement inside adorner overflow panel -->
<!-- If you change ToolBar to ItemsControl, items won't be wrapped by adorner
panel and everything will be OK -->
<!-- ... -->
</local:CustomControl>
简而言之:当某些元素位于装饰器内部时,您不能简单地使用 Binding 的relativesource属性来访问装饰可视化树内的元素。
当我需要将其 FontSize 绑定到工具提示的所有者 FontSize 时,我已经习惯了 ToolTip 遇到同样的问题 - 有非常方便的 PlacementTarget 属性,我不需要在树内部查找 - 绑定看起来像这样:
这几乎是同样的问题 - 当该项目位于 ToolBarOverflowPanel 内时,它似乎位于装饰器内,因此relativesource 显然无法绑定。
问题是:我该如何解决这个棘手的问题?我真的需要绑定到容器的属性。即使我能够绑定装饰元素,距离祖先也还有很长的路要走。
UPD:最不幸的副作用是命令无法到达预期目标 - 通过冒泡机制的命令传播在装饰器的视觉根处停止:(。
显式目标的规范也会遇到同样的问题 - 目标必须位于 local:CustomControl
的可视化树内,而同一相对源绑定无法访问该可视化树。
UPD2:添加视觉树和逻辑树遍历结果:
UPD3:删除旧的遍历结果。添加了更精确的遍历:
UPD4:(希望这是最终的)。遍历逻辑父母的视觉树:
VisualTree
System.Windows.Controls.Button
System.Windows.Controls.ContentPresenter
System.Windows.Controls.Primitives.ToolBarOverflowPanel inherits from System.Windows.Controls.Panel
LogicalTree
System.Windows.Controls.Border
Microsoft.Windows.Themes.SystemDropShadowChrome inherits from System.Windows.Controls.Decorator
System.Windows.Controls.Primitives.Popup
System.Windows.Controls.Grid
logical root: System.Windows.Controls.Grid
System.Windows.Controls.Border
LogicalTree
Microsoft.Windows.Themes.SystemDropShadowChrome inherits from System.Windows.Controls.Decorator
System.Windows.Controls.Primitives.Popup
System.Windows.Controls.Grid
logical root: System.Windows.Controls.Grid
Microsoft.Windows.Themes.SystemDropShadowChrome inherits from System.Windows.Controls.Decorator
LogicalTree
System.Windows.Controls.Primitives.Popup
System.Windows.Controls.Grid
logical root: System.Windows.Controls.Grid
System.Windows.Documents.NonLogicalAdornerDecorator inherits from System.Windows.Documents.AdornerDecorator
LogicalTree
logical root: System.Windows.Controls.Decorator
System.Windows.Controls.Decorator
visual root: System.Windows.Controls.Primitives.PopupRoot inherits from System.Windows.FrameworkElement
LogicalTree
System.Windows.Controls.Primitives.Popup
VisualTree
System.Windows.Controls.Grid
System.Windows.Controls.Grid
here it is: System.Windows.Controls.ToolBar
System.Windows.Controls.Grid
logical root: System.Windows.Controls.Grid
提前致谢!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好的,现在很容易看出这里发生了什么。线索存在于您最初的问题中,但在您发布逻辑树之前,我不清楚您在做什么。
正如我怀疑的那样,您的问题是由于缺乏逻辑继承引起的:在大多数示例中,您将在网上看到 ContentPresenter 将呈现一个 FrameworkElement,它将是 ToolBar 的逻辑后代,因此即使在以下情况下,事件路由和 FindAncestor 也可以工作视觉树被弹出窗口打断。
在您的情况下,不存在逻辑树连接,因为 ContentPresenter 呈现的内容不是 FrameworkElement。
换句话说,这将允许绑定和事件路由甚至在装饰器内部工作:
但这不会:
当然,如果您的项目是 FrameworkElement 派生的,它们可以是 Controls,并且您可以使用 ControlTemplate 而不是 DataTemplate。或者,它们可以是仅呈现其数据项的 ContentPresenter。
如果您在代码中设置 ItemsSource,则这是一个简单的更改。将其替换
为
:如果要在 XAML 中设置 ItemsSource,我通常使用的技术是在我自己的类中创建附加属性(例如“DataItemsSource”)并设置 PropertyChangedCallback,以便在设置 DataItemsSource 时,它执行上面所示的 .Select() 来创建 ContentPresenter 并设置 ItemsSource。重点是:
它将允许这个工作:
其中 myItems 是 内联列出项目)
DataTemplate
应用到的非FrameworkElement
的集合。 (也可以使用另请注意,这种包装数据项的技术假设数据的模板是通过样式应用的,而不是通过样式应用的。通过
ItemsControl.ItemTemplate 属性
。如果您确实想通过 ItemsControl.ItemTemplate 应用模板,则您的 ContentPresenter 需要将绑定添加到其ContentTemplate
属性中,该属性使用 FindAncestor 在ItemsControl
中查找模板。这是在使用“SetBinding”“new ContentPresenter”之后完成的。希望这有帮助。
Okay, now it is easy to see what is going on here. The clues where there in your original question but it wasn't obvious to me what you were doing until you posted the logical tree.
As I suspected, your problem is caused by a lack of logical inheritance: In most examples you'll see online the ContentPresenter would be presenting a FrameworkElement which would be a logical descendant of the ToolBar, so it event routing and FindAncestor would work even when the visual tree is interrupted by a popup.
In your case, there is no logical tree connection because the content being presented by the ContentPresenter is not a FrameworkElement.
In other words, this will allow bindings and event routing to work even inside an adorner:
But this won't:
Of course if your items are FrameworkElement-derived, they can be Controls and you can use a ControlTemplate instead of a DataTemplate. Alternatively they can be ContentPresenters that simply present their data items.
If you're setting ItemsSource in code, this is an easy change. Replace this:
with this:
If you're setting ItemsSource in XAML, the technique I generally use is to create an attached property (for example, "DataItemsSource") in my own class and set a PropertyChangedCallback so that any time DataItemsSource is set, it does the .Select() shown above to create ContentPresenters and sets ItemsSource. Here's the meat:
which will allow this to work:
where myItems is a collection of non-
FrameworkElement
s that theDataTemplate
applies to. (Listing the items inline is also possible with<Toolbar.DataItemsSource><x:Array ...
)Also note that this technique of wrapping data items assumes your data's template is applied through styles, not through the
ItemsControl.ItemTemplate property
. If you do want to apply the template through ItemsControl.ItemTemplate, your ContentPresenters need to have a binding added to theirContentTemplate
property which uses FindAncestor to find the template in theItemsControl
. This is done after "new ContentPresenter" using "SetBinding".Hope this helps.
好吧,
ToolBar
的溢出面板似乎有非常奇怪的行为 - 它有测量问题以及随机绑定问题,所以我设计了简单的CommandsHost
控件,它使用 < code>Popup 以及那里的一切都运行良好。该控件符合我的要求,请随意根据您的需要进行修改。
这是样式:
这是逻辑:
我希望这会对某人有所帮助。
OK,
ToolBar
appeared to have very weird behavior with its overflow panel - it have measure issues as well as random binding issues, so I've designed simpleCommandsHost
control which usesPopup
and everything there works great.This control fits my requirements, feel free to modify it for you needs.
Here is styling:
Here is the logic:
I hope this will help somebody.