WPF 在运行时更改按钮样式

WPF Change button style at runtime

我正在尝试使用切换开关在运行时打开或关闭样式。 我已将样式添加到资源字典中,但我不确定如何编写一些 C# 代码来加载或卸载资源。我所有的按钮都使用 "PassiveGlowButton" 的动态资源,当我使用切换开关时,我希望它删除 "PassiveGlowButton" 所以它使用 "GlowButton"

的样式

背后的代码"GlowButton" 这是我想在切换打开时应用的代码。这是在 App.Xaml 下 Application.resources, resourceDictionary:

            <ResourceDictionary>
                <Style x:Key="GlowButton"  TargetType="{x:Type Button}"
                       BasedOn="{StaticResource AccentedSquareButtonStyle}">
                    <Setter Property="BorderThickness" Value="0" />
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect  ShadowDepth="5" Color="WhiteSmoke" BlurRadius="18"/>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <EventTrigger RoutedEvent="Button.Loaded">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation 
                                 Storyboard.TargetProperty="Effect.ShadowDepth"
                                 From="3.0" To="0.0" Duration="0:0:1" 
                                 AutoReverse="True" RepeatBehavior="Forever"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <!-- Mouse over glow -->
                        <Trigger Property="IsMouseOver" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                                 Storyboard.TargetProperty="Effect.BlurRadius"
                                 From="45.0" To="17.0" Duration="0:0:1" 
                                 AutoReverse="True" RepeatBehavior="Forever"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                                 Storyboard.TargetProperty="Effect.BlurRadius"
                                 From="15.0" To="15.0" Duration="0:0:1" 
                                 AutoReverse="True" RepeatBehavior="Forever"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ResourceDictionary>

Update I have been able to set the style using a button but it will only apply to a button called Testbttn. Is there a way to change it to apply to Button.Style? If i use this method it also looses the storyboard of the button for some reason

        Style style = this.FindResource("PassiveGlowButton") as Style;
        TestBttn.Style = style;

Update 2: The solution was to create 3 styles, one the button uses from load and then 2 others, one with a blank button and one with the style i wanted. I have attached the code i used to swap between the styles.

private void ButtonStyle_Checked(object sender, RoutedEventArgs e)
    {
        Application.Current.Resources["PassiveGlowButton"] = Application.Current.Resources["PassiveGlowButtonOn"];
    }


    private void ButtonStyle_UnChecked(object sender, RoutedEventArgs e)
    {
        Application.Current.Resources["PassiveGlowButton"] = Application.Current.Resources["PassiveGlowButtonOff"];
    }

有几种方法可以做到这一点。

您的问题最好重新设计以使用 VisualStateManager

另一种选择是将样式重新设计为 StyleViewModel。 (我建议使用枚举并键入您的样式,以便 VM 可以独立于样式本身存在/引用)如果您正确执行此操作,您可以更改样式类型并且样式绑定将更新。

最后,您可以使用 DynamicResource 作为样式,并制作一个默认样式资源,在其他地方设置。样式用作资源时,可以在不同的字典中具有相同的键。名称重叠,因此最后一个(或最接近层次结构中请求它的控件)将被使用。您可以重新安排样式顺序或添加/删除它们,但控件在下次加载它们之前不会更新。

每个实现起来都有点棘手,虽然我喜欢 VisualStateManager 我自己也是绑定修复程序(选项 2)的粉丝。两者之间是有区别的;所以我不希望这让你感到困惑或引发争论。我只是在说明选项。

这里有一个绑定样式的简单示例,如果您确实喜欢走这条路线,IMO 可以解决您的问题。

示例:

样式

<Application x:Class="Question_Answer_WPF_App.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <Style x:Key="StyleA"
               TargetType="Button">
            <Setter Property="Background"
                    Value="Green" />
            <Setter Property="Height"
                    Value="40" />
            <Setter Property="Margin"
                    Value="4" />
        </Style>

        <Style x:Key="StyleB"
               TargetType="Button">
            <Setter Property="Background"
                    Value="Blue" />
            <Setter Property="Height"
                    Value="30" />
        </Style>
    </Application.Resources>
</Application>

枚举

namespace Question_Answer_WPF_App.ViewModels
{
    public enum Styles
    {
        StyleA,
        StyleB
    }
}

ViewModel

using System.Windows.Input;

namespace Question_Answer_WPF_App.ViewModels
{
    public class StylesViewModel : NotifyModel
    {
        private Styles selectedStyle;

        public StylesViewModel()
        {
            SelectStyleCommand = new RelayCommand(SelectStyle);
        }

        public Styles SelectedStyle
        {
            get { return selectedStyle; }
            set
            {
                selectedStyle = value;
                Notify();
            }
        }

        public ICommand SelectStyleCommand { get; }

        private void SelectStyle(object obj)
        {
            if (obj is Styles style) SelectedStyle = style;
        }
    }
}

转换器

using Question_Answer_WPF_App.ViewModels;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace Question_Answer_WPF_App.Views
{
    public class StyleTypeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var result = Application.Current.Resources["StyleA"];
            if (value is Styles style)
            {
                switch (style)
                {
                    case Styles.StyleB:
                        result = Application.Current.Resources["StyleB"];
                        break;
                    case Styles.StyleA:
                    default:
                        result = Application.Current.Resources["StyleA"];
                        break;
                }
            }
            return result;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            => DependencyProperty.UnsetValue;
    }
}

查看

<UserControl x:Class="Question_Answer_WPF_App.Views.StylesTestView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ViewModels="clr-namespace:Question_Answer_WPF_App.ViewModels"
             xmlns:local="clr-namespace:Question_Answer_WPF_App.Views">

    <UserControl.Resources>
        <ViewModels:StylesViewModel x:Key="StylesViewModel" />
        <local:StyleTypeConverter x:Key="StyleTypeConverter" />
    </UserControl.Resources>

    <StackPanel>
        <Button Style="{Binding SelectedStyle, Source={StaticResource StylesViewModel}, Converter={StaticResource StyleTypeConverter}}"
                Command="{Binding SelectStyleCommand, Source={StaticResource StylesViewModel}}"
                CommandParameter="{x:Static ViewModels:Styles.StyleA}"
                Content="Select Style A" />
        <Button Style="{Binding SelectedStyle, Source={StaticResource StylesViewModel}, Converter={StaticResource StyleTypeConverter}}"
                Command="{Binding SelectStyleCommand, Source={StaticResource StylesViewModel}}"
                CommandParameter="{x:Static ViewModels:Styles.StyleB}"
                Content="Select Style B" />
    </StackPanel>
</UserControl>

结果