Phil Jollans
Phil Jollans

Reputation: 3759

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

I want to show a list of items in a WPF ListView and use a tooltip to show additional details of an item in the list.

I have made a sample project which you can download from http://www.jollans.com/WpfTooltipTest.zip

For the sample I have made a class with some properties:

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; }
}

and a model which contains a list of these items

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" } ) ;
  }
}

The main window just instantiates the model and sets it as the DataContext

public partial class MainWindow : Window
{
  private Model   _model ;

  public MainWindow()
  {
    InitializeComponent();

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

The basic ListView is something like this:

<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>

but this doesn't have any tooltips.

I know that one way is to add something like:

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

which shows a single property from the ItemClass, but I want something much more elaborate.

I have defined a style resource containing a grid as a tooltip:

<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>

I have added a third column to the grid with a TextBlock, which uses this tooltip.

<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>

(I don't really want the TextBlock, but that is not the subject of this question.)

The tooltip is displayed and shows the grid, but the bindings don't work, because I haven't set up the DataContext. I have defined four rows in the grid which should display fields from the ItemClass.

How can I set up the DataContext in the tooltip, so that I can bind to properties of the item in the grid where the tooltip is shown?

Upvotes: 0

Views: 878

Answers (4)

SledgeHammer
SledgeHammer

Reputation: 7681

When you just blindly target a Tooltip, the bindings aren't going to work because {Binding} is going to point to the Tooltip itself, not the ListViewItem. Your Style solution will work, but you'd need to use a RelativeSource binding to chase up to the ListViewItem... or for some syntactic sugar, you can bind the DataContext of the Tooltip in the style to the ListViewItem with RelativeSource binding and then {Binding} will work throughout the style, but you'd lose the ability to bind to the tooltip properties.

Upvotes: 0

Phil Jollans
Phil Jollans

Reputation: 3759

Thanks @sledgehammer.

I have moved the definition of the grid out of the Window.Resources into the ListView defintion:

<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>

Fantastic. This works as I originally wanted.

I would still be interested if there is a way to do it if the style is defined as a resource, but that is just academic.

Upvotes: 0

SledgeHammer
SledgeHammer

Reputation: 7681

The ToolTip property is an object, so you can just use Setter.Value syntax:

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

The tooltip will be rendered via a ContentPresenter, so anything in there will show correctly.

Upvotes: 0

Brian Gradin
Brian Gradin

Reputation: 2275

If I'm reading your example correctly, the ListView is inside the third column of the Grid in the DataTemplate in your Window.Resources. If this is the case and you want to access the DataContext of the Grid, you should be able to use TemplatedParent. In GridViewColumn.CellTemplate for the ListView:

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

...or something similar.

Upvotes: 0

Related Questions