Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在按钮中管理新的可视状态_C#_Wpf_Visualstatemanager_Visualstates - Fatal编程技术网

C# 在按钮中管理新的可视状态

C# 在按钮中管理新的可视状态,c#,wpf,visualstatemanager,visualstates,C#,Wpf,Visualstatemanager,Visualstates,我想向WPF按钮添加一个新的“激活”状态,并避免从头开始重新创建控件 此新状态链接到IsActivated依赖项属性,必须更改按钮的背景色。以下是IsEnabled和IsActivated依赖属性之间交互的真值表: 我编写了一个从Button扩展而来的类,创建了依赖属性,并在IsActivated的回调中计算了按钮的可视状态 问题是ButtonBase类型已经通过管理可视状态,而不能重写 在管理两个依赖项属性的回调后,IsActivated和IsEnabled之间的交互按预期工作,但单击按钮或

我想向WPF按钮添加一个新的“激活”状态,并避免从头开始重新创建控件

此新状态链接到
IsActivated
依赖项属性,必须更改按钮的背景色。以下是
IsEnabled
IsActivated
依赖属性之间交互的真值表:

我编写了一个从Button扩展而来的类,创建了依赖属性,并在
IsActivated
的回调中计算了按钮的可视状态

问题是ButtonBase类型已经通过管理可视状态,而不能重写

在管理两个依赖项属性的回调后,
IsActivated
IsEnabled
之间的交互按预期工作,但单击按钮或将鼠标放在按钮上会覆盖
Activated
的可视状态

这是否可以通过可视状态来实现,还是应该使用简单的触发器

到目前为止,控制代码:

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace OrganizationName.BasicControls.Primitive
{
    public class MultiStateButton : Button
    {
        static MultiStateButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiStateButton), new FrameworkPropertyMetadata(typeof(MultiStateButton)));
            IsEnabledProperty.OverrideMetadata(typeof(MultiStateButton), new FrameworkPropertyMetadata(propertyChangedCallback: IsEnabledCallback));
        }

        internal void ChangeVisualState(bool useTransitions)
        {
            if (!IsEnabled)
            {
                VisualStateManager.GoToState(this, "Disabled", useTransitions);
            }
            else if (IsActivated)
            {
                VisualStateManager.GoToState(this, "Activated", useTransitions);
            }
            else if (IsPressed)
            {
                VisualStateManager.GoToState(this, "Pressed", useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, "Normal", useTransitions);
            }
        }

        #region IsEnabled override
        private static void IsEnabledCallback(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            MultiStateButton multiStateButton = o as MultiStateButton;
            if (multiStateButton == null) return;

            multiStateButton.ChangeVisualState(true);
        }
        #endregion IsEnabled override

        #region DP IsActivated 
        public bool IsActivated
        {
            get { return (bool)GetValue(IsActivatedProperty); }
            set { SetValue(IsActivatedProperty, value); }
        }

        private static void IsActivatedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            MultiStateButton multiStateButton = o as MultiStateButton;
            if (multiStateButton == null) return;

            multiStateButton.ChangeVisualState(true);
        }

        private readonly static FrameworkPropertyMetadata IsActivatedMetadata = new FrameworkPropertyMetadata
        {
            PropertyChangedCallback = IsActivatedCallback,
            DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        };

        public static readonly DependencyProperty IsActivatedProperty =
            DependencyProperty.Register("IsActivated", typeof(bool), typeof(MultiStateButton), IsActivatedMetadata);
        #endregion DP IsActivated
    }
}


按钮的默认样式:

<sys:Double x:Key="ButtonCornerRadiusValue">5</sys:Double>

<CornerRadius x:Key="ButtonCornerRadius"
              TopLeft="{StaticResource ButtonCornerRadiusValue}"
              BottomLeft="{StaticResource ButtonCornerRadiusValue}"
              TopRight="{StaticResource ButtonCornerRadiusValue}"
              BottomRight="{StaticResource ButtonCornerRadiusValue}"/>
