VisualStateManager-集中控制时显示鼠标悬停状态 [英] VisualStateManager -- showing mouseover state when control is focused

查看:128
本文介绍了VisualStateManager-集中控制时显示鼠标悬停状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Windows 8样式(以前称为Metro)创建WPF按钮.

I am creating a WPF button using Windows 8 styling (formerly metro).

我希望按钮的聚焦状态以纯色背景显示.当鼠标悬停在控件上时,我希望背景稍微变暗,以创建可以单击按钮的视觉提示.

I would like the focused state of the button to show with a solid background. When the mouse is over the control, I would like th background to darken slightly to create the visual cue that the button can be clicked.

不幸的是,我在下面编写的XAML无法正常工作.焦点状态可以正确显示,但是当鼠标悬停在控件上时,背景不会像我希望的那样变暗.

Unfortunately, the XAML I've written below does not work. The focused state shows correctly, but when the mouse is over the control, the background does not darken as I would like it to.

<Color x:Key="DoxCycleGreen">
    #FF8DC63F
</Color>

<!-- Soft Interface : DoxCycle Green --> 
<Color x:Key="DoxCycleGreenSoft">
    #FFC0DC8F
</Color>

<Style x:Key="MetroButton" TargetType="{x:Type Button}">
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Name="RootElement">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup Name="CommonStates">
                            <VisualState Name="Normal" />
                            <VisualState Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundColor" Storyboard.TargetProperty="Color" To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" Storyboard.TargetProperty="Color" To="White" Duration="0:0:0.150" />
                                </Storyboard>
                            </VisualState>
                            <VisualState Name="Focused">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundColor" Storyboard.TargetProperty="Color" To="{StaticResource DoxCycleGreenSoft}" Duration="0:0:0.150" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" Storyboard.TargetProperty="Color" To="White" Duration="0:0:0.150" />
                                </Storyboard>
                            </VisualState>
                            <VisualState Name="Pressed">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundColor" Storyboard.TargetProperty="Color" To="Transparent" Duration="0:0:0.150" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" Storyboard.TargetProperty="Color" To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
                                </Storyboard>
                            </VisualState>
                            <VisualState Name="Disabled">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BorderColor" Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />                                        
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>


                    <Grid Background="Transparent" >
                        <Border BorderThickness="1,1,1,1" Padding="2">
                            <Border.BorderBrush>
                                <SolidColorBrush x:Name="BorderColor" Color="{StaticResource DoxCycleGreen}"/>
                            </Border.BorderBrush>
                            <Border.Background>
                                <SolidColorBrush x:Name="BackgroundColor" Color="White"/>
                            </Border.Background>

                            <ContentPresenter 
                                              x:Name="ContentSite" 
                                              VerticalAlignment="Center" 
                                              HorizontalAlignment="Center" 
                                              ContentSource="Content">
                                <TextBlock.Foreground>
                                    <SolidColorBrush x:Name="FontColor" Color="{StaticResource DoxCycleGreen}"/>
                                </TextBlock.Foreground>
                            </ContentPresenter>
                        </Border>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

推荐答案

我现在已经测试了您的代码.您在这里遇到了几个问题.但是主要问题是WPF控件一次只能处于特定状态组的一个可视状态.并且在您遇到的情况下,该控件可以是无论是集中精力还是悬而未决,WPF都必须选择适用哪个国家(由于这两个国家属于同一国家组,因此不能同时适用两个国家).因此,在这种情况下,只需将其保持在Focused状态,而不将其发送到MouseOver状态.

I've now tested your code. You've got a couple of issues at play here. But the main issue is that a WPF control can only be in one Visual State of a particular State Group at a time. And in cases like you've got here, where the control can be both focused and moused-over, WPF has to make choice of which State to apply (it cannot apply both States since they are in the same State Group). So in this case it is just keeping it in the Focused state and not sending it to the MouseOver state.

一个控件 可以处于多个状态.从此文档:

A control can be in multiple states if each of those states are in different state groups. From this documentation:

每个VisualStateGroup包含互斥的VisualState对象的集合.也就是说,在每个VisualStateGroup中,控件始终处于完全正确的一种状态.

Each VisualStateGroup contains a collection of VisualState objects that are mutually exclusive. That is, the control is always in exactly one state of in each VisualStateGroup.

因此,纠正此代码的第一步是包括适当的状态组,该状态组将使按钮能够显示其Focused状态,然后显示其MouseOver状态(其他可能性可以通过此更改来纠正,但这就是一个可能性)您特别注意到您没有采用以前的方法.

So our first step to correcting this code is to include the proper state groups that will allow the button to be able to show its Focused state and then show its MouseOver state (other possibilities can be corrected by this change but that's the one you noticed in particular that you didn't get with your previous approach).

