HandyControl中Button图标展示多色路径

多色路径需求

HandyControl 中支持图标和图标+文本显示,图标主要是矢量路径也就是对应着svg的数据格式。而对于多图形GeometryGroup 虽然能够多路径组合,但是仅支持单色,并不支持多色,日常使用如下:

原始svg内容

svg 复制代码
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_103_3747"><rect x="0" y="0" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_103_3747)"><path d="M10.832551042773437,1.59998178671875L2.8058843427734375,9.84051518671875C2.7448177427734377,9.90398218671875,2.7320177427734373,9.91651538671875,2.7082843427734375,9.97331528671875L2.6714843427734376,10.06851478671875L2.616551142773438,10.23118018671875L2.4133510427734377,10.86424728671875L2.0098843527734376,12.14691338671875L1.3573510647734375,14.24211338671875C1.2330844397734375,14.64291338671875,1.5976177427734375,15.01811338671875,1.9952178027734375,14.91758038671875L2.6701511427734372,14.69784638671875L5.239218042773437,13.85011338671875L5.939217542773438,13.61357938671875L6.079217442773437,13.56424738671875C6.099751042773438,13.55651238671875,6.116550942773437,13.55011238671875,6.131484042773438,13.54398038671875L6.192017542773438,13.51891438671875C6.248017742773437,13.49464738671875,6.260550942773437,13.48158038671875,6.321084042773437,13.42184638671875L14.353082342773437,5.17464708671875C14.756508342773438,4.760472786718751,14.756508342773438,4.10028838671875,14.353082342773437,3.68611358671875L12.341351342773438,1.61998176671875C11.930258342773438,1.19789707671875,11.254809342773438,1.18906152671875,10.832818042773438,1.60024842671875L10.832551042773437,1.59998178671875ZM3.6506848427734373,10.50264838671875L11.576818342773437,2.3642483867187503L13.589085342773437,4.430381786718749L5.645885442773437,12.58611438671875L5.209618542773438,12.73544738671875L3.7389519427734377,13.22344738671875L2.6826853427734374,13.57118138671875L3.3749520427734376,11.36104638671875L3.6157517427734374,10.60771468671875L3.6504187427734376,10.50238128671875L3.6506848427734373,10.50264838671875Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M9.0365343134375,3.750135658203125C9.2321443534375,3.554630756203125,9.5447464034375,3.541084766203125,9.7565345734375,3.718935728203125L9.7906675334375,3.750135658203125L12.190666923437501,6.150135558203125C12.3937263234375,6.3518721582031255,12.4013290234375,6.677928958203125,12.2078933234375,6.888910558203126C12.0144577234375,7.099892358203125,11.688966723437499,7.120558758203125,11.470400823437501,6.935735658203125L11.4365339234375,6.904535558203126L9.0365343134375,4.5045356782031245C8.8281536104375,4.296238418203125,8.8281536104375,3.958432908203125,9.0365343134375,3.750135658203125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M13.599989443554687,13.333343505859375L13.599989443554687,13.866677345859374C13.599990343554687,14.161229195859375,13.361208443554688,14.400011205859375,13.066656543554688,14.400010105859375L8.266655443554688,14.400010105859375C7.9721040735546875,14.400011205859375,7.7333226203918475,14.161229195859375,7.7333221435546875,13.866677345859374L7.7333221435546875,13.333343505859375L13.599989443554687,13.333343505859375Z" fill="#00B386" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg>

Geometry转换

实际转换为GeometryGroup后如下:

