WPF 实现更换主题色

 WPF 实现更换主题色

WPF 使用 WPFDevelopers.Minimal 如何更换主题色

作者:WPFDevelopersOrg

原文链接:    https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal

  • 框架使用大于等于.NET40

  • Visual Studio 2022;

  • 项目使用 MIT 开源许可协议;

  • Nuget Install-Package WPFDevelopers.Minimal 3.2.6-preview

  • 定义一个公共颜色资源文件,所有控件都动态资源引用DynamicResource颜色的Key,然后这样修改一个资源文件的Key  SolidColorBrushColor颜色值就可以实现更换主题色;

  • 此篇主要是定义了多个公共颜色资源文件如下,选择不同的资源文件进行更换App.xaml的资源字典实现更换主题色;

    • Application.Current.Resources.MergedDictionaries.Remove(Blue) 字典寻找Blue,然后移除Application.Current.Resources.MergedDictionaries.Add(Green); 就完成更换主题色;

    • 创建 Light.Blue

    • 创建 Light.Green

    • 创建 Light.Orange

    • 创建 Light.Pink

    • 创建 Light.Purple

    • 创建 Light.Purple7bd5469f7ef23542f8a877422422679a.png

  • 使用 WPFDevelopers.Minimal 如何更换主题色Nuget包大于等于3.2.3如下;

  • 新增主题色如下;

    • 新建资源文件Light.Carmine.xaml如下,自己更换颜色值即可;

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" po:Freeze="True">
   
    <!--Default颜色-->
    <Color x:Key="DefaultBorderBrushColor" po:Freeze="True">#CD7474</Color>
    <SolidColorBrush x:Key="DefaultBorderBrushSolidColorBrush" Color="{StaticResource DefaultBorderBrushColor}" po:Freeze="True"></SolidColorBrush>
    <Color x:Key="DefaultBackgroundColor" po:Freeze="True">#CFA0A0</Color>
    <SolidColorBrush x:Key="DefaultBackgroundSolidColorBrush" Color="{StaticResource DefaultBackgroundColor}" po:Freeze="True"></SolidColorBrush>
    <Color x:Key="DefaultBackgroundPressedColor" po:Freeze="True">#B70404</Color>
    <SolidColorBrush x:Key="DefaultBackgroundPressedSolidColorBrush" Color="{StaticResource DefaultBackgroundPressedColor}" po:Freeze="True"></SolidColorBrush>
    <!--Primary颜色-->
    <Color x:Key="PrimaryNormalColor" po:Freeze="True">#B31B1B</Color>
    <SolidColorBrush x:Key="PrimaryNormalSolidColorBrush" Color="{StaticResource PrimaryNormalColor}" po:Freeze="True"></SolidColorBrush>
    <SolidColorBrush x:Key="WindowBorderBrushSolidColorBrush" Color="{StaticResource PrimaryNormalColor}" po:Freeze="True"></SolidColorBrush>

    <Color x:Key="PrimaryMouseOverColor" po:Freeze="True">#BB5F5F</Color>
    <SolidColorBrush x:Key="PrimaryMouseOverSolidColorBrush" Color="{StaticResource PrimaryMouseOverColor}" po:Freeze="True"></SolidColorBrush>

    <Color x:Key="PrimaryPressedColor" po:Freeze="True">#B70404</Color>
    <SolidColorBrush x:Key="PrimaryPressedSolidColorBrush" Color="{StaticResource PrimaryPressedColor}" po:Freeze="True"></SolidColorBrush>

</ResourceDictionary>
  • 1)把上面新增的资源添加App.xaml中即可;

<Application x:Class="WPFDevelopers.Minimal.Sample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"
             StartupUri="ExampleViews\MainView.xaml" ShutdownMode="OnMainWindowClose">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/WPFDevelopers.Minimal.Sample.Net40;component/Light.Carmine.xaml"/>
                 <!--需要注意ws:Resources 必须再配色主题后,Theme="Dark" 为黑色皮肤 -->
                <ws:Resources Theme="Light"/>
                <ResourceDictionary Source="pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Theme.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
  • 2)或者把资源添加到ThemesCollection集合中;

if (ThemesCollection != null)
    ThemesCollection.Add(new ThemeModel
    {
     Color = "#B31B1B",
     ResourcePath =
     "pack://application:,,,/WPFDevelopers.Minimal.Sample.Net40;component/Light.Carmine.xaml"
   });
  • 重启应用效果如下;

1f717d599146a557f9586dcdd5aab94d.png bf5310209d44c6fe91bba761385476ff.png

