如何更改原生WPF控件主题的颜色?

如何解决如何更改原生WPF控件主题的颜色?

如何在Windows 10下更改本机WPF控制主题使用的基础颜色?我知道有类似MahApps.Metro和MUI的库,但是我要做的就是使应用程序中的元素以一致的颜色绘制(MenuItem和Toolbar,我在看你的颜色以及你不太协调的颜色) 。我还想提供各种颜色主题

我该怎么做?

我不得不承认我只是不明白我所问的是否可行。一些调查表明,认的WPF主题对按钮背景之类的东西使用静态资源:

<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>

如果该资源是静态的,我想我不能更改它?仅复制所有原始WPF模板并以某种方式用动态资源替换静态资源是否有意义?

解决方法

要想对所有控件和颜色“正确”执行此操作,比您想象的要复杂得多。

有些画笔使用Windows主题色,有些使用硬编码值。

您可以替代Windows主题颜色。

例如,在合并到app.xaml中的资源字典中,您可以为所有颜色和画笔设置自己的首选项。

这里是一个:

 <SolidColorBrush Color="LimeGreen" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>

https://docs.microsoft.com/en-us/dotnet/api/system.windows.systemcolors?view=netcore-3.1

您会发现这只会改变某些事情。

您将必须重新模板化控件以替换硬编码值。

那是什么意思?

看看单选按钮模板:

https://docs.microsoft.com/en-us/dotnet/desktop/wpf/controls/radiobutton-styles-and-templates?view=netframeworkdesktop-4.8

在其中您会看到类似以下内容的东西:

          <Ellipse x:Name="Border"
                   StrokeThickness="1">
            <Ellipse.Stroke>
              <LinearGradientBrush EndPoint="0.5,1"
                                   StartPoint="0.5,0">
                <GradientStop Color="{DynamicResource BorderLightColor}"
                              Offset="0" />
                <GradientStop Color="{DynamicResource BorderDarkColor}"
                              Offset="1" />
              </LinearGradientBrush>
            </Ellipse.Stroke>
            <Ellipse.Fill>
              <LinearGradientBrush StartPoint="0,0"
                                   EndPoint="0,1">
                <LinearGradientBrush.GradientStops>
                  <GradientStopCollection>
                    <GradientStop Color="{DynamicResource ControlLightColor}" />
                    <GradientStop Color="{DynamicResource ControlMediumColor}"
                                  Offset="1.0" />
                  </GradientStopCollection>
                </LinearGradientBrush.GradientStops>
              </LinearGradientBrush>
            </Ellipse.Fill>
          </Ellipse>

这些资源在主模板下的长列表中定义。您想替换所有这些。

无论您是想让控件全部使用Windows主题还是自己的主题,如果从头开始,您都需要做很多工作。

您可能想看看各种可用的预卷主题。材质设计非常流行,因为很多人都熟悉android。

http://materialdesigninxaml.net/

,

我最近为正在处理的应用程序创建了一些自定义主题。首先要注意的是WPF中的控件是SolidColorBrush类。这些可以通过在不同的构造函数中采用RGBA的各种方法来构造。

这是一项巨大的工作,因此,如果至少要跳转到App.xaml中的Style xaml示例,并注意您可以在setter中绑定到ViewModel中的变量,则可以使StaticResource返回Dynamic Color值。那就是绑定的美丽。您仍然可以通过{StaticResource}分配样式,但是样式给出的值由代码中的绑定变量确定。

我创建了三个类来处理选项和主题。一个OptionsMenu.xaml文件作为一个对话框让用户选择,一个OptionsMenuVM.cs设置绑定,并使用Options.cs容纳数据。它们分别是视图,视图模型和模型。

Options.cs具有

     public class Options
{
    //white theme color values
    public SolidColorBrush whiteThemeLightPanelColor = new SolidColorBrush(Color.FromRgb(255,255,255));
    public SolidColorBrush whiteThemeDarkPanelColor = new SolidColorBrush(Color.FromRgb(230,230,230));
    public SolidColorBrush whiteThemeTextBoxBackgroundColor = new SolidColorBrush(Color.FromRgb(255,255));
    public SolidColorBrush whiteThemeTextBoxForegroundColor = new SolidColorBrush(Color.FromRgb(0,0));

