WPF Focused Visual State 仅在按下按钮时才会启动,为啥?

Posted

技术标签:

【中文标题】WPF Focused Visual State 仅在按下按钮时才会启动,为啥?【英文标题】:WPF Focused Visual State kicks in only when button pressed, why?WPF Focused Visual State 仅在按下按钮时才会启动,为什么? 【发布时间】:2014-05-12 03:18:37 【问题描述】:

关于下面的代码,谁能告诉我为什么当按钮处于正常状态和焦点状态时,按钮不反弹?换句话说,一个按钮不应该在它获得焦点状态时反弹,无论它是否被按下?

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

        <Style TargetType="x:Type Button">
            <Setter Property="FocusVisualStyle" Value="x:Null"/>
            <Setter Property="Background" Value="Black"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="x:Type Button">
                        <Grid RenderTransformOrigin=".5,.5">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup Name="CommonStates">
                                    <VisualState Name="Normal"/>
                                    <VisualState Name="MouseOver">
                                        <Storyboard>
                                            <ColorAnimation Storyboard.TargetName="outerCircle"
                                                            Storyboard.TargetProperty="(Ellipse.Fill).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)"
                                                            To="Orange" Duration="0:0:.4"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState Name="Pressed">
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To=".9" Duration="0"/>
                                            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To=".9" Duration="0"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState Name="Disabled">
                                        <Storyboard>
                                            <ColorAnimation Storyboard.TargetName="outerCircle"
                                                            Storyboard.TargetProperty="(Ellipse.Fill).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)"
                                                            To="Gray" Duration="0:0:.4"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup Name="FocusStates">
                                    <VisualState Name="Unfocused"/>
                                    <VisualState Name="Focused">
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetProperty="(Grid.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
                                                             To="-20" AutoReverse="True" RepeatBehavior="Forever" Duration="0:0:.4">
                                                <DoubleAnimation.EasingFunction>
                                                    <QuadraticEase/>
                                                </DoubleAnimation.EasingFunction>
                                            </DoubleAnimation>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform x:Name="scaleTransform"/>
                                    <TranslateTransform x:Name="translateTransform"/>
                                </TransformGroup>
                            </Grid.RenderTransform>
                            <Ellipse x:Name="outerCircle">
                                <Ellipse.Fill>
                                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                        <GradientStop Offset="0"
                                                      Color="Binding RelativeSource=RelativeSource TemplatedParent, Path=Background.Color"/>
                                        <GradientStop x:Name="highlightGradientStop" Offset="1" Color="Red"/>
                                    </LinearGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
                            <Ellipse RenderTransformOrigin=".5,.5">
                                <Ellipse.RenderTransform>
                                    <ScaleTransform ScaleX=".8" ScaleY=".8"/>
                                </Ellipse.RenderTransform>
                                <Ellipse.Fill>
                                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                        <GradientStop Offset="0" Color="White"/>
                                        <GradientStop Offset="1" Color="Transparent"/>
                                    </LinearGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
                            <Viewbox>
                                <ContentPresenter Margin="TemplateBinding Padding"/>
                            </Viewbox>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <Button Height="200" Width="200" FontSize="20" Padding="20" Margin="5">OK</Button>
        <Button Height="200" Width="200" FontSize="20" Padding="20" Margin="5">OK</Button>
        <Button Height="200" Width="200" FontSize="20" Padding="20" Margin="5">OK</Button>
        <Button Height="200" Width="200" FontSize="20" IsEnabled="False" Padding="20" Margin="5">OK</Button>
        <Button Height="200" Width="200" FontSize="20" Padding="20" Margin="5">OK</Button>
    </StackPanel>
</Window>

namespace ButtonTemplateUsingVSM

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
        
    

【问题讨论】:

【参考方案1】:

