WPF TreeView: как стилизовать выбранные элементы с закругленными углами, как в Проводнике



выбранный элемент в WPF TreeView имеет темно-синий фон с" острыми " углами. Это выглядит немного устаревшим сегодня:



WPF selected TreeViewItem with or without focus



Я хотел бы изменить фон, чтобы выглядеть как в Проводнике Windows 7 (С / без фокуса):



Explorer TreeViewItem selectedExplorer TreeViewItem selected without focus



то, что я пробовал до сих пор не удаляет оригинальный темно-синий фон, но рисует закругленную границу поверх него, так что вы видите темно-синий цвет по краям и слева сбоку-некрасиво.



enter image description here



интересно, что когда моя версия не имеет фокуса, она выглядит довольно хорошо:



enter image description here



Я хотел бы воздержаться от переопределения шаблона управления как показали здесь или здесь. Я хочу установить минимально необходимые свойства, чтобы выбранный элемент выглядит в Проводнике.



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



вот мой код:



<TreeView 
x:Name="TreeView"
ItemsSource="{Binding TopLevelNodes}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">

<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />

<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="#FF7DA2CE" />
<Setter Property="Background" Value="#FFCCE2FC" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>

<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodels:ObjectBaseViewModel}" ItemsSource="{Binding Children}">
<Border Name="ItemBorder" CornerRadius="2" Background="{Binding Background, RelativeSource={RelativeSource AncestorType=TreeViewItem}}"
BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" BorderThickness="1">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Name="icon" Source="/ExplorerTreeView/Images/folder.png"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</Border>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>




решение



с отличными ответами Шеридана и Мелика мой TreeView теперь выглядит так в коде (результат, которым я очень доволен и который довольно близок к стилю проводника):



<TreeView 
...
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- Style for the selected item -->
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<!-- Selected and has focus -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="#7DA2CE"/>
</Trigger>
<!-- Mouse over -->
<Trigger Property="helpers:TreeView_IsMouseDirectlyOverItem.IsMouseDirectlyOverItem" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFFAFBFD" Offset="0"/>
<GradientStop Color="#FFEBF3FD" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="#B8D6FB"/>
</Trigger>
<!-- Selected but does not have the focus -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
<Condition Property="IsSelectionActive" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#D9D9D9"/>
</MultiTrigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="2"/>
</Style>
</Style.Resources>
</Style>
</TreeView.ItemContainerStyle>

<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodels:ObjectBaseViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2,1,5,2">
<Grid Margin="0,0,3,0">
<Image Name="icon" Source="/ExplorerTreeView/Images/folder.png"/>
</Grid>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>

<!-- Brushes for the selected item -->
<LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDCEBFC" Offset="0"/>
<GradientStop Color="#FFC1DBFC" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="{x:Static SystemColors.ControlBrushKey}" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF8F8F8" Offset="0"/>
<GradientStop Color="#FFE5E5E5" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
</TreeView.Resources>
</TreeView>
1200   3  

3 ответов:

добавление к ответу @ Sheridan
Это не 100% точный, но должен получить вас довольно близко (он использует цвета от GridView что довольно близко к проводнику Windows)

enter image description here

<TreeView ...>
    <TreeView.Resources>
        <LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFD9F4FF" Offset="0"/>
            <GradientStop Color="#FF9BDDFB" Offset="1"/>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="{x:Static SystemColors.ControlBrushKey}" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFEEEDED" Offset="0"/>
            <GradientStop Color="#FFDDDDDD" Offset="1"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
    </TreeView.Resources>
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="BorderThickness" Value="1.5"/>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="BorderBrush" Value="#adc6e5"/>
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsSelected" Value="True"/>
                        <Condition Property="IsSelectionActive" Value="False"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="BorderBrush" Value="LightGray"/>
                </MultiTrigger>
            </Style.Triggers>
            <Style.Resources>
                <Style TargetType="Border">
                    <Setter Property="CornerRadius" Value="2"/>
                </Style>
            </Style.Resources>                    
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

добавьте это в свой TreeView.ContainerStyle удалить значение по умолчанию blue фон.

<Style.Resources>
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
    <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
    <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
</Style.Resources>

можно заменить на Black С любым цветом, который вы хотите, чтобы ваш текст элемента и выбранный текст элемента были.

чтобы иметь серый фон, когда он не сфокусирован, вы можете настроить "не сфокусированный"Style с серым backgorund и использовать EventTriggerна TreeViewItem.GotFocus и LostFocus события для переключения между Style s.

EDIT>>>

если вы хотите быть вспышка, вы можете использовать анимацию для изменения между цветами фона, добавив триггеры к вашему ItemBorder Border прямо в HierarchicalDataTemplate вот так:

<Border.Triggers>
    <EventTrigger RoutedEvent="Border.GotFocus">
        <EventTrigger.Actions>
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="YourColour" Duration="0:0:0.2" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Border.LostFocus">
        <EventTrigger.Actions>
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="LightGray" Duration="0:0:0.2" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger.Actions>
    </EventTrigger>
