none
Trouble with Tooltips on ListBoxItem RRS feed

  • Question

  • I have s skinny ListBox with long items. The user simply wants a tooltip to appear when the mouse is over a ListBoxItem.

    The following solution (which I will post below) kind of works, but there's at least a few things I don't like and would like to see if there's a better way to solve the problem. Some of the issues I have with this implementation:

    1) I restyle the ListBoxItem with an EventSetter - this seems to cause a " first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in UIAutomationProvider.dll"

    2) I had to create a ToolTipText property in the code behind and wait until the event is over the TextBlock in order to change it.

    Here's the XAML:

    <Window x:Class="SDKSample.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
      xmlns:src="clr-namespace:SDKSample"
      xmlns:dat="clr-namespace:System.Windows.Data;assembly=PresentationFramework" 
      Title="CollectionViewSourceSample" x:Name="SampleListControl">
        <Window.Resources>
            <src:Places x:Key="places"/>
            <XmlDataProvider x:Key="myTasks" XPath="Tasks/Task">
                <x:XData>
                    <Tasks xmlns="">
                        <Task Name="Groceries" Priority="2" Type="Home">
                            <Description>Pick up Groceries and Detergent</Description>
                        </Task>
                        <Task Name="Laundry" Priority="2" Type="Home">
                            <Description>Do Laundry</Description>
                        </Task>
                        <Task Name="Email" Priority="1" Type="Work">
                            <Description>Email Clients</Description>
                        </Task>
                        <Task Name="Clean" Priority="3" Type="Work">
                            <Description>Clean my office</Description>
                        </Task>
                        <Task Name="Dinner" Priority="1" Type="Home">
                            <Description>Get ready for family reunion</Description>
                        </Task>
                        <Task Name="Proposals" Priority="2" Type="Work">
                            <Description>Review new budget proposals</Description>
                        </Task>
                    </Tasks>
                </x:XData>
            </XmlDataProvider>
            <CollectionViewSource x:Key="mySortedTasks"
                                  Source="{StaticResource myTasks}">
                <CollectionViewSource.SortDescriptions>
                    <scm:SortDescription PropertyName="@Priority" />
                </CollectionViewSource.SortDescriptions>
                <CollectionViewSource.GroupDescriptions>
                    <dat:PropertyGroupDescription PropertyName="@Priority" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>
            <Style x:Key="ListBoxItemToolTipStyle" TargetType="{x:Type ListBoxItem}">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                <Setter Property="Padding" Value="2,0,0,0"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="true">
                                    <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                                </Trigger>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsSelected" Value="true"/>
                                        <Condition Property="Selector.IsSelectionActive" Value="false"/>
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                                </MultiTrigger>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <EventSetter Event="MouseEnter" Handler="MouseOver">
                </EventSetter>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="ToolTip" Value="{Binding ElementName = SampleListControl, Path=ToolTipText}"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <DockPanel>
            <ListBox ItemsSource ="{Binding Source={StaticResource mySortedTasks}}" ItemContainerStyle="{DynamicResource ListBoxItemToolTipStyle}">
                <ListBox.Resources>
                    <Style TargetType="{x:Type ListBox}">
                        <Setter Property="Background"
                    Value="Ivory" />
                    </Style>
                </ListBox.Resources>
            </ListBox>
        </DockPanel>
    </Window>
    Here's the codebehind:

    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Controls;
    using System.ComponentModel;
    
    namespace SDKSample
    {
        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
    
        public partial class Window1 : Window, INotifyPropertyChanged
        {
            public Window1()
            {
                InitializeComponent();
            }
    
            private string _ToolTipText;
            public string ToolTipText
            {
                get
                {
                    return _ToolTipText;
                }
                set
                {
                    if (_ToolTipText != value)
                    {
                        _ToolTipText = value;
                        OnPropertyChanged("ToolTipText");
                    }
                }
            }
    
    
            private void MouseOver(object sender, RoutedEventArgs e)
            {
                MouseEventArgs args = e as MouseEventArgs;
                MouseDevice device = args.MouseDevice;
                TextBlock textBlock = device.DirectlyOver as TextBlock;
    
                if (textBlock != null)
                {
                    ToolTipText = textBlock.Text;
                }
            }
    
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
            #endregion
        }
    }
    Is there a more elegant and simplistic way to do this?


    Hedley
    Wednesday, March 17, 2010 6:28 PM

Answers

  • I think you can do what you want with an ItemTemplate for your ListBox.  Just set up a DataTeplate for your Tasks and bind the ToolTip there.
    • Marked as answer by Hedley Sohn Wednesday, March 17, 2010 9:52 PM
    Wednesday, March 17, 2010 7:41 PM

All replies

  • I think you can do what you want with an ItemTemplate for your ListBox.  Just set up a DataTeplate for your Tasks and bind the ToolTip there.
    • Marked as answer by Hedley Sohn Wednesday, March 17, 2010 9:52 PM
    Wednesday, March 17, 2010 7:41 PM
  • I forget about those DataTemplate. That'll work, thanks!
    Hedley
    Wednesday, March 17, 2010 9:53 PM