<CornerRadius x:Key="ButtonCornerRadiusLeft"
              TopLeft="{StaticResource ButtonCornerRadiusValue}"
              BottomLeft="{StaticResource ButtonCornerRadiusValue}"/>
<CornerRadius x:Key="ButtonCornerRadiusRight"
              TopRight="{StaticResource ButtonCornerRadiusValue}"
              BottomRight="{StaticResource ButtonCornerRadiusValue}"/>
<CornerRadius x:Key="ButtonCornerRadiusTop"
              TopRight="{StaticResource ButtonCornerRadiusValue}"
              TopLeft="{StaticResource ButtonCornerRadiusValue}"/>
<CornerRadius x:Key="ButtonCornerRadiusBottom"
              BottomLeft="{StaticResource ButtonCornerRadiusValue}"
              BottomRight="{StaticResource ButtonCornerRadiusValue}"/>

<Style x:Key="{x:Type primitives:MultiStateButton}"
       TargetType="{x:Type primitives:MultiStateButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type primitives:MultiStateButton}">
                <Border TextBlock.Foreground="{TemplateBinding Foreground}"
                        x:Name="Border"
                        CornerRadius="{StaticResource ButtonCornerRadius}"
                        Background="White"
                        BorderThickness="1">
                    <Border.Effect>
                        <DropShadowEffect Color="#CDD5E3"
                                          ShadowDepth="0"
                                          BlurRadius="13"
                                          Direction="0"/>
                    </Border.Effect>
                    <Border.BorderBrush>
                        <RadialGradientBrush Center="0.5,0.5"
                                             RadiusY="1"
                                             RadiusX="5"
                                             GradientOrigin="0.5,0.5">
                            <RadialGradientBrush.GradientStops>
                                <GradientStopCollection>
                                    <GradientStop Color="Transparent"
                                                  Offset="0" />
                                    <GradientStop Color="Transparent"
                                                  Offset="0.5" />
                                    <GradientStop Color="{StaticResource BorderPushedColor}"
                                                  Offset="0.8" />
                                    <GradientStop Color="{StaticResource BorderPushedColor}"
                                                  Offset="1" />
                                </GradientStopCollection>
                            </RadialGradientBrush.GradientStops>
                        </RadialGradientBrush>
                    </Border.BorderBrush>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver"/>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetProperty="(Border.Width)"
                                                     Storyboard.TargetName="LeftBorder"
                                                     Duration="00:00:00"
                                                     To="10"/>
                                    <DoubleAnimation Storyboard.TargetProperty="(Border.Width)"
                                                     Storyboard.TargetName="RightBorder"
                                                     Duration="00:00:00"
                                                     To="10"/>
                                    <DoubleAnimation Storyboard.TargetProperty="(Border.Height)"
                                                     Storyboard.TargetName="TopBorder"
                                                     Duration="00:00:00"
                                                     To="10"/>
                                    <DoubleAnimation Storyboard.TargetProperty="(Border.Height)"
                                                     Storyboard.TargetName="BottomBorder"
                                                     Duration="00:00:00"
                                                     To="10"/>
                                    <DoubleAnimation Storyboard.TargetProperty="(Border.Effect).(DropShadowEffect.BlurRadius)"
                                                     Storyboard.TargetName="Border"
                                                     Duration="00:00:00"
                                                     To="10"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background)"
                                                                  Storyboard.TargetName="Border">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SkyblueLight}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Activated">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background)"
                                                                   Storyboard.TargetName="Border">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ReflexBlue}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid Background="Transparent">
                        <ContentPresenter Margin="2"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          RecognizesAccessKey="True"/>
                        <Border BorderThickness="0"
                                Width="0"
                                HorizontalAlignment="Left"
                                CornerRadius="{StaticResource ButtonCornerRadiusLeft}"
                                x:Name="LeftBorder">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="1,0.5" EndPoint="0,0.5">
                                    <LinearGradientBrush.GradientStops>
                                        <GradientStop Offset="0" Color="Transparent"/>
                                        <GradientStop Offset="1" Color="#D9DDE4"/>
                                    </LinearGradientBrush.GradientStops>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>
                        <Border BorderThickness="0"
                                Width="0"
                                HorizontalAlignment="Right"
                                CornerRadius="{StaticResource ButtonCornerRadiusRight}"
                                x:Name="RightBorder">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                                    <LinearGradientBrush.GradientStops>
                                        <GradientStop Offset="0" Color="Transparent"/>
                                        <GradientStop Offset="1" Color="#D9DDE4"/>
                                    </LinearGradientBrush.GradientStops>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>

                        <Border BorderThickness="0"
                                Height="0"
                                VerticalAlignment="Top"
                                CornerRadius="{StaticResource ButtonCornerRadiusTop}"
                                x:Name="TopBorder">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0.5,1" EndPoint="0.5,0">
                                    <LinearGradientBrush.GradientStops>
                                        <GradientStop Offset="0" Color="Transparent"/>
                                        <GradientStop Offset="1" Color="#D9DDE4"/>
                                    </LinearGradientBrush.GradientStops>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>

                        <Border BorderThickness="0"
                                Height="0"
                                VerticalAlignment="Bottom"
                                CornerRadius="{StaticResource ButtonCornerRadiusBottom}"
                                x:Name="BottomBorder">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                    <LinearGradientBrush.GradientStops>
                                        <GradientStop Offset="0" Color="Transparent"/>
                                        <GradientStop Offset="1" Color="#D9DDE4"/>
                                    </LinearGradientBrush.GradientStops>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
