gpt4 book ai didi

wpf - 绑定(bind)到装饰元素的祖先

转载 作者:行者123 更新时间:2023-12-03 15:01:48 27 4
gpt4 key购买 nike

情况是这样的:

<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 绑定(bind)到工具提示的所有者 FontSize 时,我已经习惯了使用 ToolTip 遇到同样的问题 - 有非常方便的 PlacementTarget 属性,我不需要在树中查找 -绑定(bind)看起来像这样:<Binding PlacementTarget.FontSize />

这几乎是同样的问题 - 当项目在 ToolBarOverflowPanel 内部时,它似乎在装饰器内部,因此 RelativeSource 显然无法绑定(bind)。

问题是:如何解决这个棘手的问题?我真的需要绑定(bind)到容器的属性。即使我能够绑定(bind)到装饰元素,离祖先还有很长的路要走。

UPD: 最不幸的副作用是命令未到达预期目标 - 通过冒泡机制的命令传播在装饰者的可视根处停止:(。明确目标的规范遇到了同样的问题——目标必须在 local:CustomControl 内的可视化树,无法通过相同的 RelativeSource 绑定(bind)访问。

UPD2:添加可视化和逻辑树遍历结果:

UPD3: 删除了旧的遍历结果。添加更精确的遍历:

UPD4:(希望这是最终版本)。逻辑 parent 的遍历可视化树:

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

提前致谢!

最佳答案

好的,现在很容易看出这里发生了什么。你原来的问题中有线索,但在你发布逻辑树之前我并不清楚你在做什么。

正如我所怀疑的,您的问题是由于缺乏逻辑继承引起的:在大多数示例中,您会在网上看到 ContentPresenter 会呈现一个 FrameworkElement,它是 ToolBar 的逻辑后代,因此事件路由和 FindAncestor 会即使在可视化树被弹出窗口打断时也能正常工作。

在您的情况下,没有逻辑树连接,因为 ContentPresenter 呈现的内容不是 FrameworkElement。

换句话说,这将允许绑定(bind)和事件路由甚至在装饰器内部工作:

<Toolbar Width="15">
<MenuItem .../>
<MenuItem .../>
</Toolbar>

但这不会:

<Toolbar Width="15">
<my:NonFrameworkElementObject />
<my:NonFrameworkElementObject />
</Toolbar>

当然,如果您的项是 FrameworkElement 派生的,它们可以是控件,您可以使用 ControlTemplate 而不是 DataTemplate。或者,它们可以是仅展示其数据项的 ContentPresenters。

如果您在代码中设置 ItemsSource,这是一个简单的更改。替换为:

MyItems.ItemsSource = ComputeItems();

用这个:

MyItems.ItemsSource = ComputeItems()
.Select(item => new ContentPresenter { Content = item });

如果您在 XAML 中设置 ItemsSource,我通常使用的技术是在我自己的类中创建附加属性(例如,“DataItemsSource”)并设置 PropertyChangedCallback,以便在设置 DataItemsSource 时,它​​会执行上面显示的 .Select() 创建 ContentPresenters 并设置 ItemsSource。这是肉:

public class MyItemsSourceHelper ...
{
... RegisterAttached("DataItemsSource", ..., new FrameworkPropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var dataSource = GetDataItemsSource(obj);
obj.SetValue(ItemsControl.ItemsSource,
dataSource==null ? null :
dataSource.Select(item => new ContentPresenter { Content = item });
}
}

这将允许它工作:

<Toolbar Width="15" DataTemplate="..."
my:MyItemsSourceHelper.DataItemsSource="{Binding myItems}" />

其中 myItems 是非 FrameworkElement 的集合那就是DataTemplate适用于。 (使用 <Toolbar.DataItemsSource><x:Array ... 也可以内联列出项目)

另请注意,这种包装数据项的技术假定您的数据模板是通过样式而不是通过 ItemsControl.ItemTemplate property 应用的。 .如果您确实想通过 ItemsControl.ItemTemplate 应用模板,您的 ContentPresenters 需要将绑定(bind)添加到其 ContentTemplate使用 FindAncestor 在 ItemsControl 中查找模板的属性.这是在使用“SetBinding”的“new ContentPresenter”之后完成的。

希望这对您有所帮助。

关于wpf - 绑定(bind)到装饰元素的祖先,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1732920/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com