    //light theme color values
    public SolidColorBrush lightThemeLightPanelColor = new SolidColorBrush(Color.FromRgb(200,200,200));
    public SolidColorBrush lightThemeDarkPanelColor = new SolidColorBrush(Color.FromRgb(225,225,225));
    public SolidColorBrush lightThemeTextBoxBackgroundColor = new SolidColorBrush(Color.FromRgb(180,180,180));
    public SolidColorBrush lightThemeTextBoxForegroundColor = new SolidColorBrush(Color.FromRgb(0,0));

    //dark theme color values
    public SolidColorBrush darkThemeLightPanelColor = new SolidColorBrush(Color.FromRgb(100,100,100));
    public SolidColorBrush darkThemeDarkPanelColor = new SolidColorBrush(Color.FromRgb(70,70,70));
    public SolidColorBrush darkThemeTextBoxBackgroundColor = new SolidColorBrush(Color.FromRgb(50,50,50));
    public SolidColorBrush darkThemeTextBoxForegroundColor = new SolidColorBrush(Color.FromRgb(255,255));

    //blue theme color values
    public SolidColorBrush blueThemeLightPanelColor = new SolidColorBrush(Color.FromRgb(105,175,209));
    public SolidColorBrush blueThemeDarkPanelColor = new SolidColorBrush(Color.FromRgb(34,103,140));
    public SolidColorBrush blueThemeTextBoxBackgroundColor = new SolidColorBrush(Color.FromRgb(211,211,211));
    public SolidColorBrush blueThemeTextBoxForegroundColor = new SolidColorBrush(Color.FromRgb(0,0));

    //most recent custom color values
    public SolidColorBrush lastCustomLightPanelColor = new SolidColorBrush(Color.FromRgb(200,200));
    public SolidColorBrush lastCustomDarkPanelColor = new SolidColorBrush(Color.FromRgb(225,225));
    public SolidColorBrush lastCustomTextBoxBackgroundColor = new SolidColorBrush(Color.FromRgb(180,180));
    public SolidColorBrush lastCustomTextBoxForegroundColor = new SolidColorBrush(Color.FromRgb(0,0));

    //runtime color values
    public SolidColorBrush lightPanelColor;
    public SolidColorBrush darkPanelColor;
    public SolidColorBrush textBoxBackgroundColor;
    public SolidColorBrush textBoxForegroundColor;

    public string chosenTheme = "Light";
    }

视图模型具有

    private Options userOptions;
    public Options UserOptions
    {
        get => userOptions;
        set
        {
            userOptions = value;

            OnPropertyChanged("UserOptions");

            OnPropertyChanged("ChosenTheme");

            UpdateColors();
        }
    }
    public SolidColorBrush LightPanelColor
    {
        get => userOptions.lightPanelColor;
        set
        {
            userOptions.lightPanelColor = value;
            OnPropertyChanged("LightPanelColor");
        }
    }

    public SolidColorBrush DarkPanelColor
    {
        get => userOptions.darkPanelColor;
        set
        {
            userOptions.darkPanelColor = value;
            OnPropertyChanged("DarkPanelColor");
        }
    }

    public SolidColorBrush TextBoxBackgroundColor
    {
        get => userOptions.textBoxBackgroundColor;
        set
        {
            userOptions.textBoxBackgroundColor = value;
            OnPropertyChanged("TextBoxBackgroundColor");
        }
    }
   public ObservableCollection<string> ThemeOptions { get; } = new ObservableCollection<string>() { "White","Light","Blue","Dark","Custom" };

    
    public string ChosenTheme
    {
        get => userOptions.chosenTheme;
        set
        {
            userOptions.chosenTheme = value;
            OnPropertyChanged("ChosenTheme");
            OnPropertyChanged("EnableColorSelection");
            UpdateColors();
        }
    }

    public void UpdateColors()
    {
        if(userOptions.chosenTheme.Equals("White"))
        {
            LightPanelColor = userOptions.whiteThemeLightPanelColor;
            DarkPanelColor = userOptions.whiteThemeDarkPanelColor;
            TextBoxBackgroundColor = userOptions.whiteThemeTextBoxBackgroundColor;
            TextBoxForegroundColor = userOptions.whiteThemeTextBoxForegroundColor;
        }
        else if (userOptions.chosenTheme.Equals("Light"))
        {
            LightPanelColor = userOptions.lightThemeLightPanelColor;
            DarkPanelColor = userOptions.lightThemeDarkPanelColor;
            TextBoxBackgroundColor = userOptions.lightThemeTextBoxBackgroundColor;
            TextBoxForegroundColor = userOptions.lightThemeTextBoxForegroundColor;
        }
        else if (userOptions.chosenTheme.Equals("Dark"))
        {
            LightPanelColor = userOptions.darkThemeLightPanelColor;
            DarkPanelColor = userOptions.darkThemeDarkPanelColor;
            TextBoxBackgroundColor = userOptions.darkThemeTextBoxBackgroundColor;
            TextBoxForegroundColor = userOptions.darkThemeTextBoxForegroundColor;
        }
        else if (userOptions.chosenTheme.Equals("Blue"))
        {
            LightPanelColor = userOptions.blueThemeLightPanelColor;
            DarkPanelColor = userOptions.blueThemeDarkPanelColor;
            TextBoxBackgroundColor = userOptions.blueThemeTextBoxBackgroundColor;
            TextBoxForegroundColor = userOptions.blueThemeTextBoxForegroundColor;
        }
        else if(userOptions.chosenTheme.Equals("Custom"))
        {
            LightPanelColor = userOptions.lastCustomLightPanelColor;
            DarkPanelColor = userOptions.lastCustomDarkPanelColor;
            TextBoxBackgroundColor = userOptions.lastCustomTextBoxBackgroundColor;
            TextBoxForegroundColor = userOptions.lastCustomTextBoxForegroundColor;
        }
    }