1)ThemeControl.cs代码如下;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using WPFDevelopers.Minimal.Helpers;
using WPFDevelopers.Minimal.Models;

namespace WPFDevelopers.Minimal.Controls
{
    public class ThemeControl : Control
    {
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<ThemeModel>), typeof(ThemeControl),
                new PropertyMetadata(null));

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

        public ObservableCollection<ThemeModel> ItemsSource
        {
            get => (ObservableCollection<ThemeModel>)GetValue(ItemsSourceProperty);
            set => SetValue(ItemsSourceProperty, value);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            ItemsSource = new ObservableCollection<ThemeModel>();
            ItemsSource.Add(new ThemeModel
            {
                Color = "#409EFF",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Blue.xaml"
            });
            ItemsSource.Add(new ThemeModel
            {
                Color = "#FF033E",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Red.xaml"
            });
            ItemsSource.Add(new ThemeModel
            {
                Color = "#A21BFC",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Purple.xaml"
            });
            ItemsSource.Add(new ThemeModel
            {
                Color = "#FE9426",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Orange.xaml"
            });
            ItemsSource.Add(new ThemeModel
            {
                Color = "#00B050",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Green.xaml"
            });
            ItemsSource.Add(new ThemeModel
            {
                Color = "#FF007F",
                ResourcePath = "pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Pink.xaml"
            });
            if (ThemeCache.ThemesDictCache.Count > 0)
                foreach (var item in ThemeCache.ThemesDictCache)
                    if (ItemsSource.Any(x => x.Color != item.Key))
                        ItemsSource.Add(item.Value);
            SelectChecked();
            ItemsSource.CollectionChanged += ItemsSource_CollectionChanged;
            foreach (var theme in ItemsSource)
                theme.PropertyChanged += Theme_PropertyChanged;
        }

        private void SelectChecked()
        {
            var existsTheme = ItemsSource.FirstOrDefault(y =>
                Application.Current.Resources.MergedDictionaries.ToList().Exists(j =>
                    j.Source != null && y.ResourcePath.Contains(j.Source.AbsoluteUri)));

            if (existsTheme != null)
                existsTheme.IsChecked = true;
        }

        private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (ThemeModel item in e.NewItems)
                    {
                        if (!ThemeCache.ThemesDictCache.ContainsKey(item.Color))
                            ThemeCache.ThemesDictCache.Add(item.Color, item);
                        item.PropertyChanged += Theme_PropertyChanged;
                        SelectChecked();
                        if (!item.IsChecked) return;
                        ReviseTheme(item);
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (ThemeModel item in e.NewItems)
                        if (ThemeCache.ThemesDictCache.ContainsKey(item.Color))
                            ThemeCache.ThemesDictCache.Remove(item.Color);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Move:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
            }
        }


        private void Theme_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ThemeModel.IsChecked))
            {
                var theme = sender as ThemeModel;
                if (!theme.IsChecked) return;
                ReviseTheme(theme);
            }
        }

        private void ReviseTheme(ThemeModel theme)
        {
            if (theme == null) return;

            var old = ItemsSource.FirstOrDefault(x => x.IsChecked && x.Color != theme.Color);
            if (old != null)
            {
                ItemsSource.Where(y => !y.Color.Equals(theme.Color) && y.IsChecked).ToList()
                    .ForEach(h => h.IsChecked = false);
                var existingResourceDictionary =
                    Application.Current.Resources.MergedDictionaries.FirstOrDefault(x =>
                        x.Source != null && x.Source.Equals(old.ResourcePath));
                if (existingResourceDictionary != null)
                    Application.Current.Resources.MergedDictionaries.Remove(existingResourceDictionary);
                var newResource =
                    Application.Current.Resources.MergedDictionaries.FirstOrDefault(x =>
                        x.Source != null && x.Source.Equals(theme.ResourcePath));
                if (newResource != null) return;
                var newResourceDictionary = new ResourceDictionary { Source = new Uri(theme.ResourcePath) };
                Application.Current.Resources.MergedDictionaries.Insert(0, newResourceDictionary);
                ControlHelper.ThemeRefresh();
            }
        }
    }

    public class ThemeCache
    {
        public static Dictionary<string, ThemeModel> ThemesDictCache = new Dictionary<string, ThemeModel>();
    }
}

