WPF 使用VisualStateManager实现自定义控件闪烁呼吸警报灯

实现一个自定义控件警报灯,添加一个枚举类型的State类,包含这六个状态:

        Warning,(红灯不闪烁)
        Running,(绿灯不闪烁)
        Init,(黄灯不闪烁)
        EnableWarning,(红灯闪烁)
        EnableRunning,(绿灯闪烁)
        EnableInit,(黄灯闪烁)

要求根据不同的状态呼吸灯颜色效果相应改变,当State值为Warning、EnableWarning,时呼吸灯颜色为Red,当State值为Running、EnableRunning,时呼吸灯颜色为Green,当State值为Init、EnableInit,时呼吸灯颜色为Orange,并且当状态为Enable时触发呼吸灯的闪烁效果,我们使用Trigger来实现颜色的改变,用VisualStateManager的状态切换来实现呼吸灯闪烁效果,具体实现如下:

1.添加一个新建的自定义控件LightControl1(注意不是用户控件!

2.在Generic.xaml(此文件在添加自定义控件时会自动添加)中设置控件模板,xaml代码如下:

<Style TargetType="local:LightControl1">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:LightControl1">
                    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Border x:Name="stateLight" 
                                Background="{TemplateBinding Background}"
                                BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="50" />
                        <Border 
                                Background="Transparent"
                                BorderBrush="Gray" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="50" />
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="StateEnable">
                                <VisualState x:Name="Enable">
                                    <Storyboard>
                                        <DoubleAnimation AutoReverse="True" RepeatBehavior="Forever" Storyboard.TargetName="stateLight"
                                                         Storyboard.TargetProperty="Opacity" From="0.1" To="1"
                                                         Duration="0:0:0.3" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="UnEnable" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="State" Value="Init">
                <Setter Property="Background" Value="Orange" />
            </Trigger>
            <Trigger Property="State" Value="Warning">
                <Setter Property="Background" Value="Red" />
            </Trigger>
            <Trigger Property="State" Value="Running">
                <Setter Property="Background" Value="Green" />
            </Trigger>
            <Trigger Property="State" Value="EnableInit">
                <Setter Property="Background" Value="Orange" />
            </Trigger>
            <Trigger Property="State" Value="EnableWarning">
                <Setter Property="Background" Value="Red" />
            </Trigger>
            <Trigger Property="State" Value="EnableRunning">
                <Setter Property="Background" Value="Green" />
            </Trigger>
        </Style.Triggers>
    </Style>

3.后台代码如下:

public class LightControl1 : Control
    {
        public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
            "State", typeof(State), typeof(LightControl1), new PropertyMetadata(State.Init, OnStateChanged));

        public State State
        {
            get { return (State)GetValue(StateProperty); }
            set { SetValue(StateProperty, value); }
        }

        static LightControl1()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(LightControl1), new FrameworkPropertyMetadata(typeof(LightControl1)));
            
        }

        private void UpdateBreathingLight()
        {
            if (State == State.EnableWarning || State == State.EnableRunning || State == State.EnableInit)
            {
                VisualStateManager.GoToState(this, "Enable", true);
            }
            else
            {
                VisualStateManager.GoToState(this, "UnEnable", true);
            }
            
        }

        //OnApplyTemplate方法保证在控件的模板被应用到控件上后会被调用。
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            UpdateBreathingLight();
        }

        private static void OnStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LightControl1 control = d as LightControl1;
            control.UpdateBreathingLight();
        }

    }

public enum State
    {
        Warning,
        Running,
        Init,
        EnableWarning,
        EnableRunning,
        EnableInit
    }

4.在窗口应用该控件

<Window x:Class="StateLightControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MyNamespace="clr-namespace:StateLightControl" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:StateLightControl"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="MainWindow" Width="800"
        Height="450" mc:Ignorable="d">
    <StackPanel>
        <local:LightControl1  State="Warning" Width="50" Height="50" BorderThickness="5"/>
        <local:LightControl1 x:Name="lc1" State="Init" Width="20" Height="20" BorderThickness="2"/>
        <local:LightControl1 State="Running" Width="20" Height="20" BorderThickness="1"/>
        <local:LightControl1  State="EnableWarning" Width="50" Height="50" BorderThickness="5"/>
        <local:LightControl1  State="EnableInit" Width="20" Height="20" BorderThickness="2"/>
        <local:LightControl1 State="EnableRunning" Width="20" Height="20" BorderThickness="1"/>
        <Button x:Name="bt1" Content="Click" Click="bt1_Click" />
    </StackPanel>
</Window>

效果如图(非动图,下面三个是闪烁效果):

5. 博主在原有的样式基础上美化按钮

使用画刷给控件边框看起来更仿真,添加一个阴影效果,后台代码不用改,xaml代码如下:

<Style TargetType="local:LightControl1">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:LightControl1">
                    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Border x:Name="stateLight" 
                                Background="{TemplateBinding Background}"
                                BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="50" >
                            <Border.Effect>
                                <DropShadowEffect Color="{TemplateBinding Property=Background}" Direction="0" ShadowDepth="0" Opacity="1" BlurRadius="50"/>
                            </Border.Effect>
                        </Border>
                        <Border 
                                Background="Transparent"
                                BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="50" >
                            <Border.BorderBrush>
                                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                                    <GradientStop Color="Gray" Offset="0"/>
                                    <GradientStop Color="FloralWhite" Offset="0.5"/>
                                    <GradientStop Color="Gray" Offset="1"/>
                                </LinearGradientBrush>
                            </Border.BorderBrush>
                        </Border>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="StateEnable">
                                <VisualState x:Name="Enable">
                                    <Storyboard>
                                        <DoubleAnimation AutoReverse="True" RepeatBehavior="Forever" Storyboard.TargetName="stateLight"
                                                         Storyboard.TargetProperty="Opacity" From="0.1" To="1"
                                                         Duration="0:0:0.5" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="UnEnable" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="State" Value="Init">
                <Setter Property="Background" Value="Orange" />
            </Trigger>
            <Trigger Property="State" Value="Warning">
                <Setter Property="Background" Value="Red" />
            </Trigger>
            <Trigger Property="State" Value="Running">
                <Setter Property="Background" Value="Green" />
            </Trigger>
            <Trigger Property="State" Value="EnableInit">
                <Setter Property="Background" Value="Orange" />
            </Trigger>
            <Trigger Property="State" Value="EnableWarning">
                <Setter Property="Background" Value="Red" />
            </Trigger>
            <Trigger Property="State" Value="EnableRunning">
                <Setter Property="Background" Value="Green" />
            </Trigger>
        </Style.Triggers>
    </Style>

具体实现效果如图(非动图,具体效果自己实现看):

关于WPF自定义控件官方参考资料:创建具有可自定义外观的控件 - WPF .NET Framework | Microsoft Learn

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在WPF自定义控件使用LiveCharts显示两个图表,可以使用以下步骤: 1. 引用LiveCharts NuGet包。 2. 在自定义控件的XAML文件中,添加LiveCharts的命名空间:xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" 3. 在自定义控件的XAML文件中,添加两个LiveCharts的控件(比如LineSeries、ColumnSeries等),并分别设置它们的数据源和属性。 4. 在自定义控件的代码文件中,为每个LiveCharts控件创建一个依赖属性,并在属性变化时重新绘制图表。 下面是一个简单的示例代码,其中MyCustomControl是自定义控件,其中包含两个LiveCharts控件: ```csharp public class MyCustomControl : Control { static MyCustomControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl))); } public SeriesCollection Series1 { get { return (SeriesCollection)GetValue(Series1Property); } set { SetValue(Series1Property, value); } } public static readonly DependencyProperty Series1Property = DependencyProperty.Register("Series1", typeof(SeriesCollection), typeof(MyCustomControl), new PropertyMetadata(null, OnSeries1PropertyChanged)); private static void OnSeries1PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as MyCustomControl; control.DrawChart1(); } public SeriesCollection Series2 { get { return (SeriesCollection)GetValue(Series2Property); } set { SetValue(Series2Property, value); } } public static readonly DependencyProperty Series2Property = DependencyProperty.Register("Series2", typeof(SeriesCollection), typeof(MyCustomControl), new PropertyMetadata(null, OnSeries2PropertyChanged)); private static void OnSeries2PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as MyCustomControl; control.DrawChart2(); } private void DrawChart1() { //绘制第一个图表 chart1.Series = Series1; } private void DrawChart2() { //绘制第二个图表 chart2.Series = Series2; } private LiveCharts.Wpf.CartesianChart chart1; private LiveCharts.Wpf.CartesianChart chart2; public override void OnApplyTemplate() { base.OnApplyTemplate(); chart1 = GetTemplateChild("PART_Chart1") as LiveCharts.Wpf.CartesianChart; chart2 = GetTemplateChild("PART_Chart2") as LiveCharts.Wpf.CartesianChart; DrawChart1(); DrawChart2(); } } ``` 在自定义控件的XAML文件中,可以使用以下代码来定义两个LiveCharts控件: ```xml <Style TargetType="{x:Type local:MyCustomControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MyCustomControl}"> <Grid> <lvc:CartesianChart x:Name="PART_Chart1" /> <lvc:CartesianChart x:Name="PART_Chart2" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> ``` 然后,在使用自定义控件的地方,可以使用以下代码来设置两个图表的数据源: ```xml <local:MyCustomControl Series1="{Binding Data1}" Series2="{Binding Data2}" /> ``` 其中,Data1和Data2是两个SeriesCollection类型的数据源,可以在ViewModel中定义并绑定到视图中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值