WPF 视觉状态VisualState使用与动态主题实现

VisualState基本使用

首先搭建一个自定义控件,继承自ContentControl,自动生成了这些文件
image
由于CustomButtonCustom命名空间中,所以改一下xaml中的引用

xmlns:local="clr-namespace:WpfApp1.Custom"
  • 定义部件和视觉状态
    TemplatePart是模板中的部件名
    TemplateVisualState是模板中的样式名
[TemplatePart(Name = "P_Border")]
[TemplateVisualState(Name = mouseoverstate, GroupName = mousegroup)]
[TemplateVisualState(Name = mouseleavestate, GroupName = mousegroup)]
public class CustomButton : ContentControl
{
    public const string mouseoverstate = "mouseover";
    public const string mouseleavestate = "mouseleave";
    public const string mousegroup = "mousegroup1";
}
  • 添加样式
    推荐把视觉状态放在模板的根元素下,这个例子中是Border
<Style TargetType="{x:Type local:CustomButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomButton}">
                <Border
                        x:Name="P_Border"
                        Padding="5"
                        CornerRadius="5"
                        Background="Transparent"
                        BorderBrush="Transparent"
                        BorderThickness="0">
                    <TextBlock Text="{TemplateBinding Content}" Foreground="White" HorizontalAlignment="Center"/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="mousegroup1">
                            <!--GoToState第三个参数useTransitions为false时进入这里-->
                            <VisualState x:Name="mouseover">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="#FF16b777"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="mouseleave">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="#CC16b777"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
  • 后台代码
    这里在鼠标事件中改变视觉状态
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    VisualStateManager.GoToState(this, mouseleavestate, false);
}

protected override void OnMouseEnter(MouseEventArgs e)
{
    base.OnMouseEnter(e);
    VisualStateManager.GoToState(this, mouseoverstate, false);
}

protected override void OnMouseLeave(MouseEventArgs e)
{
    base.OnMouseLeave(e);
    VisualStateManager.GoToState(this, mouseleavestate, false);
}
  • 效果
    image

增加VisualTransition过渡效果

但这没有过渡,添加过渡需要设置GoToState(this, mouseoverstate, true)
同时还要添加过渡效果的xaml元素
需要注意的是,由于使用了(Border.Background).(SolidColorBrush.Color),所以BorderBackground必须有个预设值

<VisualStateGroup x:Name="mousegroup1">
    <!--GoToState第三个参数useTransitions为true时进入这里-->
    <VisualStateGroup.Transitions>
        <!--from是前一个状态名,to是后一个状态名-->
        <VisualTransition To="mouseover">
            <Storyboard>
                <!--(Border.Background).(SolidColorBrush.Color)是PropertyPath类型限定的单个属性语法,格式为(targetTtype.property)-->
                <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:1" To="#FF167777"/>
            </Storyboard>
        </VisualTransition>
        <VisualTransition To="mouseleave">
            <Storyboard>
                <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:1" To="#AA167777"/>
            </Storyboard>
        </VisualTransition>
    </VisualStateGroup.Transitions>
    <!--GoToState第三个参数useTransitions为false时进入这里-->
    <VisualState x:Name="mouseover">
        <Storyboard>
            <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="#FF167777"/>
        </Storyboard>
    </VisualState>
    <VisualState x:Name="mouseleave">
        <Storyboard>
            <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="#AA167777"/>
        </Storyboard>
    </VisualState>
</VisualStateGroup>

再把GoToState(this, mouseoverstate, false)改为true
但注意OnApplyTemplate中不要改,因为那里是首次加载,许需要过渡

  • 效果
    image

动态主题

  • 首先确定哪些样式需要改为资源
    1. 按钮文字颜色 FontColor
    2. 按钮背景颜色 BackgroundColor
    3. 鼠标悬浮时按钮背景颜色 HoverBackgroundColor
  • 为每个主题建立资源字典
    image
  • 设置每个主题中这些资源
//Light.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApp1.Custom">

    <SolidColorBrush x:Key="FontColor" Color="#FF000000"/>
    <Color x:Key="BackgroundColor">#FFaaaaaa</Color>
    <Color x:Key="HoverBackgroundColor">#66aaaaaa</Color>
</ResourceDictionary>
//Dark.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApp1.Custom">
	
    <SolidColorBrush x:Key="FontColor" Color="#FFffffff"/>
    <Color x:Key="BackgroundColor">#FF000000</Color>
    <Color x:Key="HoverBackgroundColor">#55000000</Color>
</ResourceDictionary>
//Normal.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
					
    <SolidColorBrush x:Key="FontColor" Color="#FFffffff"/>
    <Color x:Key="BackgroundColor">#FF167777</Color>
    <Color x:Key="HoverBackgroundColor">#AA167777</Color>
</ResourceDictionary>
  • 将控件样式中的固定值改为资源引用
    特别注意,Border的背景颜色不能用动态资源,只能用静态资源。按钮需要一个初始颜色
<SolidColorBrush x:Key="DefaultBackgoundColor" Color="#FF167777"/>
<Style TargetType="{x:Type local:CustomButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomButton}">
                <Border
                        x:Name="P_Border"
                        Padding="5"
                        CornerRadius="5"
                        Background="{StaticResource DefaultBackgoundColor}"
                        BorderBrush="Transparent"
                        BorderThickness="0">
                    <TextBlock Text="{TemplateBinding Content}" Foreground="{DynamicResource FontColor}" HorizontalAlignment="Center"/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="mousegroup1">
                            <!--GoToState第三个参数useTransitions为true时进入这里-->
                            <VisualStateGroup.Transitions>
                                <!--from是前一个状态名,to是后一个状态名-->
                                <VisualTransition To="mouseover">
                                    <Storyboard>
                                        <!--(Border.Background).(SolidColorBrush.Color)是PropertyPath类型限定的单个属性语法,格式为(targetTtype.property)-->
                                        <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:1" To="{DynamicResource BackgroundColor}"/>
                                    </Storyboard>
                                </VisualTransition>
                                <VisualTransition To="mouseleave">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:1" To="{DynamicResource HoverBackgroundColor}"/>
                                    </Storyboard>
                                </VisualTransition>
                            </VisualStateGroup.Transitions>
                            <!--GoToState第三个参数useTransitions为false时进入这里-->
                            <VisualState x:Name="mouseover">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="{DynamicResource BackgroundColor}"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="mouseleave">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="P_Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Duration="0:0:0" To="{DynamicResource HoverBackgroundColor}"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

效果

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <!--<ResourceDictionary Source="Themes/Light.xaml"/>-->
            <!--<ResourceDictionary Source="Themes/Dark.xaml"/>-->
            <ResourceDictionary Source="Themes/Normal.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

image

image

image

posted @ 2024-06-23 16:16  ggtc  阅读(63)  评论(0编辑  收藏  举报
//右下角目录