默认情况下,按钮仅在按下时才会获得焦点(除非您在代码中更改它)。您还可以获得键盘焦点。因此,您的弹跳动画仅在按下其中一个按钮时才会起作用,因为它仅设置为 Focused 状态:

<VisualStateGroup Name="FocusStates">
    <VisualState Name="Unfocused"/>  <!--Right here-->
    <VisualState Name="Focused">
        <Storyboard>
            <DoubleAnimation Storyboard.TargetProperty="(Grid.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)"
                             To="-20" AutoReverse="True" RepeatBehavior="Forever" 
                             Duration="0:0:.4">
                <DoubleAnimation.EasingFunction>
                    <QuadraticEase/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
    </VisualState>
</VisualStateGroup>

如您所见,Unfocused VisualState 没有任何与之相关的动画。因此,除非其中一个按钮获得焦点,否则它不会反弹。根据MouseOverVisualState,它只会将颜色从红色变为橙色。

我认为这对你来说是一本好书:MSDN Focus Overview

编辑:

在 cmets 中讨论的解释。

XAML:

<Window x:Class="Focus.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button Content="Button 1" HorizontalAlignment="Center" Width="75" Margin="20" GotFocus="Button1_GotFocus"/>
        <Button Content="Button 2" HorizontalAlignment="Center" Width="75" GotFocus="Button2_GotFocus"/>
    </StackPanel>
</Window>

代码隐藏:

using System.Windows;

namespace Focus

    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
        

        private void Button1_GotFocus(object sender, RoutedEventArgs e)
        
            MessageBox.Show("Button 1 Focused");
        

        private void Button2_GotFocus(object sender, RoutedEventArgs e)
        
            MessageBox.Show("Button 2 Focused");
        
    

【讨论】:

我的印象是这种情况——也就是说,一个按钮只有在按下时才处于焦点状态。但是这个例子取自“WPF 4 unleashed”一书,关于那个例子,有一个插图说明了所有的可能性(例如,MouseOver Unfocused、Disabled、Unfocused)。其中一种可能性是“Normal Focused”,这就是我发布这个问题的原因......在这种情况下,我不确定为什么提到“Normal Focused”作为用户交互可能导致的一种可能组合? @WITL 我不知道正常的专注状态。一个按钮有两个状态组。 CommonStates:Normal、MouseOver、Pressed、Disabled 和 FocuStates:未聚焦、聚焦。这本书可能有错字……很多书都有。我没有这本书,所以我不知道它到底在说什么,但我告诉你的是它是怎样的。这是一个很好的州资源:blogs.msdn.com/b/jeetenk/archive/2009/07/10/… 当您按下并释放按钮时,按钮处于 CommonStates- Normal 和 FocuStates-focused 状态,这可以是“Normal Focused”状态并且按钮仍会弹跳 @WITL 嗯...据我所知,这本书没有免费版本。在遇到麻烦之前删除该链接。但是,请阅读第 645 页底部的内容。它详细描述了我告诉您的内容。那么,当你鼠标悬停时会发生什么?它从正常状态进入鼠标悬停状态。如果按下按钮会发生什么?它从不专注的状态到专注的状态。当您将鼠标从按下的按钮上移开时会发生什么?它从 MouseOver 状态变为 Normal 状态,但是因为动画没有过渡,所以按钮会继续弹跳。 @WITL 因此,按钮只有在获得焦点时才会弹跳。您在问为什么它在 Normal Focused 状态下不反弹,但确实如此。我认为您对什么是 Focus 感到困惑。阅读我在回答中提供的链接,该链接告诉您有关 WPF 中的焦点。接收焦点的方式有很多种,但默认情况下,鼠标悬停不会接收到焦点。

以上是关于WPF Focused Visual State 仅在按下按钮时才会启动,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

Android的Button组件

在wpf中给button设置背景图片,点击button会不停的闪烁

背景选择器

在visual中建立WPF程序怎么没法建立啊?

android:state_enabled有啥作用

自定义Android系统Tab样式