xml 复制代码
<GeometryGroup x:Key="InputGeometry">   <Geometry>M10.832551042773437,1.59998178671875L2.8058843427734375,9.84051518671875C2.7448177427734377,9.90398218671875,2.7320177427734373,9.91651538671875,2.7082843427734375,9.97331528671875L2.6714843427734376,10.06851478671875L2.616551142773438,10.23118018671875L2.4133510427734377,10.86424728671875L2.0098843527734376,12.14691338671875L1.3573510647734375,14.24211338671875C1.2330844397734375,14.64291338671875,1.5976177427734375,15.01811338671875,1.9952178027734375,14.91758038671875L2.6701511427734372,14.69784638671875L5.239218042773437,13.85011338671875L5.939217542773438,13.61357938671875L6.079217442773437,13.56424738671875C6.099751042773438,13.55651238671875,6.116550942773437,13.55011238671875,6.131484042773438,13.54398038671875L6.192017542773438,13.51891438671875C6.248017742773437,13.49464738671875,6.260550942773437,13.48158038671875,6.321084042773437,13.42184638671875L14.353082342773437,5.17464708671875C14.756508342773438,4.760472786718751,14.756508342773438,4.10028838671875,14.353082342773437,3.68611358671875L12.341351342773438,1.61998176671875C11.930258342773438,1.19789707671875,11.254809342773438,1.18906152671875,10.832818042773438,1.60024842671875L10.832551042773437,1.59998178671875ZM3.6506848427734373,10.50264838671875L11.576818342773437,2.3642483867187503L13.589085342773437,4.430381786718749L5.645885442773437,12.58611438671875L5.209618542773438,12.73544738671875L3.7389519427734377,13.22344738671875L2.6826853427734374,13.57118138671875L3.3749520427734376,11.36104638671875L3.6157517427734374,10.60771468671875L3.6504187427734376,10.50238128671875L3.6506848427734373,10.50264838671875Z</Geometry><Geometry>M9.0365343134375,3.750135658203125C9.2321443534375,3.554630756203125,9.5447464034375,3.541084766203125,9.7565345734375,3.718935728203125L9.7906675334375,3.750135658203125L12.190666923437501,6.150135558203125C12.3937263234375,6.3518721582031255,12.4013290234375,6.677928958203125,12.2078933234375,6.888910558203126C12.0144577234375,7.099892358203125,11.688966723437499,7.120558758203125,11.470400823437501,6.935735658203125L11.4365339234375,6.904535558203126L9.0365343134375,4.5045356782031245C8.8281536104375,4.296238418203125,8.8281536104375,3.958432908203125,9.0365343134375,3.750135658203125Z</Geometry>
<Geometry>M13.599989443554687,13.333343505859375L13.599989443554687,13.866677345859374C13.599990343554687,14.161229195859375,13.361208443554688,14.400011205859375,13.066656543554688,14.400010105859375L8.266655443554688,14.400010105859375C7.9721040735546875,14.400011205859375,7.7333226203918475,14.161229195859375,7.7333221435546875,13.866677345859374L7.7333221435546875,13.333343505859375L13.599989443554687,13.333343505859375Z</Geometry>
</GeometryGroup>

Xaml 页面使用

Button 需要声明全局引入引入HandyControl 库并引入命名空间:

xml 复制代码
<hc:Window x:Class="blog_hc_button.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:blog_hc_button"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <UniformGrid>
        <Button 
        Width="Auto"
        Padding="12,6"
        Height="36"
        FontSize="14"
        Content="测试样例"
        hc:IconElement.Geometry="{StaticResource InputGeometry}" 
        hc:IconElement.Width="24" hc:IconElement.Height="20"></Button>
    </UniformGrid>
</hc:Window>

运行效果如下,字体色即为文本色:

源码细节分析

查看Button样式

通过查看HandyControlButtonBaseStyle 样式代码,去除无关部分,不能看出按钮的字体颜色即为路径的填充色<Path ... Fill="{TemplateBinding Foreground}"/>

xml 复制代码
<Style x:Key="ButtonBaseStyle" BasedOn="{StaticResource ButtonBaseBaseStyle}" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <hc:SimplePanel>
                    <Border Background="{TemplateBinding Background}" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"/>
                    <Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}">
                        <!--路径图标-->
                            <Path x:Name="PathMain" Width="{TemplateBinding hc:IconElement.Width}" Height="{TemplateBinding hc:IconElement.Height}" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" Stretch="Uniform" Data="{TemplateBinding hc:IconElement.Geometry}"/>
                        <!--文本内容-->
                            <ContentPresenter x:Name="ContentPresenterMain" RecognizesAccessKey="True" VerticalAlignment="Center" Margin="6,0,0,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </StackPanel>
                    </Border>
                </hc:SimplePanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

查看DrawingBrush

如果需要多色,就需要将 ButtonForegroundIcon 的填充色进行分离,进而实现两者的独立配置,虽然 GeometryGroup 无法实现多色,但是Fill 本身可以支持DrawingBrush,可以通过单独设置DrawingBrush实现图形填充色,使用了DrawingGroup 以及 GeometryDrawing,如果不熟悉可以查阅微软文档。

