我有一个典型的Listbox设置和vm + DataTemplate和项目vm.数据模板具有工具提示,其中包含绑定到项目vm的元素.一切都很棒.
现在,我想将工具提示相对于列表框本身放置.它相当大,当随便鼠标悬停在列表框上时会妨碍它.所以我想我会在DataTemplate中做这样的事情:
<Grid ...>
<TextBlock x:Name="ObjectText"
ToolTipService.Placement="Left"
ToolTip="{StaticResource ItemToolTip}"
ToolTipService.PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}">
</TextBlock>
...
Run Code Online (Sandbox Code Playgroud)
...使用静态资源...
<ToolTip x:Key="ItemToolTip">
<StackPanel>
<TextBlock Text="{Binding DisplayName.Name}"/>
<TextBlock Text="{Binding Details}" FontStyle="Italic"/>
...
</StackPanel>
</ToolTip>
Run Code Online (Sandbox Code Playgroud)
这是我的问题.当我使用该PlacementTarget时,我得到一个绑定错误,DisplayName.Name和Details不绑定.它尝试绑定的对象不是项目vm,而是整个Listbox vm.
所以我的问题是:如何为工具提示设置ToolTipService.PlacementTarget,同时保持DataContext继承自其所有者?
好吧,工作的朋友大多数都是为我找的.这种方式超级干净,不觉得hacky.
这是基本问题:正如user164184所提到的,工具提示是弹出窗口,因此不属于可视化树.所以WPF有一些神奇之处.弹出窗口的DataContext来自PlacementTarget,它是绑定大部分时间的工作方式,尽管弹出窗口不是树的一部分.但是当您更改PlacementTarget时,这将覆盖默认值,现在DataContext来自新的PlacementTarget,无论它是什么.
完全不直观.如果MSDN有,而不是花费数小时构建所有那些不同工具提示出现的漂亮图表,那就好了,用一句话说一下DataContext会发生什么.
无论如何,对解决方案!与所有有趣的WPF技巧一样,附加属性也可以解决.我们将添加两个附加属性,以便我们可以在生成时直接设置工具提示的DataContext.
public static class BindableToolTip
{
public static readonly DependencyProperty ToolTipProperty = DependencyProperty.RegisterAttached(
"ToolTip", typeof(FrameworkElement), typeof(BindableToolTip), new PropertyMetadata(null, OnToolTipChanged));
public static void SetToolTip(DependencyObject element, FrameworkElement value) { element.SetValue(ToolTipProperty, value); }
public static FrameworkElement GetToolTip(DependencyObject element) { return (FrameworkElement)element.GetValue(ToolTipProperty); }
static void OnToolTipChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
{
ToolTipService.SetToolTip(element, e.NewValue);
if (e.NewValue != null)
{
((ToolTip)e.NewValue).DataContext = GetDataContext(element);
}
}
public static readonly DependencyProperty DataContextProperty = DependencyProperty.RegisterAttached(
"DataContext", typeof(object), typeof(BindableToolTip), new PropertyMetadata(null, OnDataContextChanged));
public static void SetDataContext(DependencyObject element, object value) { element.SetValue(DataContextProperty, value); }
public static object GetDataContext(DependencyObject element) { return element.GetValue(DataContextProperty); }
static void OnDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
{
var toolTip = GetToolTip(element);
if (toolTip != null)
{
toolTip.DataContext = e.NewValue;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在XAML中:
<Grid ...>
<TextBlock x:Name="ObjectText"
ToolTipService.Placement="Left"
ToolTipService.PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
mystuff:BindableToolTip.DataContext="{Binding}">
<mystuff:BindableToolTip.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock Text="{Binding DisplayName.Name}"/>
<TextBlock Text="{Binding Details}" FontStyle="Italic"/>
...
</StackPanel>
</ToolTip>
</mystuff:BindableToolTip.ToolTip>
</TextBlock>
...
Run Code Online (Sandbox Code Playgroud)
只需将其切换ToolTip到BindableToolTip.ToolTip相反的位置,然后添加一个新的BindableToolTip.DataContext指向您想要的任何内容.我只是将它设置为当前的DataContext,因此它最终继承了绑定到DataTemplate的viewmodel.
请注意,我嵌入了ToolTip而不是使用StaticResource.这是我原来问题中的一个错误.显然必须为每个项目生成唯一.另一种选择是使用ControlTemplate Style触发器.
一个改进可能是让BindableToolTip.DataContext注册ToolTip上的通知更改,然后我可以摆脱BindableToolTip.ToolTip.另一天的任务!
工具提示不是可视树的一部分,因为它们是基于弹出窗口的.因此,您的放置目标biding(使用Visual Tree搜索)来获得相对祖先不会工作.为什么不使用ContentHacking呢?这样就可以从ContextMenu,Popups,ToolTip等逻辑元素中攻击可视树.
声明一个StaticResource,它是任何FrameworkElement(我们需要支持数据上下文).
<UserControl.Resources ...>
<TextBlock x:Key="ProxyElement" DataContext="{Binding}" />
</UserControl.Resources>
Run Code Online (Sandbox Code Playgroud)在Visual Tree中提供内容控件,并将此静态资源"ProxyElement"设置为其内容.
<UserControl ...>
<Grid ...>
<ItemsControl x:Name="MyItemsControl"
ItemsTemplate="{StaticResource blahblah}" .../>
<ContentControl Content="{StaticResource ProxyElement}"
DataContext="{Binding ElementName=MyItemsControl}" Visibility="Collapsed"/>
Run Code Online (Sandbox Code Playgroud)上面的步骤是什么,"ProxyElement"已经连接到ItemsControl(用作DataContext),它可以作为SaticResource在任何地方使用.
现在使用此StaticResource作为工具提示中失败的任何绑定的源...
<Grid ...>
<TextBlock x:Name="ObjectText"
ToolTipService.Placement="Left"
ToolTip="{StaticResource ItemToolTip}"
PlacementTarget="{Binding Source={StaticResource ProxyElement}, Path=DataContext}" ... /> <!-- This sets the target as the items control -->
Run Code Online (Sandbox Code Playgroud)和
<ToolTip x:Key="ItemToolTip">
<StackPanel DataContext="{Binding Source={StaticResource ProxyElement}, Path=DataContext.DataContext}"><!-- sets data context of the items control -->
<TextBlock Text="{Binding DisplayName.Name}"/>
<TextBlock Text="{Binding Details}" FontStyle="Italic"/> ...
</StackPanel>
</ToolTip>
Run Code Online (Sandbox Code Playgroud)
如果这有帮助,请告诉我......
| 归档时间: |
|
| 查看次数: |
9556 次 |
| 最近记录: |