пятница, 23 декабря 2016 г.

Замена стандартной подсветки в TreeView

Пятница, голова уже не варит, максимум на что ее еще хватит написать ответ на очередной вопрос на форумах MSDN. Суть вопроса: нужно при выборе элемента в дереве не подсвечивать стандартное выделение всего TreeViewItem, а подсвечивать часть DataTemplate, который отвечает за показ элементов дерева. Т.е. сделать надо, чтобы выделение работало вот так, как на январе:

Интересно? Тогда идем под кат.



Первое с чего начнем, это с модели данных. Я особо усердствовать не буду и воспользуюсь, благо всего неделя до нового года, вот таким классом:
public class Model : DependencyObject
{
    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set { SetValue(IsSelectedProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for IsSelected.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsSelectedProperty =
        DependencyProperty.Register("IsSelected"typeof(bool), typeof(Model), new PropertyMetadata(false));
 
 
    public string Name { getset; }
 
    public Model[] Children { getset; }
 
    public static Model[] DemoData
    {
        get
        {
            var result = new Model[1];
            result[0] = new Model() { Name = "Зима" };
            result[0].Children = new[] { new Model() { Name = "Декабрь" }, new Model() { Name = "Январь" }, new Model() { Name = "Февраль" } };
            return result;
        }
    }
}
Теперь переходим к разметке.
Добавляем в дерево загрузку данных:
<TreeView ItemsSource="{Binding Source={x:Static local:Model.DemoData}}">
Теперь убираем подсветку выбранной ячейки. Для этого меняем цвет фона выбранного элемента и цвет текста выбранного элемента:
<TreeView.Resources>
    <Style TargetType="TreeViewItem">
        <Style.Resources>
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
        </Style.Resources>
        <Setter Property="IsSelected" Value="{Binding IsSelected}" />
    </Style>
</TreeView.Resources>
Теперь, чтобы не заморачиваться с DataTriger-ами добавим простенький конвертер:
public class IsSelectedToBrushConverter : IValueConverter
{
    static Brush HighlightBrush = new SolidColorBrush(Colors.LightBlue);
 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Brush result = null;
        if (value is bool && (bool)value)
        {
            result = HighlightBrush;
        }
        return result;
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Подключаем этот конвертер в ресурсы окна:
<Window.Resources>
    <local:IsSelectedToBrushConverter x:Key="IsSelectedToBrushConverter" />
</Window.Resources> 
Все, осталось сделать HierarchicalDataTemplate, в котором будет подсвечивать только то, что нам нужно:
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
    <StackPanel Orientation="Horizontal">
        <Image Source="Images/snowflake.PNG" Width="20" />
        <TextBlock Text="{Binding Name}" Background="{Binding IsSelected,Converter={StaticResource IsSelectedToBrushConverter}}" />
        <Image Source="Images/snowflake.PNG" Width="20" />
    </StackPanel>
</HierarchicalDataTemplate>
Как все это выглядит, можно посмотреть в картинке из заголовка поста.

1 комментарий:

  1. Я вообще отказался от TreeView, вместо этого, сделал своё представление коллекции в итоге в любом контроле унаследованном от ItemsControl'а можно интегрировать дерево, при этом есть поддержка MultiSelect'а, адекватная вертикальная виртуализация, возможность использовать дерево в ComboBox'е, DataGrid'е без особых заморочек.
    При этом мне не надо тащить в элементы дерева свойства IsSelected и IsExpanded, так как всё это делается на уровне обёртки в представлении коллекции.
    + имхо, реализовывать в шаблоне выделение не очень по MVVM, по идее, цвет выделения лучше менять в шаблоне контрола, а содержимое этого шаблона можно разбить на части (как вариант).
    Так как выделение (CurrentItem) не обязательно IsSelected.
    за CurrentItem отвечает представление коллекции (если работает синхронизация), а за IsSelected всего лишь свойство зависимости элемента дерева. + при виртуализации элементы которые находятся за ViewPort'ом не связываны с моделью представления.

    ОтветитьУдалить