当应用失去焦点时,最后一个焦点项目的视觉指示/ IsKeyboardFocusWithin的替代方法

问题描述 投票:0回答:2

在具有多个面板或文档要与之交互的应用程序中,需要明确指出应用程序的哪个区域具有焦点。 Visual Studio本身就是一个很好的例子。

以下MCV Example接近达到所需的效果。但是,由于它使用IsKeyboardFocusWithin,因此当应用程序自身失去焦点时,不会保留应用程序中最近关注的项目。

所需行为:当应用程序失去焦点时,将保留由蓝色“ SelectedColor”指示的焦点项目。 Visual Studio会这样做。

当应用失去焦点时如何保持焦点指示?

Example Image

  • 代码

    注意:没有代码隐藏。这是完整的示例。

    <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>
    
wpf user-interface mvvm focus datatrigger
2个回答
1
投票

部分答案:

当窗口失去焦点时,可以通过将MultiDataTrigger Condition更改为IsFocused而不是IsKeyboardFocusWithin来使焦点突出的选项卡保持蓝色,如下所示:

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

但是,没有直接的方法可以知道何时将TabControl的子项聚焦,以便使边框变成蓝色。


0
投票

[按照@Andy的建议,我创建了一个LastFocusedControl属性并从中触发。此解决方案完全按预期工作,虽然有点冗长。

编辑:一旦允许焦点转移到选项卡内容内的控件,此解决方案就会瓦解。将答案留在这里,以便在找到解决方法时可以对其进行更新。

如果有人有更好的解决方案,请分享!

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 { };
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.