5

在向现有控件添加简单可视状态时,我通常避免创建子类,而是使用附加属性

在为现有控件实现新的可视状态时,您根本不必使用
VisualStateManager
。特别是如果您不使用动画

我建议改用
触发器

如果要继续使用
多状态按钮
控件,只需执行以下操作:

<ControlTemplate ...>
    ...
    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True" />
                <Condition Property="IsActivated" Value="True" />
            </MultiTrigger.Conditions>
            <Setter TargetName="Border" Property="Background" Value="{StaticResource ReflexBlue}" />
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate ...>
    ...
    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True" />
                <Condition Property="ap:MultiStateButtonProperties.IsActivated" Value="True" />
            </MultiTrigger.Conditions>
            <Setter TargetName="Border" Property="Background" Value="{StaticResource ReflexBlue}" />
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
然后,在样式的控件模板中,可以使用上面的多触发器并执行以下操作:

<ControlTemplate ...>
    ...
    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True" />
                <Condition Property="IsActivated" Value="True" />
            </MultiTrigger.Conditions>
            <Setter TargetName="Border" Property="Background" Value="{StaticResource ReflexBlue}" />
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate ...>
    ...
    <ControlTemplate.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True" />
                <Condition Property="ap:MultiStateButtonProperties.IsActivated" Value="True" />
            </MultiTrigger.Conditions>
            <Setter TargetName="Border" Property="Background" Value="{StaticResource ReflexBlue}" />
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

...

我希望这有帮助。

我有点困惑?有什么原因不能只使用
切换按钮
?它们的存在是为了在其
IsChecked
属性中保持状态。@Tronald这确实是我在IsActivated DP中忽略的东西。需要注意的是,我精简了这个类,它目前包含ControlTemplate中使用的其他DPs。看起来触发器是一种方法,但我想确保我没有忽略一些东西。我没有考虑使用附加属性。谢谢你的解决方案。使用触发器而不是可视状态是否有缺点?我只是开始使用这些,所以我想用视觉状态来解决这个问题。使用触发器真的没有什么缺点。事实上,如果您使用Blend for Visual Studio提取控件模板,您将看到Microsoft对几乎所有控件都使用触发器。我已经建立并维护了一个大型WPF主题库,现在几乎只使用触发器。VisualStateManager的一个缺点是故事板冻结了资源。触发器没有,而且更具动态性。