修改PackIconDataFactory.cs
private static readonly Lazy<IReadOnlyDictionary<PackIconKind, string>> s_instanceHolder = new(Create);
internal static IReadOnlyDictionary<PackIconKind, string> Data => s_instanceHolder.Value;
private static IReadOnlyDictionary<PackIconKind, string> Create() => new Dictionary<PackIconKind, string>
{
// 字典
}编写自己的IconElement.cs
using System.Windows;
using System.Windows.Media;
namespace AtmsII;
/// <summary>
/// 图标元素
/// </summary>
public static class IconElement
{
#region PackIconKind
/// <summary>
/// 依赖属性:图标标识
/// </summary>
public static readonly DependencyProperty PackIconKindProperty = DependencyProperty.RegisterAttached(
"PackIconKind",
typeof(PackIconKind),
typeof(IconElement),
new PropertyMetadata(default(PackIconKind), (d, e) => {
if (e.NewValue is PackIconKind kind)
{
HandyControl.Controls.IconElement.SetGeometry(d, Geometry.Parse(PackIconDataFactory.Data[kind]));
}
else
{
HandyControl.Controls.IconElement.SetGeometry(d, Geometry.Empty);
}
}));
/// <summary>
/// 设置图标标识
/// </summary>
/// <param name="element">依赖对象</param>
/// <param name="value">属性值</param>
public static void SetPackIconKind(DependencyObject element, PackIconKind value) => element.SetValue(PackIconKindProperty, value);
/// <summary>
/// 获取图标标识
/// </summary>
/// <param name="element">依赖对象</param>
/// <returns>属性值</returns>
public static PackIconKind GetPackIconKind(DependencyObject element) => (PackIconKind)element.GetValue(PackIconKindProperty);
#endregion
#region PackIconKindSelected
/// <summary>
/// 依赖属性:图标标识
/// </summary>
public static readonly DependencyProperty PackIconKindSelectedProperty = DependencyProperty.RegisterAttached(
"PackIconKindSelected",
typeof(PackIconKind),
typeof(IconElement),
new PropertyMetadata(default(PackIconKind), (d, e) => {
if (e.NewValue is PackIconKind kind)
{
HandyControl.Controls.IconSwitchElement.SetGeometrySelected(d, Geometry.Parse(PackIconDataFactory.Data[kind]));
}
else
{
HandyControl.Controls.IconSwitchElement.SetGeometrySelected(d, Geometry.Empty);
}
}));
/// <summary>
/// 设置图标标识
/// </summary>
/// <param name="element">依赖对象</param>
/// <param name="value">属性值</param>
public static void SetPackIconKindSelected(DependencyObject element, PackIconKind value) => element.SetValue(PackIconKindSelectedProperty, value);
/// <summary>
/// 获取图标标识
/// </summary>
/// <param name="element">依赖对象</param>
/// <returns>属性值</returns>
public static PackIconKind GetPackIconKindSelected(DependencyObject element) => (PackIconKind)element.GetValue(PackIconKindSelectedProperty);
#endregion
}编写自己的MetaPackIcon.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace AtmsII;
/// <summary>
/// 图标控件
/// </summary>
public class MetaPackIcon : Control
{
/// <summary>
/// 外部图标索引
/// </summary>
private static readonly Lazy<IDictionary<string, string>> s_iconIndex = new(() =>
{
try
{
string path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Icons", $"{nameof(MetaPackIcon)}.json");
if (System.IO.File.Exists(path))
{
return JsonConvert.DeserializeObject<Dictionary<string, string>>(System.IO.File.ReadAllText(path)) ?? [];
}
}
catch
{
// 忽略异常
}
return new Dictionary<string, string>();
});
static MetaPackIcon() => DefaultStyleKeyProperty.OverrideMetadata(typeof(MetaPackIcon), new FrameworkPropertyMetadata(typeof(MetaPackIcon)));
/// <summary>
/// 应用模板时获取 Path 引用
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateData();
}
#region Kind
/// <summary>
/// 依赖属性:内部图标标识
/// </summary>
public static readonly DependencyProperty PackIconKindProperty = DependencyProperty.Register(
nameof(PackIconKind),
typeof(PackIconKind),
typeof(MetaPackIcon),
new PropertyMetadata(default(PackIconKind), static (d, e) =>
{
((MetaPackIcon)d).UpdateData();
}));
/// <summary>
/// 内部图标标识
/// </summary>
public PackIconKind PackIconKind
{
get => (PackIconKind)GetValue(PackIconKindProperty); set => SetValue(PackIconKindProperty, value);
}
/// <summary>
/// 依赖属性:图标标识
/// </summary>
public static readonly DependencyProperty KindProperty = DependencyProperty.Register(
nameof(Kind),
typeof(string),
typeof(MetaPackIcon),
new FrameworkPropertyMetadata(default(string), static (d, e) =>
{
((MetaPackIcon)d).UpdateData();
}));
/// <summary>
/// 图标标识
/// </summary>
public string Kind
{
get => (string)GetValue(KindProperty); set => SetValue(KindProperty, value);
}
private void UpdateData()
{
string? data;
if (string.IsNullOrEmpty(Kind))
{
data = PackIconDataFactory.Data[PackIconKind];
}
else
{
s_iconIndex.Value.TryGetValue(Kind, out data);
}
Data = data;
}
#endregion
#region Data
private static readonly DependencyPropertyKey DataPropertyKey = DependencyProperty.RegisterReadOnly(nameof(Data), typeof(string), typeof(MetaPackIcon), new PropertyMetadata(string.Empty));
/// <summary>
/// 依赖属性:路径数据
/// </summary>
public static readonly DependencyProperty DataProperty = DataPropertyKey.DependencyProperty;
/// <summary>
/// 路径数据
/// </summary>
[TypeConverter(typeof(GeometryConverter))]
public string? Data
{
get => (string?)GetValue(DataProperty); private set => SetValue(DataPropertyKey, value);
}
#endregion
#region ScaleToSizeOfWith
/// <summary>
/// 依赖属性:自适应大小
/// </summary>
public static readonly DependencyProperty ScaleToSizeOfWithProperty = DependencyProperty.Register(
nameof(ScaleToSizeOfWith),
typeof(FrameworkElement),
typeof(MetaPackIcon),
new PropertyMetadata(null, OnScaleToSizeOfWithChanged));
/// <summary>
/// 自适应大小
/// </summary>
public FrameworkElement? ScaleToSizeOfWith
{
get => (FrameworkElement?)GetValue(ScaleToSizeOfWithProperty); set => SetValue(ScaleToSizeOfWithProperty, value);
}
private static void OnScaleToSizeOfWithChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MetaPackIcon icon)
{
BindingOperations.ClearBinding(icon, HeightProperty);
BindingOperations.ClearBinding(icon, WidthProperty);
if (e.NewValue is FrameworkElement source)
{
var multiBinding = new MultiBinding
{
Converter = new ElementInnerSizeConverter(),
Mode = BindingMode.OneWay
};
multiBinding.Bindings.Add(new Binding(nameof(ActualHeight)) { Source = source });
multiBinding.Bindings.Add(new Binding(nameof(ActualWidth)) { Source = source });
multiBinding.Bindings.Add(new Binding("(Control.Padding)") { Source = source, FallbackValue = new Thickness(0) });
icon.SetBinding(HeightProperty, multiBinding);
icon.SetBinding(WidthProperty, multiBinding);
}
}
}
/// <summary>
/// 计算控件扣除Padding后的内部可用正方形尺寸
/// 参数顺序:[ActualHeight, ActualWidth, Padding]
/// </summary>
internal class ElementInnerSizeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if ((values.Length >= 3)
&& (values[0] is double actualHeight)
&& (values[1] is double actualWidth)
&& values[2] is Thickness padding)
{
double innerH = actualHeight - padding.Top - padding.Bottom;
double innerW = actualWidth - padding.Left - padding.Right;
return Math.Max(16.0, Math.Min(innerH, innerW));
}
return 16.0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => [];
}
#endregion
#region StrokeThickness
/// <summary>
/// 依赖属性:笔画粗细
/// </summary>
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
nameof(StrokeThickness),
typeof(double),
typeof(MetaPackIcon),
new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 笔画粗细
/// </summary>
public double StrokeThickness
{
get => (double)GetValue(StrokeThicknessProperty); set => SetValue(StrokeThicknessProperty, value);
}
#endregion
#region IsFill
/// <summary>
/// 依赖属性:是否填充
/// </summary>
public static readonly DependencyProperty IsFillProperty = DependencyProperty.Register(
nameof(IsFill),
typeof(bool),
typeof(MetaPackIcon),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 是否填充
/// </summary>
public bool IsFill
{
get => (bool)GetValue(IsFillProperty); set => SetValue(IsFillProperty, value);
}
#endregion
#region Utilities
/// <summary>
/// 获取所有可用图标名称
/// </summary>
public static ICollection<string> GetAvailableIcons() => s_iconIndex.Value.Keys;
/// <summary>
/// 检查图标是否存在
/// </summary>
public static bool IconExists(string kind) => !string.IsNullOrEmpty(kind) && s_iconIndex.Value.ContainsKey(kind);
#endregion
}编写MetaPackIcon样式Themes\Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AtmsII">
<Style TargetType="{x:Type local:MetaPackIcon}">
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="FlowDirection" Value="LeftToRight" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="Background" Value="{DynamicResource RegionBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MetaPackIcon}">
<Grid Background="{TemplateBinding Background}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
<Path x:Name="IconPath"
Data="{Binding Data, RelativeSource={RelativeSource TemplatedParent}}"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="{TemplateBinding StrokeThickness}"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RenderOptions.EdgeMode="Aliased"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFill" Value="False">
<Setter TargetName="IconPath" Property="Fill" Value="{x:Null}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="IconPath" Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>用法示例:
<Button Margin="4" Style="{StaticResource ButtonBaseStyle}" Width="48" Height="48">
<atmsii:MetaPackIcon PackIconKind="Plus" Foreground="Green" Width="32" Height="32"/>
</Button>
<ToggleButton Margin="4" Style="{StaticResource ToggleButtonIcon}" Width="48" Height="48" Foreground="Green"
atmsii:IconElement.PackIconKind="{x:Static atmsii:PackIconKind.Plus}"
atmsii:IconElement.PackIconKindSelected="{x:Static atmsii:PackIconKind.Minus}"/>原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。