Visual indication of last focused item when application loses focus / alternative to IsKeyboardFocusWithin

↘锁芯ラ 提交于 2019-12-10 12:10:58

问题


In an application with multiple panels or documents to interact with, one needs a clear indication of which area of the app has focus. Visual Studio itself is a good example of this.

The following MCV Example is close to achieving the desired effect.
However, because it uses IsKeyboardFocusWithin, the most recently focused item in the application is not maintained when the application itself loses focus.

Desired Behavior: The focused item indicated by blue "SelectedColor" is maintained when the application loses focus. Visual Studio does this.

How can the indication of focus be maintained when the app loses focus?

  • Code

    Note: There is no code-behind. This is the complete example.

    <Window x:Class="TrackFocusMCVE.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
        Title="MainWindow" Height="150" Width="300">
    <Window.Resources>
    
        <SolidColorBrush x:Key="MouseOverColor" Color="#FF1C97EA"/>
        <SolidColorBrush x:Key="SelectedColor" Color="#FF007ACC"/>
        <SolidColorBrush x:Key="InactiveColor" Color="#19FFFFFF"/>
        <SolidColorBrush x:Key="BackgroundColor" Color="#FF44454B"/>
    
        <Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
            <Setter Property="Margin" Value="5,15,5,7"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="BorderThickness" Value="0,2,0,0"/>
            <Setter Property="BorderBrush" Value="{DynamicResource InactiveColor}"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="{StaticResource BackgroundColor}"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="TabControl.BorderBrush" Value="{StaticResource SelectedColor}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    
        <Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
            <Setter Property="Padding" Value="15,2"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Border Name="TabBorder" MinWidth="40" MinHeight="20"
                                BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
                                Margin="{TemplateBinding Margin}" Padding="{TemplateBinding Padding}"
                                Background="{TemplateBinding Background}">
                            <ContentPresenter ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true" />
                                    <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
                                </MultiDataTrigger.Conditions>
                                <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource MouseOverColor}"/>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="false" />
                                    <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource InactiveColor}"/>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="true" />
                                    <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
                                </MultiDataTrigger.Conditions>
                                <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource SelectedColor}"/>
                            </MultiDataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
        <Grid Background="DimGray">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TabControl Grid.Column="0" x:Name="tc1" Style="{DynamicResource TabControlStyle}">
                <TabItem Header="1" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="2" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="3" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
            </TabControl>
            <TabControl Grid.Column="1" x:Name="tc2" Style="{DynamicResource TabControlStyle}">
                <TabItem Header="4" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="5" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="6" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
            </TabControl>
        </Grid>
    </Window>
    

回答1:


Partial answer:

You can make your focused tab stay blue when the window loses focus by changing your MultiDataTrigger Condition to use IsFocused instead of IsKeyboardFocusWithin like this:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding IsFocused, RelativeSource={RelativeSource Self}}" Value="true" />
        <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true" />
    </MultiDataTrigger.Conditions>
    <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource SelectedColor}" />
</MultiDataTrigger>

However, there is no straight forward way to know when a child of the TabControl is focused so that you can pain the border blue.




回答2:


Taking @Andy 's advice, I created a LastFocusedControl property and trigger off of that.
This solution works exactly as desired, albiet a tad verbose.

Edit: This solution falls apart as soon as you allow focus to shift to a control inside the tab content. Leaving the answer here, so I can update it if I find a work-around.

If anyone has a better solution, please share!

