如何在 XAML ListView 的复杂工具提示中定义 DataContext

How to define the DataContext in a complex tooltip on a XAML ListView

我想在 WPF ListView 中显示项目列表,并使用工具提示显示列表中项目的其他详细信息。

我制作了一个示例项目,您可以从 http://www.jollans.com/WpfTooltipTest.zip

下载

对于示例,我制作了一个具有某些属性的 class:

class ItemClass
{
  public String    mainProperty_1 { get; set; }
  public String    mainProperty_2 { get; set; }
  public String    detailProperty_1 { get; set; }
  public String    detailProperty_2 { get; set; }
}

以及包含这些项目列表的模型

class Model
{
  public   ObservableCollection<ItemClass>  Items { get; private set; }

  public Model()
  {
    Items = new ObservableCollection<ItemClass>() ;
    Items.Add ( new ItemClass { mainProperty_1 = "One",   mainProperty_2 = "First item",  detailProperty_1="A", detailProperty_2 = "1" } ) ;
    Items.Add ( new ItemClass { mainProperty_1 = "Two",   mainProperty_2 = "Second item", detailProperty_1="B", detailProperty_2 = "2" } ) ;
    Items.Add ( new ItemClass { mainProperty_1 = "Three", mainProperty_2 = "Third item",  detailProperty_1="C", detailProperty_2 = "3" } ) ;
    Items.Add ( new ItemClass { mainProperty_1 = "Four",  mainProperty_2 = "Fourth item", detailProperty_1="D", detailProperty_2 = "4" } ) ;
    Items.Add ( new ItemClass { mainProperty_1 = "Five",  mainProperty_2 = "Fifth item",  detailProperty_1="E", detailProperty_2 = "5" } ) ;
    Items.Add ( new ItemClass { mainProperty_1 = "six",   mainProperty_2 = "Sixth item",  detailProperty_1="F", detailProperty_2 = "6" } ) ;
  }
}

主要window只是实例化模型并将其设置为DataContext

public partial class MainWindow : Window
{
  private Model   _model ;

  public MainWindow()
  {
    InitializeComponent();

    _model = new Model() ;
    this.DataContext = _model ;
  }
}

基本的ListView是这样的:

<ListView ItemsSource="{Binding Items}">
  <ListView.View>
    <GridView>
      <GridViewColumn Width="100" Header="Main Property 1" DisplayMemberBinding="{Binding Path=mainProperty_1}"/>
      <GridViewColumn Width="200" Header="Main Property 2" DisplayMemberBinding="{Binding Path=mainProperty_2}"/>
    </GridView>
  </ListView.View>
</ListView>

但这没有任何工具提示。

我知道一种方法是添加如下内容:

<ListView.ItemContainerStyle>
  <Style TargetType="{x:Type ListViewItem}">
    <Setter Property="ToolTip" Value="{Binding Path=detailProperty_1}" />
  </Style>
</ListView.ItemContainerStyle>

它显​​示了 ItemClass 中的单个 属性,但我想要更详细的东西。

我定义了一个包含网格作为工具提示的样式资源:

<Window.Resources>

  <Style x:Key="ItemTooltip" TargetType="ToolTip">
    <Setter Property="ContentTemplate">
      <Setter.Value>
        <DataTemplate>

          <Grid Margin="5">
            <Grid.RowDefinitions>
              <RowDefinition/>
              <RowDefinition/>
              <RowDefinition/>
              <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="113"/>
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0">Main property 1:</TextBlock>
            <TextBlock Grid.Row="1" Grid.Column="0">Main property 2:</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="0">Detail property 1:</TextBlock>
            <TextBlock Grid.Row="3" Grid.Column="0">Detail property 2:</TextBlock>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=mainProperty_1}" />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=mainProperty_2}" />
            <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=detailProperty_1}" />
            <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Path=detailProperty_2}" />
          </Grid>

        </DataTemplate>
      </Setter.Value>
    </Setter>
  </Style>

</Window.Resources>

我用一个 TextBlock 向网格添加了第三列,它使用了这个工具提示。