Options.xaml具有

    <StackPanel Orientation="Horizontal">
                    <Label Content="Theme: " />
                    <ComboBox Width="150" ItemsSource="{Binding ThemeOptions}" SelectedItem="{Binding ChosenTheme}" />
                </StackPanel>
                <GroupBox Header="Custom Theme Color Selections" IsEnabled="{Binding EnableColorSelection}" Style="{StaticResource GroupBoxStyle}">
                    <StackPanel Orientation="Vertical">
                        <StackPanel Orientation="Horizontal">
                            <Label Content="Light Panel Background: " Style="{StaticResource OptionsLabel}"/>
                            <Button Foreground="{Binding Path=LightPanelColor,UpdateSourceTrigger=PropertyChanged}" ToolTip="Click to Change Color" Width="28" Height="18" Command="{Binding ChooseColorCmd}" CommandParameter="LightPanelColor">
                                <Rectangle Width="40" Height="15" Fill="{Binding Path=LightPanelColor,UpdateSourceTrigger=PropertyChanged}"/>
                            </Button>
                        </StackPanel>

                        <StackPanel Orientation="Horizontal">
                            <Label Content="Dark Panel Background: " Style="{StaticResource OptionsLabel}"/>
                            <Button Foreground="{Binding Path=DarkPanelColor,UpdateSourceTrigger=PropertyChanged}" ToolTip="Click to Change Color" Width="28" Height="18" Command="{Binding ChooseColorCmd}" CommandParameter="DarkPanelColor">
                                <Rectangle Width="40" Height="15" Fill="{Binding Path=DarkPanelColor,UpdateSourceTrigger=PropertyChanged}"/>
                            </Button>
                        </StackPanel>

                        <StackPanel Orientation="Horizontal">
                            <Label Content="Text Box Background: " Style="{StaticResource OptionsLabel}"/>
                            <Button Foreground="{Binding Path=TextBoxBackgroundColor,UpdateSourceTrigger=PropertyChanged}" ToolTip="Click to Change Color" Width="28" Height="18" Command="{Binding ChooseColorCmd}" CommandParameter="TextBoxBackgroundColor">
                                <Rectangle Width="40" Height="15" Fill="{Binding Path=TextBoxBackgroundColor,UpdateSourceTrigger=PropertyChanged}"/>
                            </Button>
                        </StackPanel>

                        <StackPanel Orientation="Horizontal">
                            <Label Content="Text Box Foreground: " Style="{StaticResource OptionsLabel}"/>
                            <Button Foreground="{Binding Path=TextBoxForegroundColor,UpdateSourceTrigger=PropertyChanged}" ToolTip="Click to Change Color" Width="28" Height="18" Command="{Binding ChooseColorCmd}" CommandParameter="TextBoxForegroundColor">
                                <Rectangle Width="40" Height="15" Fill="{Binding Path=TextBoxForegroundColor,UpdateSourceTrigger=PropertyChanged}"/>
                            </Button>
                        </StackPanel>
                    </StackPanel>
                </GroupBox>