xml 复制代码
<DrawingBrush x:Key="Input2GeometryBrush">
    <DrawingBrush.Drawing>
        <DrawingGroup>
            <GeometryDrawing Brush="{DynamicResource PrimaryTextBrush}" Pen="{x:Null}" Geometry="M10.832551042773437,1.59998178671875L2.8058843427734375,9.84051518671875C2.7448177427734377,9.90398218671875,2.7320177427734373,9.91651538671875,2.7082843427734375,9.97331528671875L2.6714843427734376,10.06851478671875L2.616551142773438,10.23118018671875L2.4133510427734377,10.86424728671875L2.0098843527734376,12.14691338671875L1.3573510647734375,14.24211338671875C1.2330844397734375,14.64291338671875,1.5976177427734375,15.01811338671875,1.9952178027734375,14.91758038671875L2.6701511427734372,14.69784638671875L5.239218042773437,13.85011338671875L5.939217542773438,13.61357938671875L6.079217442773437,13.56424738671875C6.099751042773438,13.55651238671875,6.116550942773437,13.55011238671875,6.131484042773438,13.54398038671875L6.192017542773438,13.51891438671875C6.248017742773437,13.49464738671875,6.260550942773437,13.48158038671875,6.321084042773437,13.42184638671875L14.353082342773437,5.17464708671875C14.756508342773438,4.760472786718751,14.756508342773438,4.10028838671875,14.353082342773437,3.68611358671875L12.341351342773438,1.61998176671875C11.930258342773438,1.19789707671875,11.254809342773438,1.18906152671875,10.832818042773438,1.60024842671875L10.832551042773437,1.59998178671875ZM3.6506848427734373,10.50264838671875L11.576818342773437,2.3642483867187503L13.589085342773437,4.430381786718749L5.645885442773437,12.58611438671875L5.209618542773438,12.73544738671875L3.7389519427734377,13.22344738671875L2.6826853427734374,13.57118138671875L3.3749520427734376,11.36104638671875L3.6157517427734374,10.60771468671875L3.6504187427734376,10.50238128671875L3.6506848427734373,10.50264838671875Z"/>
            <GeometryDrawing Brush="{DynamicResource PrimaryTextBrush}" Pen="{x:Null}" Geometry="M9.0365343134375,3.750135658203125C9.2321443534375,3.554630756203125,9.5447464034375,3.541084766203125,9.7565345734375,3.718935728203125L9.7906675334375,3.750135658203125L12.190666923437501,6.150135558203125C12.3937263234375,6.3518721582031255,12.4013290234375,6.677928958203125,12.2078933234375,6.888910558203126C12.0144577234375,7.099892358203125,11.688966723437499,7.120558758203125,11.470400823437501,6.935735658203125L11.4365339234375,6.904535558203126L9.0365343134375,4.5045356782031245C8.8281536104375,4.296238418203125,8.8281536104375,3.958432908203125,9.0365343134375,3.750135658203125Z"/>
            <GeometryDrawing Brush="{DynamicResource PrimaryBrush}" Pen="{x:Null}" Geometry="M13.599989443554687,13.333343505859375L13.599989443554687,13.866677345859374C13.599990343554687,14.161229195859375,13.361208443554688,14.400011205859375,13.066656543554688,14.400010105859375L8.266655443554688,14.400010105859375C7.9721040735546875,14.400011205859375,7.7333226203918475,14.161229195859375,7.7333221435546875,13.866677345859374L7.7333221435546875,13.333343505859375L13.599989443554687,13.333343505859375Z"/>
        </DrawingGroup>
    </DrawingBrush.Drawing>
</DrawingBrush>

逻辑代码调整

创建附加属性

那又该如何在Button控件中使用?实际可以参考HandyControl 原有自带的附加属性IconElement 相关属性。本地构建一个IconElement 类增加一个Brush 类型的附加属性IconElement.Foreground

cs 复制代码
using System.Windows;
using System.Windows.Media;
namespace blog_hc_button
{
    public class IconElement
    {
        public static Brush GetForeground(DependencyObject obj)
        {
            return (Brush)obj.GetValue(ForegroundProperty);
        }
        public static void SetForeground(DependencyObject obj, Brush value)
        {
            obj.SetValue(ForegroundProperty, value);
        }
        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.RegisterAttached("Foreground", typeof(Brush), typeof(IconElement), new PropertyMetadata(Brushes.Transparent));

    }
}