要执行此操作,我们需要小心地命名我们的状态组,尤其是正确地命名我们的状态名.这是因为Button类内部的代码可能会像VisualStateManager.GoToState(this, "VerySpecificStateName", true);这样进行调用(我没有检查Button类的实际源代码来验证这一点,而是在需要启动状态更改的地方编写了自定义控件,我知道一定是这样的.为了获得我们需要的状态组和状态名称的列表,我们可以使用Expression Blend来编辑"控制模板的副本(它将为我们填充所需的状态),或者找到它们此处.该文档向我们显示了我们需要一个名为"FocusStates"的状态组以及该组中的两个状态"Focused"和"Unfocused"(以及其他状态组和状态).顺便说一句,为了说明Button类如何通过这些特定的命名状态来启动其状态更改,如果您通过将"Focus"状态名称替换为"MisspelledFocus"来更改原始代码,则会看到您的按钮从不输入状态.

To do this, we need to be careful to name our state groups and (especially) our state names correctly. This is because the code internal to the Button class likely makes a call like VisualStateManager.GoToState(this, "VerySpecificStateName", true); (I have not inspected the actual source code of the Button class to verify this, but having written custom controls where I've needed to initiate the state changes, I know it must be something like that). In order to get a list of the state group and state names that we'll need, we could either use Expression Blend to "edit a copy" of the control template (which will populate the needed states for us), or find them here. That documentation shows us that we need a state group called "FocusStates" and two states in that group called "Focused" and "Unfocused" (along with other state groups and states). As an aside, to illustrate how the Button class is initiating its state changes by these specific named states, if you change your original code by replacing the "Focus" state name with "MisspelledFocus", you'll see that your button never enters that state.

实施第一个更改,我们可能会得到如下结果:

Implementing this first change, we could end up with something like:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="CommonStates">
        <VisualState x:Name="Normal" />
        <VisualState x:Name="MouseOver">
            <Storyboard>
                <ColorAnimation Storyboard.TargetName="BackgroundColor" 
                                                Storyboard.TargetProperty="Color" 
                                                To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
                <ColorAnimation Storyboard.TargetName="FontColor"                                           Storyboard.TargetProperty="Color" 
                                                To="White" Duration="0:0:0.150" />
            </Storyboard>
        </VisualState>
        <VisualState x:Name="Pressed">
            <Storyboard>
                <ColorAnimation Storyboard.TargetName="BackgroundColor" 
                                                Storyboard.TargetProperty="Color" 
                                                To="Transparent" Duration="0:0:0.150" />
                <ColorAnimation Storyboard.TargetName="FontColor" 
                                                Storyboard.TargetProperty="Color" 
                                                To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
            </Storyboard>
          </VisualState>
          <VisualState x:Name="Disabled">
              <Storyboard>
                <ColorAnimation Storyboard.TargetName="BorderColor" 
                                                Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />
                <ColorAnimation Storyboard.TargetName="FontColor" 
                                                Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />                                        
              </Storyboard>
          </VisualState>
      </VisualStateGroup>
  <!-- Focus States -->
      <VisualStateGroup x:Name="FocusStates">
            <VisualState x:Name="Focused">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="BackgroundColor" 
                                                Storyboard.TargetProperty="Color" 
                                                To="{StaticResource DoxCycleGreenSoft}" Duration="0:0:0.150" />
                    <ColorAnimation Storyboard.TargetName="FontColor" 
                                                Storyboard.TargetProperty="Color" 
                                                To="White" Duration="0:0:0.150" />
                  </Storyboard>
            </VisualState>
            <VisualState x:Name="Unfocused"/>
       </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>

这在某种程度上解决了问题.但是,如果您在Expression Blend中查看此内容,则会在状态组标题中看到一条警告:

This somewhat solves the problem. However, if you're looking at this in Expression Blend, you'll notice a warning in the state group headings:

我们收到此警告,是因为我们正在多个状态组中更改同一属性/对象对的值-在本例中,该属性/对象对的名称是"BackgroundColor"的"Color"属性.为什么这可能是个问题?因为我之前说过-如果一个控件处于不同的状态组中,则该控件可以一次处于多个状态.因此,如果用户已赋予按钮焦点并且用户也将鼠标悬停在按钮上,则WPF对于应应用哪个动画可能会模棱两可,因为这两种状态都表示要对相同的精确属性进行动画处理,但使用不同的方式.

We are getting this warning because we are changing the value of an identical property/object pair in more than one state group - in this case the "Color" property of the object named "BackgroundColor". Why could this be an issue? Because of what I said earlier - the fact that a control can be in multiple states at once if those states are in different state groups. So if the user has given the button focus and the user has also moused-over the button, it could be ambiguous to WPF as to which animation to apply, since both states say to animate the same exact property but in different ways.

此外,第一个更改并未完全为我们提供我们想要的东西.如果您尝试为按钮提供焦点,然后将鼠标悬停在它上面,则它的正确位置从正常"变为焦点",再到"MouseOver".但是,如果现在停止悬停,则会看到该按钮不会返回其焦点"状态.

Also, this first change does not completely get us what we want. If you try giving the button focus, then hover over it, it correctly goes from "Normal", to "Focused", to "MouseOver". But if you now discontinue hovering, you'll see that the button does not return to its "Focused" state.

您可以使用多种方法来解决此问题,并实现与您想要的目标类似的功能,但仅作为示例,我们可以执行类似的操作. (这可能不是最干净的实现,但是它解决了常见的对象/属性问题.):

There are several approaches you could use to remedy this problem and achieve something similar to what you wanted, but just as an example, we could do something like this. (This may not be the cleanest implementation for this, but it fixes the common object/property issue.):

<Color x:Key="DoxCycleGreen">
    #FF8DC63F
</Color>

<SolidColorBrush x:Key="DoxCycleGreenBrush" Color="{StaticResource DoxCycleGreen}" />

<!-- Soft Interface : DoxCycle Green --> 
<Color x:Key="DoxCycleGreenSoft">
    #FFC0DC8F
</Color>

<SolidColorBrush x:Key="DoxCycleGreenSoftBrush" Color="{StaticResource DoxCycleGreenSoft}" />

<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Name="RootElement">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundColor" 
                                                    Storyboard.TargetProperty="Color" 
                                                    To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" 
                                                    Storyboard.TargetProperty="Color" 
                                                    To="White" Duration="0:0:0.150" />
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="MouseOverBorder">
                                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundColor" 
                                                    Storyboard.TargetProperty="Color" 
                                                    To="Transparent" Duration="0:0:0.150" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" 
                                                    Storyboard.TargetProperty="Color" 
                                                    To="{StaticResource DoxCycleGreen}" Duration="0:0:0.150" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BorderColor" 
                                                    Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />
                                    <ColorAnimation Storyboard.TargetName="FontColor" 
                                                    Storyboard.TargetProperty="Color" To="DarkGray" Duration="0:0:0.1" />                                        
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <!-- Focus States -->
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="0:0:0.15"/>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Focused">
                                <Storyboard>

                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" 
                                                                   Storyboard.TargetName="FocusBorder">
                                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ContentSiteWhiteForeground">
                                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>


                    <Grid Background="Transparent" >
                        <Border x:Name="BaseBorder" BorderThickness="1,1,1,1">
                            <Border.BorderBrush>
                                <SolidColorBrush x:Name="BorderColor" Color="{StaticResource DoxCycleGreen}"/>
                            </Border.BorderBrush>
                            <Border.Background>
                                <SolidColorBrush x:Name="BackgroundColor" Color="White"/>
                            </Border.Background>
                        </Border>
                        <Border x:Name="FocusBorder" 
                                BorderThickness="1,1,1,1" 
                                Background="{DynamicResource DoxCycleGreenSoftBrush}" 
                                Opacity="0" />

                        <Border x:Name="MouseOverBorder" 
                                BorderThickness="1,1,1,1"
                                Background="{DynamicResource DoxCycleGreenBrush}" 
                                Opacity="0" />

                        <ContentPresenter 
                            x:Name="ContentSite" 
                            VerticalAlignment="Center" 
                            HorizontalAlignment="Center" 
                            ContentSource="Content" Margin="2">
                            <TextBlock.Foreground>
                                <SolidColorBrush x:Name="FontColor" Color="{StaticResource DoxCycleGreen}"/>
                            </TextBlock.Foreground>
                        </ContentPresenter>

                        <ContentPresenter 
                            x:Name="ContentSiteWhiteForeground"

                            VerticalAlignment="Center" 
                            HorizontalAlignment="Center" 
                            ContentSource="Content" Margin="2" Opacity="0">
                            <TextBlock.Foreground>
                                <SolidColorBrush Color="White" />
                            </TextBlock.Foreground>
                        </ContentPresenter>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

现在您将看到我们已经消除了WPF的歧义.而且我们看到它现在可以处理状态从正常"更改为焦点"再更改为"MouseOver"再正确返回到焦点"的情况.

Now you'll see that we have removed the ambiguity for WPF. And we see that it now handles the case of state changes from "Normal" to "Focus" to "MouseOver" and back to "Focus" correctly.

这篇关于VisualStateManager-集中控制时显示鼠标悬停状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