这为您提供了一个选择主题的组合框,如果他们选择“自定义”,我将启用几个按钮以使他们从颜色选择器中进行选择。您可以从颜色选择器中获取RGB并以这种方式设置颜色。如果他们选择了正常主题,UpdateColors()将查看selectedTheme并将其颜色设置为option.cs中的默认颜色。

这是我的viewModel中的函数,按钮调用这些函数以选择自定义颜色。 Btw标准主题更容易添加自定义颜色,使其更加复杂。

    private void ChooseColor(object cp)
    {
        string caller = cp.ToString();
        System.Windows.Forms.ColorDialog cd = new System.Windows.Forms.ColorDialog();
        Color startColor = Color.FromRgb(0,0);
        if (caller.Equals("LightPanelColor"))
        {
            startColor = LightPanelColor.Color;
        }
        else if (caller.Equals("DarkPanelColor"))
        {
            startColor = DarkPanelColor.Color;
        }
        else if (caller.Equals("TextBoxBackgroundColor"))
        {
            startColor = TextBoxBackgroundColor.Color;
        }
        else if (caller.Equals("TextBoxForegroundColor"))
        {
            startColor = TextBoxForegroundColor.Color;
        }
        else
        {
            return;
        }

        cd.Color = System.Drawing.Color.FromArgb(startColor.A,startColor.R,startColor.G,startColor.B);
        if (cd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            if(caller.Equals("LightPanelColor"))
            {
                LightPanelColor.Color = Color.FromArgb(cd.Color.A,cd.Color.R,cd.Color.G,cd.Color.B);
                OnPropertyChanged("LightPanelColor");
            }
            else if(caller.Equals("DarkPanelColor"))
            {
                DarkPanelColor.Color = Color.FromArgb(cd.Color.A,cd.Color.B);
                OnPropertyChanged("DarkPanelColor");
            }
            else if(caller.Equals("TextBoxBackgroundColor"))
            {
                TextBoxBackgroundColor.Color = Color.FromArgb(cd.Color.A,cd.Color.B);
                OnPropertyChanged("TextBoxBackgroundColor");
            }
            else if(caller.Equals("TextBoxForegroundColor"))
            {
                TextBoxForegroundColor.Color = Color.FromArgb(cd.Color.A,cd.Color.B);
                OnPropertyChanged("TextBoxForegroundColor");
            }
            else
            {
                return;
            }
        }
    }

为了使这些颜色在整个应用程序中可用,我将其放入App.xaml

    <vm:OptionsMenuVM x:Key="optionsMenuVM"/>

OptionsMenu.xaml通过它获取数据上下文

    <Window Height="360" Width="564.225" DataContext="{StaticResource optionsMenuVM}" Background="{Binding DarkPanelColor}" Name="OptionsWindow">

现在,您可以在应用程序中的任何位置设置控件的背景

    Background="{Binding Path=DarkPanelColor,Source={StaticResource optionsMenuVM}}">

然后,您可以选择所需的任何颜色主题。我只有四种颜色,所以您必须决定多少种颜色。最好是一种或一组控件的颜色。因此,每个TextBox都使用TextBoxColor或您所说的任何东西。

这是我放入TextBox样式的App.xaml中的示例

    <Style x:Key="InputTextbox" TargetType="TextBox">
        <Style.Setters>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="Width" Value="90"/>
            <Setter Property="Margin" Value="0,2,2"/>
            <Setter Property="Background" Value="{Binding Path=TextBoxBackgroundColor,Source={StaticResource optionsMenuVM}}"/>
            <Setter Property="Foreground" Value="{Binding Path=TextBoxForegroundColor,Source={StaticResource optionsMenuVM}}"/>
            <EventSetter Event="Loaded" Handler="TextBox_Loaded"/>
        </Style.Setters>
    </Style>

因此,对于每个这样的TextBox,您都可以编写

    <TextBox  Style="{StaticResource InputTextbox}"/>

您可以对其他控件执行相同的操作。只要在设置颜色时调用PropertyChanged事件,整个应用程序就应该立即更新。背景颜色绑定将全部从您的视图模型中提取。

我不得不省去一些零件,因为我有一个很大的选项菜单和许多东西正在进行,但是我希望我能做到这一点的总体思路能够得到理解。完成类似操作后,您要做的一件事就是保存选项数据。