需要重写原有ButtonBaseStyle 样式,添加IconElement.Foreground 这个附加属性,首先添加本地命名空间声明。

xml 复制代码
xmlns:local="clr-namespace:blog_hc_button"

重写Button样式

进行Button样式覆写,进而能够在控件使用时传入附加属性决定图标的颜色。

xml 复制代码
<hc:Window x:Class="blog_hc_button.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:blog_hc_button"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <hc:Window.Resources>
<Style x:Key="ButtonBaseStyleX" BasedOn="{StaticResource ButtonBaseBaseStyle}" TargetType="Button">
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Background" Value="{DynamicResource RegionBrush}"/>
    <Setter Property="hc:BorderElement.CornerRadius" Value="{StaticResource DefaultCornerRadius}"/>
    <!--给local:IconElement.Foreground设置一个默认颜色-->
    <Setter Property="local:IconElement.Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <hc:SimplePanel>
                    <Border Background="{TemplateBinding Background}" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"/>
                    <Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}">
		                    <!--路径图标,设置local:IconElement.Foreground为Path的填充色-->
                            <Path x:Name="PathMain" Width="{TemplateBinding hc:IconElement.Width}" Height="{TemplateBinding hc:IconElement.Height}" Fill="{TemplateBinding local:IconElement.Foreground}" SnapsToDevicePixels="True" Stretch="Uniform" Data="{TemplateBinding hc:IconElement.Geometry}"/>
                            <ContentPresenter x:Name="ContentPresenterMain" RecognizesAccessKey="True" VerticalAlignment="Center" Margin="6,0,0,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </StackPanel>
                    </Border>
                </hc:SimplePanel>
                <ControlTemplate.Triggers>
                    <Trigger Property="Content" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed" TargetName="ContentPresenterMain"/>
                    </Trigger>
                    <Trigger Property="hc:IconElement.Geometry" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed" TargetName="PathMain"/>
                        <Setter Property="Margin" Value="0" TargetName="ContentPresenterMain"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Opacity" Value=".9"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
            <Setter Property="Opacity" Value=".6"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Opacity" Value="0.4"/>
        </Trigger>
    </Style.Triggers>
</Style>
    </hc:Window.Resources>
</hc:Window>

其中样式中设置一个默认颜色。

xml 复制代码
    <!--给local:IconElement.Foreground设置一个默认颜色-->
    <Setter Property="local:IconElement.Foreground" Value="{DynamicResource PrimaryTextBrush}"/>

页面使用附加属性

在案例项目中引入,并设置local:IconElement.ForegroundInput2GeometryBrush

xml 复制代码
<Button Style="{StaticResource ButtonBaseStyleX}"
        Width="Auto"
        Padding="12,6"
        Height="36"
        FontSize="14"
        Content="多色样例"
        hc:IconElement.Geometry="{StaticResource InputGeometry}"
        local:IconElement.Foreground="{StaticResource Input2GeometryBrush}"
        hc:IconElement.Width="24" hc:IconElement.Height="20"></Button>

比对效果如下:

当然,不这样弄行不行?也可以通过三方开源nugetSharpVectors 直接使用svg 文件,减少重复Brush 对象的创建。

总结

以上就是在HandyControl 库中,不引入其他三方库时,实现多彩路径svg 按钮的解决方案,这里主要是对GeometryGroupStyle 覆写、DrawingBrushDrawingGroupDrawingGeometry以及 附加属性 的整合应用,内容有点绕,目的也就是为了多彩路径。

相关推荐
2501_930707782 小时前
使用C#代码获取PDF文件的页数
开发语言·pdf·c#
人工智能AI技术2 小时前
C# Runner + OpenClaw双实战:用.NET写原生AI Agent,告别Python依赖
人工智能·c#
似水明俊德11 小时前
02-C#
开发语言·c#
似水明俊德11 小时前
01-C#.Net-泛型-面试题
java·开发语言·面试·c#·.net
似水明俊德12 小时前
07-C#
开发语言·c#
似水明俊德13 小时前
12-C#
开发语言·数据库·oracle·c#
似水明俊德15 小时前
01-C#.Net-泛型-学习笔记
java·笔记·学习·c#·.net
篮l球场15 小时前
Trie(字典树/前缀树)
开发语言·c#
似水明俊德15 小时前
15-C#
android·开发语言·c#