在具有多个面板或文档要与之交互的应用程序中,需要明确指出应用程序的哪个区域具有焦点。 Visual Studio本身就是一个很好的例子。
以下MCV Example接近达到所需的效果。但是,由于它使用IsKeyboardFocusWithin
,因此当应用程序自身失去焦点时,不会保留应用程序中最近关注的项目。
所需行为:当应用程序失去焦点时,将保留由蓝色“ SelectedColor”指示的焦点项目。 Visual Studio会这样做。
当应用失去焦点时如何保持焦点指示?
注意:没有代码隐藏。这是完整的示例。
<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>
部分答案:
当窗口失去焦点时,可以通过将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的子项聚焦,以便使边框变成蓝色。
[按照@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 { };
}
}