Reputation: 3759
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
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
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
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
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