</Border.Triggers>

обратите внимание, что это будет работать только если ColorAnimation есть From цвета. Поскольку этот код стоит, среда выполнения будет искать SolidColorBrush установить на Border.Background собственность, поэтому вы должны установить один. Вы можете установить ColorAnimation.From свойства непосредственно вместо этого.

Windows 10 TreeView (и ListView) стиль

Я изначально искал способ применить цветовую схему Windows 10 к TreeViewItem, в том числе

  • IsMouseOver только для текущего элемента
  • Windows 10 цветов, которые WPF уже применяет их к ListBox (не Проводник Windows)

если кто-то из вас ищет именно этого, пожалуйста, не стесняйтесь взять код ниже. Я Решение Helge Klein для проблемы IsMouseOver и применил цвета Windows 10 к XAML. Поэтому я предлагаю это как дополнение к принятому ответу.

кроме того, ниже слово ListView и ComboBox как хорошо.


скриншот

example

приложение.xaml

<Style TargetType="{x:Type TreeView}">
    <Style.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#CBE8F6" />
        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#F6F6F6" />
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
    </Style.Resources>
</Style>
<Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="BorderThickness" Value="1" />
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="BorderBrush" Value="#26A0DA" />
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="local:TreeViewItemHelper.IsMouseDirectlyOverItem" Value="True" />
                <Condition Property="IsSelected" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="#E5F3FB" />
            <Setter Property="BorderBrush" Value="#70C0E7" />
        </MultiTrigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsSelected" Value="True" />
                <Condition Property="IsSelectionActive" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="BorderBrush" Value="#DADADA" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

TreeViewItemHelper (источник как написал Хельге Кляйн,незначительные изменения / упрощения)

public static class TreeViewItemHelper
{
    private static TreeViewItem CurrentItem;
    private static readonly RoutedEvent UpdateOverItemEvent = EventManager.RegisterRoutedEvent("UpdateOverItem", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TreeViewItemHelper));
    private static readonly DependencyPropertyKey IsMouseDirectlyOverItemKey = DependencyProperty.RegisterAttachedReadOnly("IsMouseDirectlyOverItem", typeof(bool), typeof(TreeViewItemHelper), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CalculateIsMouseDirectlyOverItem)));
    public static readonly DependencyProperty IsMouseDirectlyOverItemProperty = IsMouseDirectlyOverItemKey.DependencyProperty;

    static TreeViewItemHelper()
    {
        EventManager.RegisterClassHandler(typeof(TreeViewItem), UIElement.MouseEnterEvent, new MouseEventHandler(OnMouseTransition), true);
        EventManager.RegisterClassHandler(typeof(TreeViewItem), UIElement.MouseLeaveEvent, new MouseEventHandler(OnMouseTransition), true);
        EventManager.RegisterClassHandler(typeof(TreeViewItem), UpdateOverItemEvent, new RoutedEventHandler(OnUpdateOverItem));
    }
    public static bool GetIsMouseDirectlyOverItem(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsMouseDirectlyOverItemProperty);
    }
    private static object CalculateIsMouseDirectlyOverItem(DependencyObject item, object value)
    {
        return item == CurrentItem;
    }
    private static void OnUpdateOverItem(object sender, RoutedEventArgs e)
    {
        CurrentItem = sender as TreeViewItem;
        CurrentItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);
        e.Handled = true;
    }
    private static void OnMouseTransition(object sender, MouseEventArgs e)
    {
        lock (IsMouseDirectlyOverItemProperty)
        {
            if (CurrentItem != null)
            {
                DependencyObject oldItem = CurrentItem;
                CurrentItem = null;
                oldItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);
            }

            Mouse.DirectlyOver?.RaiseEvent(new RoutedEventArgs(UpdateOverItemEvent));
        }
    }
}

ListBox/ListView и ComboBox: В Windows 7 (и 8?), это приведет к тому, что дизайн от TreeView до ListBox / ListView и ComboBox будет отличаться. Поэтому, если вы хотите применить эту цветовую схему к этим типам элементов управления, также используйте это:

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border Name="Border" BorderThickness="1" Background="Transparent">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="#E5F3FB" />
                        <Setter TargetName="Border" Property="BorderBrush" Value="#70C0E7" />
                    </Trigger>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="#CBE8F6" />
                        <Setter TargetName="Border" Property="BorderBrush" Value="#26A0DA" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="True" />
                            <Condition Property="Selector.IsSelectionActive" Value="False" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="Border" Property="Background" Value="#F6F6F6" />
                        <Setter TargetName="Border" Property="BorderBrush" Value="#DADADA" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}" />
<Style TargetType="{x:Type ComboBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ComboBoxItem}">
                <Border Name="Border" BorderThickness="1" Padding="1" Background="Transparent">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="#E5F3FB" />
                        <Setter TargetName="Border" Property="BorderBrush" Value="#70C0E7" />
                    </Trigger>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="#CBE8F6" />
                        <Setter TargetName="Border" Property="BorderBrush" Value="#26A0DA" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Comments

    Ничего не найдено.