Тултипы как в офисе 2007


В 2007-ом офисе появилось множество всяческих достаточно интересных элементов управления. В том числе достаточно привлекательная всплывающая подсказка (tooltip).
Тултипы как в офисе 2007

Сделать аналогичный tooltip в wpf возможно несколькими способами:
  • Написать свой класс унаследованный от ToolTip-а, ввести в нём нужные свойства (заголовок, картинка и т.д.) , сделать нужный стиль и задавать его в xaml-е подобным образом:
<Control.ToolTip>
        <controls:MyToolTip Title="ToolTip Title" Description="ToolTip Description" ImageSource="pack://application:,,,/;component/Ri.png"/> 
</Control.ToolTip>
Данный метод с точки зрения архитектуры wpf является самым правильным, но к сожалению для большого количества контролов задавать тултипы в xaml-е крайне утомительно, даже работая в Expression Blend.
  • Делаем тоже самое, что в предыдущем способе, только ещё пишем расширение разметки (markup extension) для того, чтобы упростить запись в xaml-е. Это позволяет нам сократить количество текста в xaml-е, но к сожалению в Expression Blend придётся все писать в коде, а не пользоваться интерфейсом пользователя.
  • Написать конвертор, и использовать его, чтоб преобразовывать заданную в тултипе строку внутри стиля. Этот метод с точки зрения архитектуры wpf-а не является особо правильным, но зато получаем короткую запись в xaml-е и удобство пользования интерфейса Expression Blend-а. Ниже я распишу этот метод подробнее.
Итак, напишем конвертер, который будет разбивать заданную строку на подстроки по разделителю '|':
/// <summary>
/// Конвертер - достаёт из тултипа строковой элемент по индексу
/// </summary>
[ValueConversion(typeof(string), typeof(string))]
public class RibbonToolTipConverter : IValueConverter
{
    ///<summary>
    ///Converts a value. 
    ///</summary>
    ///<returns>
    ///A converted value. If the method returns null, the valid null value is used.
    ///</returns>
    ///<param name="value">The value produced by the binding source.</param>
    ///<param name="targetType">The type of the binding target property.</param>
    ///<param name="parameter">The converter parameter to use.</param>
    ///<param name="culture">The culture to use in the converter.</param>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) return string.Empty;
        string[] res = (value as string).Split('|');
        int index = System.Convert.ToInt32(parameter);
        if (res.Length > index) return res[index];
        return string.Empty;
    }

    ///<summary>
    ///Converts a value. 
    ///</summary>
    ///<returns>
    ///A converted value. If the method returns null, the valid null value is used.
    ///</returns>
    ///<param name="value">The value that is produced by the binding target.</param>
    ///<param name="targetType">The type to convert to.</param>
    ///<param name="parameter">The converter parameter to use.</param>
    ///<param name="culture">The culture to use in the converter.</param>
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Данный конвертер будет использоваться нами в стиле тултипа, чтобы получать его элементы. В данном примере я буду использовать такие элементы: <заголовок тултипа>|<описание тултипа>|<путь к изображению> , но при необходимости естественно не составит никаких проблем расширить количество элементов.
Теперь напишем DataTemplate, который будет использовать полученные из конвертера данные:
<DataTemplate x:Key="ribbonToolTipDataTemplate">
  <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="" Margin="2" Grid.ColumnSpan="2" Grid.RowSpan="1" x:Name="fakeTextBlock" HorizontalAlignment="Left" Visibility="Hidden"/>
    <TextBlock Text="{Binding Converter={StaticResource RibbonToolTipConvert}, ConverterParameter=0}" Margin="2" FontWeight="Bold" TextWrapping="Wrap" Grid.ColumnSpan="2" Grid.RowSpan="1" x:Name="titleTextBlock" HorizontalAlignment="Left" Width="{Binding Path=Width, ElementName=fakeTextBlock}"/>
    <Image Source="{Binding Converter={StaticResource RibbonToolTipConvert}, ConverterParameter=2}" Margin="3,3,3,3" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" x:Name="image"/>
    <TextBlock Text="{Binding Converter={StaticResource RibbonToolTipConvert}, ConverterParameter=1}" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="2,2,2,2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" x:Name="descriptionTextBlock" MaxWidth="300"/>
  </Grid>
  <DataTemplate.Triggers>
    <Trigger Property="Text" Value="" SourceName="descriptionTextBlock">
      <Setter Property="Visibility" TargetName="image" Value="Collapsed"/>
      <Setter Property="Visibility" TargetName="descriptionTextBlock" Value="Collapsed"/>
      <Setter Property="FontWeight" TargetName="titleTextBlock" Value="Normal"/>
      <Setter Property="MaxWidth" TargetName="titleTextBlock" Value="300"/>
    </Trigger>
  </DataTemplate.Triggers>
</DataTemplate>
Ну и сделаем стиль визуально похожий на офис:
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}">
  <Setter Property="Placement" Value="Bottom"/>
  <Setter Property="VerticalOffset" Value="3"/>
  <Setter Property="Padding" Value="2"/>
  <Setter Property="ToolTipService.ShowDuration" Value="30000"/>
  <Setter Property="ContentTemplate" Value="{StaticResource ribbonToolTipDataTemplate}"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ToolTip}">
        <Grid>
          <Border HorizontalAlignment="Stretch" Margin="2,2,0,0" Width="Auto" BorderThickness="0,0,2,2" BorderBrush="#2A000000" CornerRadius="2,2,2,2"/>
          <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Margin="0,0,2,2" BorderThickness="1,1,1,1" BorderBrush="#FF767676" CornerRadius="2,2,2,2">
            <Border.Background>
              <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFFEFEFE" Offset="0"/>
                <GradientStop Color="#FFCADAEF" Offset="1"/>
              </LinearGradientBrush>
            </Border.Background>
            <ContentPresenter Width="Auto" Height="Auto" Margin="{TemplateBinding Padding}"/>
          </Border>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
Вот и всё, задавая ToolTip="Button 1 Tooltip|Button 1 Tooltip Long Long Long Long Description|pack://application:,,,/;component/Ri.png" мы получим tooltip близкий, к тому, что реализован в офисе 2007.

Скачать исходники:


PS. Данный пример не претендует на какую-либо завершённость.

КОММЕНТАРИИ


НОВЫЙ КОММЕНТАРИЙ


*жирный*
_курсив_
+подчеркнутый+
! заголовок 1
!! заголовок 2
* список
** список 2
# нумерованый список
## нумерованый список 2
[url:http://www.example.com]
{"без форматирования"}
Полное описание синтаксиса