<ListView ItemsSource="{Binding Items}">
  <ListView.View>
    <GridView>
      <GridViewColumn Width="100" Header="Main Property 1" DisplayMemberBinding="{Binding Path=mainProperty_1}"/>
      <GridViewColumn Width="200" Header="Main Property 2" DisplayMemberBinding="{Binding Path=mainProperty_2}"/>

      <GridViewColumn Width="200" Header="Main Property 2 again" >
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding Path=mainProperty_2}">
              <TextBlock.ToolTip>
                <ToolTip Style="{StaticResource ItemTooltip}" DataContext="{Binding}" />
              </TextBlock.ToolTip>
            </TextBlock>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>

    </GridView>
  </ListView.View>
</ListView>

(我真的不想要 TextBlock,但这不是这个问题的主题。)

显示工具提示并显示网格,但绑定不起作用,因为我还没有设置 DataContext。我在网格中定义了四行,它们应该显示 ItemClass 中的字段。

如何在工具提示中设置 DataContext,以便我可以绑定到显示工具提示的网格中项目的属性?

如果我没看错您的示例,ListView 位于 Window.ResourcesDataTemplateGrid 的第三列内。如果是这种情况并且您想访问 Grid 的 DataContext,您应该可以使用 TemplatedParent。在 GridViewColumn.CellTemplate 对于 ListView:

<DataTemplate>
    <TextBlock DataContext={Binding RelativeSouce={RelativeSource TemplatedParent}} Text="{Binding Path=mainProperty_2}">
        <TextBlock.ToolTip>
            <ToolTip Style="{StaticResource ItemTooltip}" />
        </TextBlock.ToolTip>
    </TextBlock>
</DataTemplate>

...或类似的东西。

工具提示 属性 是一个对象,因此您可以只使用 Setter.Value 语法:

<Setter Property="ToolTip">
  <Setter.Value>
     <Grid...>
     </Grid>
  </Setter.Value>
</Setter>

工具提示将通过 ContentPresenter 呈现,因此其中的任何内容都会正确显示。

感谢@sledgehammer。

我已将网格的定义从 Window.Resources 移到 ListView 定义中:

<ListView ItemsSource="{Binding Items}">

  <!-- This way of making a tooltip works, but it isn't really what I want -->
  <ListView.ItemContainerStyle>
    <Style TargetType="{x:Type ListViewItem}">
      <Setter Property="ToolTip">
        <Setter.Value>
          <Grid Margin="5">
            <Grid.RowDefinitions>
              <RowDefinition/>
              <RowDefinition/>
              <RowDefinition/>
              <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="113"/>
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0">Main property 1:</TextBlock>
            <TextBlock Grid.Row="1" Grid.Column="0">Main property 2:</TextBlock>
            <TextBlock Grid.Row="2" Grid.Column="0">Detail property 1:</TextBlock>
            <TextBlock Grid.Row="3" Grid.Column="0">Detail property 2:</TextBlock>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=mainProperty_1}" />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=mainProperty_2}" />
            <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=detailProperty_1}" />
            <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Path=detailProperty_2}" />
          </Grid>
        </Setter.Value>
      </Setter>
    </Style>
  </ListView.ItemContainerStyle>

  <ListView.View>
    <GridView>
      <GridViewColumn Width="100" Header="Main Property 1" DisplayMemberBinding="{Binding Path=mainProperty_1}"/>
      <GridViewColumn Width="200" Header="Main Property 2" DisplayMemberBinding="{Binding Path=mainProperty_2}"/>
    </GridView>
  </ListView.View>
</ListView>

太棒了。这符合我最初的要求。

如果样式被定义为资源,我仍然会感兴趣是否有办法做到这一点,但这只是学术上的。

当您只是盲目地定位工具提示时,绑定将不起作用,因为 {Binding} 将指向工具提示本身,而不是 ListViewItem。您的 Style 解决方案将起作用,但您需要使用 RelativeSource 绑定来追踪 ListViewItem ...或者对于某些语法糖,您可以将样式中工具提示的 DataContext 绑定到具有 RelativeSource 绑定的 ListViewItem,然后{Binding} 将在整个样式中起作用,但您将失去绑定到工具提示属性的能力。