2)Styles.ThemeControl.xaml代码如下;

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:wpfsc="clr-namespace:WPFDevelopers.Minimal.Controls"
                    xmlns:model="clr-namespace:WPFDevelopers.Minimal.Models">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="../Themes/Basic/ControlBasic.xaml"/>
        <ResourceDictionary Source="../Themes/Basic/Animations.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    
    <Style TargetType="{x:Type wpfsc:ThemeControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type wpfsc:ThemeControl}">
                    <ItemsControl ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource AncestorType=wpfsc:ThemeControl}}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <RadioButton Height="40" Width="40" Margin="4,0"
                                         Cursor="Hand" 
                                         IsChecked="{Binding IsChecked}">
                                    <RadioButton.Style>
                                        <Style TargetType="{x:Type RadioButton}">
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate TargetType="{x:Type RadioButton}">
                                                        <Border x:Name="PART_Border"
                                                            Padding="2" 
                                                            BorderThickness="0"
                                                            BorderBrush="{Binding Color}">
                                                            <Grid Background="{x:Null}">
                                                                <Rectangle x:Name="PART_Rectangle" Fill="{Binding Color}"/>
                                                                <Path Data="{StaticResource PathCheckMark}"
                                                                      Stretch="Fill" Fill="{DynamicResource BackgroundSolidColorBrush}"
                                                                      VerticalAlignment="Bottom"
                                                                      HorizontalAlignment="Right"
                                                                      Height="10" Width="12"
                                                                      Margin="0,0,4,4"
                                                                      Visibility="{Binding IsChecked,Converter={StaticResource bool2VisibilityConverter}}"/>
                                                            </Grid>
                                                        </Border>
                                                        <ControlTemplate.Triggers>
                                                            <Trigger Property="IsMouseOver" Value="True">
                                                                <Setter Property="Opacity" Value=".8" TargetName="PART_Rectangle"/>
                                                                <Setter Property="BorderThickness" Value="1" TargetName="PART_Border"/>
                                                            </Trigger>
                                                            <Trigger Property="IsChecked" Value="True">
                                                                <Setter Property="BorderThickness" Value="1" TargetName="PART_Border"/>
                                                            </Trigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </RadioButton.Style>
                                </RadioButton>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <WrapPanel/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3)ThemeControlExample.xaml代码如下;

<!--命名空间-->
  xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"
  
 <TextBlock Text="Theme" FontSize="20" Margin="0,20,0,0"/>
 <ws:ThemeControl Margin="0,10" ItemsSource="{Binding ThemesCollection,RelativeSource={RelativeSource AncestorType=local:MainView},Mode=OneWayToSource}"/>

ThemeControl|Github[1]
ThemeControl|码云[2]
Styles.ThemeControl.xaml|Github[3]
Styles.ThemeControl.xaml|码云[4]

参考资料

[1]

ThemeControl|Github: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Controls/ThemeControl.cs

[2]

ThemeControl|码云: https://gitee.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Controls/ThemeControl.cs

[3]

Styles.ThemeControl.xaml|Github: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Styles/Styles.ThemeControl.xaml

[4]

Styles.ThemeControl.xaml|码云: https://gitee.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Styles/Styles.ThemeControl.xaml

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF(Windows Presentation Foundation)是一种用于创建 Windows 应用程序的框架,它提供了丰富的图形和用户界面功能。实现像 XMind 这样的思维导图工具的过程如下: 1. 数据结构设计:首先需要设计思维导图的数据结构,包括节点(节点可能有不同的类型,如主题、子主题、注释等)、连接线等。可以使用树状结构或图结构来表示思维导图的组织关系。 2. 界面设计:使用 WPF 的图形和用户界面功能来设计思维导图的界面。可以使用画布(Canvas)来承载节点和连接线,通过鼠标事件来实现节点拖拽、连线等功能。可以为节点和连接线定义样式和模板,以美化界面和提供更多交互效果。 3. 数据绑定:将思维导图的数据模型与界面进行绑定,使得界面能够动态展示数据的变化。可以使用 WPF 的数据绑定机制,将节点的属性绑定到界面控件上,当属性值发生变化时,界面会自动更新。 4. 布局和自动排版:思维导图中的节点可能会很多,因此需要实现自动排版来保证节点的布局整齐美观。可以使用 WPF 的布局控件如网格(Grid)、堆栈面板(StackPanel)等进行节点的布局,并根据节点之间的关系自动调整节点的位置和大小。 5. 导出和导入:实现将思维导图保存为文件或导入文件的功能,可以使用 WPF 的文件操作功能来实现。可以将思维导图保存为 XML、JSON 或其他格式,并提供打开、保存功能供用户使用。 通过以上步骤,就可以使用 WPF 实现类似 XMind 的思维导图工具。当然,具体的实现过程和功能细节还需要根据实际需求进行具体设计和开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值