WPF TreeView: как оформить выбранные элементы закругленными углами, как в Проводнике - PullRequest
56 голосов
/ 19 февраля 2011

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

WPF selected TreeViewItem with or without focus

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

Explorer TreeViewItem selected Explorer TreeViewItem selected without focus

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

enter image description here

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

enter image description here

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

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

Вот мой код:

<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>

Решение

С отличными ответами Шериданаи Meleak мой 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>

Ответы [ 3 ]

40 голосов
/ 19 февраля 2011

Добавление к ответу Шеридана
Это не на 100% точно, но должно быть достаточно близко (используются цвета GridView, что очень близко к Windows Explorer)

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>
25 голосов
/ 19 февраля 2011

Добавьте это в 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 с серым фоном и использовать EventTrigger s в событиях TreeViewItem.GotFocus и LostFocus для переключения междуStyle s.

РЕДАКТИРОВАТЬ >>>

Если вы хотите быть flash, вы можете использовать анимацию для переключения между цветами фона, добавляя триггеры к вашему 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 напрямую.

1 голос
/ 27 декабря 2016

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

Первоначально я искал способ применения цветовой схемы Windows 10 к TreeViewItem, включая

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

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

Также см. Ниже слова о ListView и ComboBox.


Скриншот

example

App.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>
...