由于该视图模型仅包含Options.cs的实例,因此用已加载的实例替换该类实例将使您回到上次停止的位置。我认为将这些数据保存到AppData文件夹中是个好主意,

    Environment.SpecialFolder.ApplicationData

然后将程序的新文件夹追加到该路径(Path.Combine是您的朋友),并在其中保存文件供您选择。我尝试了序列化,但是纯色笔刷没有序列化,所以我使用BinaryWriter保存RGB值并读回。这是我的保存和加载功能

    private void SaveUserOptionsFile()
    {
        
        try
        {
            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(optionsSavePath,FileMode.Create)))
            {
                Color lastCustomLightPanelColor = userOptions.lastCustomLightPanelColor.Color;
                Color lastCustomDarkPanelColor = userOptions.lastCustomDarkPanelColor.Color;
                Color lastCustomTextBoxBackgroundColor = userOptions.lastCustomTextBoxBackgroundColor.Color;
                Color lastCustomTextBoxForegroundColor = userOptions.lastCustomTextBoxForegroundColor.Color;

                binaryWriter.Write(lastCustomLightPanelColor.R);
                binaryWriter.Write(lastCustomLightPanelColor.G);
                binaryWriter.Write(lastCustomLightPanelColor.B);

                binaryWriter.Write(lastCustomDarkPanelColor.R);
                binaryWriter.Write(lastCustomDarkPanelColor.G);
                binaryWriter.Write(lastCustomDarkPanelColor.B);

                binaryWriter.Write(lastCustomTextBoxBackgroundColor.R);
                binaryWriter.Write(lastCustomTextBoxBackgroundColor.G);
                binaryWriter.Write(lastCustomTextBoxBackgroundColor.B);

                binaryWriter.Write(lastCustomTextBoxForegroundColor.R);
                binaryWriter.Write(lastCustomTextBoxForegroundColor.G);
                binaryWriter.Write(lastCustomTextBoxForegroundColor.B);

                binaryWriter.Write(userOptions.chosenTheme);
            }
        }
        catch(IOException e)
        {
            if(File.Exists(optionsSavePath))
            {
                File.Delete(optionsSavePath);
            }
        }
    }

    public void LoadUserOptionsFile()
    {
        if (File.Exists(optionsSavePath))
        { 
            try
            {
                using (BinaryReader binaryReader = new BinaryReader(File.Open(optionsSavePath,FileMode.Open)))
                {
                    UserOptions.lastCustomLightPanelColor.Color = Color.FromRgb(binaryReader.ReadByte(),binaryReader.ReadByte(),binaryReader.ReadByte());
                    UserOptions.lastCustomDarkPanelColor.Color = Color.FromRgb(binaryReader.ReadByte(),binaryReader.ReadByte());
                    UserOptions.lastCustomTextBoxBackgroundColor.Color = Color.FromRgb(binaryReader.ReadByte(),binaryReader.ReadByte());
                    UserOptions.lastCustomTextBoxForegroundColor.Color = Color.FromRgb(binaryReader.ReadByte(),binaryReader.ReadByte());

                    ChosenTheme = binaryReader.ReadString();

                    originalUserOptions = new Options(UserOptions);
                }
            }
            catch (IOException e)
            {
                UserOptions = new Options();
                originalUserOptions = new Options();
                if (File.Exists(optionsSavePath))
                {
                    File.Delete(optionsSavePath);
                }
            }
        }
    }

我通过在主窗口中捕获close事件并调用save函数来保存此文件。程序打开时调用加载,如果没有加载,它将使用默认起始值​​。

我在那里有变量originalUserOptions,让用户取消。它被构造为用户打开菜单时存在的选项的副本。实际的选项实例通过其输入进行编辑,他们看到颜色发生了变化。如果它们单击确定,则选项保留,如果它们单击取消,则将选项设置回原始实例,并返回到它们开始时的状态。在设置选项的过程中,我必须为所有相关数据调用PropertyChanged才能获取它以更新视图。

我知道这里有很多,但这是应用程序范围内的数据争用练习,它分布在许多文件中。如果您尝试其中的任何一种,请不要复制粘贴所有我正在进行的工作,并且必须尝试提取相关的位。以它为例,使用一些想法自己构建主题系统。抱歉,如果实际使用过多。祝你好运。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?