MainWindow.xaml

    <Window x:Class="TrackFocusMCVE.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
            Title="MainWindow" Height="150" Width="300">
        <Window.Resources>

            <SolidColorBrush x:Key="MouseOverColor" Color="#FF1C97EA"/>
            <SolidColorBrush x:Key="SelectedColor" Color="#FF007ACC"/>
            <SolidColorBrush x:Key="InactiveColor" Color="#33FFFFFF"/>
            <SolidColorBrush x:Key="BackgroundColor" Color="#FF44454B"/>

            <Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
                <Setter Property="Margin" Value="5,15,5,7"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="BorderThickness" Value="0,2,0,0"/>
                <Setter Property="BorderBrush" Value="{DynamicResource InactiveColor}"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="Background" Value="{StaticResource BackgroundColor}"/>
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding LastFocusedControl}" Value="1">
                        <Setter Property="TabControl.BorderBrush" Value="{StaticResource SelectedColor}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>

            <Style x:Key="TabControlStyle2" TargetType="{x:Type TabControl}">
                <Setter Property="Margin" Value="5,15,5,7"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="BorderThickness" Value="0,2,0,0"/>
                <Setter Property="BorderBrush" Value="{DynamicResource InactiveColor}"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="Background" Value="{StaticResource BackgroundColor}"/>
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding LastFocusedControl}" Value="2">
                        <Setter Property="TabControl.BorderBrush" Value="{StaticResource SelectedColor}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>

            <Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
                <Setter Property="Padding" Value="15,2"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TabItem}">
                            <Border Name="TabBorder" MinWidth="40" MinHeight="20"
                                    BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    Margin="{TemplateBinding Margin}" 
                                    Padding="{TemplateBinding Padding}"
                                    Background="{TemplateBinding Background}">
                                <ContentPresenter ContentSource="Header" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <MultiDataTrigger>
                                    <MultiDataTrigger.Conditions>
                                        <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true" />
                                        <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
                                    </MultiDataTrigger.Conditions>
                                    <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource MouseOverColor}"/>
                                </MultiDataTrigger>
                                <MultiDataTrigger>
                                    <MultiDataTrigger.Conditions>
                                        <Condition Binding="{Binding IsFocused, RelativeSource={RelativeSource Self}}" Value="false" />
                                        <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    </MultiDataTrigger.Conditions>
                                    <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource InactiveColor}"/>
                                </MultiDataTrigger>
                                <MultiDataTrigger>
                                    <MultiDataTrigger.Conditions>
                                        <Condition Binding="{Binding IsFocused, RelativeSource={RelativeSource Self}}" Value="true" />
                                        <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
                                    </MultiDataTrigger.Conditions>
                                    <Setter TargetName="TabBorder" Property="Background" Value="{StaticResource SelectedColor}"/>
                                </MultiDataTrigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Resources>

        <Grid Background="DimGray">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TabControl Grid.Column="0" x:Name="tc1" Style="{DynamicResource TabControlStyle}" GotFocus="Tc1_GotFocus" LostFocus="Tc1_LostFocus">
                <TabItem Header="1" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="2" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="3" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
            </TabControl>
            <TabControl Grid.Column="1" x:Name="tc2" Style="{DynamicResource TabControlStyle2}" GotFocus="Tc2_GotFocus" LostFocus="Tc2_LostFocus">
                <TabItem Header="4" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="5" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
                <TabItem Header="6" Content="Tab Content" Style="{DynamicResource TabItemStyle}"/>
            </TabControl>
        </Grid>
    </Window>

MainWindow.xaml.cs

namespace TrackFocusMCVE
{
    using System.Windows;
    using System.ComponentModel;
    using System.Windows.Controls;

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private int _lastFocusedControl = 0;
        public int LastFocusedControl
        {
            get { return _lastFocusedControl; }
            set { _lastFocusedControl = value; RaisePropertyChanged(nameof(LastFocusedControl)); }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            TabItem item = (TabItem)tc1.Items[0];
            item.Focus(); // Default Focus
        }

        private void Tc1_GotFocus(object sender, RoutedEventArgs e) { LastFocusedControl = 1; }
        private void Tc1_LostFocus(object sender, RoutedEventArgs e) { LastFocusedControl = 0; }
        private void Tc2_GotFocus(object sender, RoutedEventArgs e) { LastFocusedControl = 2; }
        private void Tc2_LostFocus(object sender, RoutedEventArgs e) { LastFocusedControl = 0; }

        private void RaisePropertyChanged(string propName) { 
            PropertyChanged(this, new PropertyChangedEventArgs(propName)); }
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
}


来源:https://stackoverflow.com/questions/58207987/visual-indication-of-last-focused-item-when-application-loses-focus-alternativ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!