WPF自定义Icon控件:如何关联IconName与PathGeometry的x:Key显示图标
解决方案
你的核心问题是**IconName 属性类型与传入的字符串资源键不匹配**,且缺少从资源键到 PathGeometry 的关联逻辑。下面提供两种可行的实现方式:
方式一:使用值转换器(ValueConverter)
1. 修改 Icon.cs 中的 IconName 属性类型
将 IconName 的类型从 Geometry 改为 string,因为你要传入的是资源键字符串:
public class Icon : Control { public static readonly DependencyProperty IconNameProperty = DependencyProperty.Register("IconName", typeof(string), typeof(Icon), new PropertyMetadata(string.Empty)); public string IconName { get { return (string)GetValue(IconNameProperty); } set { SetValue(IconNameProperty, value); } } // 保留其他属性和静态构造函数 public static readonly DependencyProperty IconHeightProperty = DependencyProperty.Register("IconHeight", typeof(double), typeof(Icon), new PropertyMetadata(24.0)); public double IconHeight { get { return (double)GetValue(IconHeightProperty); } set { SetValue(IconHeightProperty, value); } } public static readonly DependencyProperty IconColorProperty = DependencyProperty.Register("IconColor", typeof(Brush), typeof(Icon), new PropertyMetadata(Brushes.Black)); public Brush IconColor { get { return (Brush)GetValue(IconColorProperty); } set { SetValue(IconColorProperty, value); } } static Icon() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Icon), new FrameworkPropertyMetadata(typeof(Icon))); } }
2. 创建字符串转 Geometry 的转换器
添加一个实现 IValueConverter 的类,负责根据资源键查找对应的 PathGeometry:
public class StringToGeometryConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string resourceKey && !string.IsNullOrEmpty(resourceKey)) { // 先尝试从应用全局资源查找 var geometry = Application.Current.TryFindResource(resourceKey) as Geometry; return geometry ?? Geometry.Empty; } return Geometry.Empty; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
3. 更新 Generic.xaml 中的模板
在资源字典中声明转换器,并在 Path 的 Data 绑定中使用它:
<!-- 声明转换器(确保命名空间 local 指向你的控件库命名空间) --> <local:StringToGeometryConverter x:Key="StringToGeometryConverter"/> <!-- 保留你的 PathGeometry 资源 --> <PathGeometry x:Key="Icon_Close" Figures="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/> <PathGeometry x:Key="Icon_WndMinimize" Figures="M19,13H5V11H19V13Z"/> <PathGeometry x:Key="Icon_WndMaximize" Figures="M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z"/> <Style TargetType="{x:Type local:Icon}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:Icon}"> <Viewbox Height="{TemplateBinding IconHeight}" Width="Auto"> <Canvas Height="24" Width="24"> <!-- 使用转换器绑定 IconName 到 Path.Data --> <Path Fill="{TemplateBinding IconColor}" Data="{TemplateBinding IconName, Converter={StaticResource StringToGeometryConverter}}"/> </Canvas> </Viewbox> </ControlTemplate> </Setter.Value> </Setter> </Style>
方式二:控件内部处理资源查找
如果不想用转换器,可以在控件内部监听 IconName 的变化,自动查找对应的资源:
1. 修改 Icon.cs,添加内部 Geometry 属性和回调
public class Icon : Control { // 内部 Geometry 属性,用于绑定到 Path.Data public static readonly DependencyProperty IconGeometryProperty = DependencyProperty.Register("IconGeometry", typeof(Geometry), typeof(Icon), new PropertyMetadata(Geometry.Empty)); public Geometry IconGeometry { get { return (Geometry)GetValue(IconGeometryProperty); } private set { SetValue(IconGeometryProperty, value); } } // 修改 IconName 属性,添加变化回调 public static readonly DependencyProperty IconNameProperty = DependencyProperty.Register("IconName", typeof(string), typeof(Icon), new PropertyMetadata(string.Empty, OnIconNameChanged)); public string IconName { get { return (string)GetValue(IconNameProperty); } set { SetValue(IconNameProperty, value); } } // 其他属性(IconHeight、IconColor)保持不变... static Icon() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Icon), new FrameworkPropertyMetadata(typeof(Icon))); } // IconName 变化时触发,查找对应资源 private static void OnIconNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var icon = d as Icon; if (icon == null) return; var resourceKey = e.NewValue as string; if (string.IsNullOrEmpty(resourceKey)) { icon.IconGeometry = Geometry.Empty; return; } // 优先从控件自身资源查找,再从全局应用资源查找 var geometry = icon.TryFindResource(resourceKey) as Geometry; if (geometry == null) { geometry = Application.Current.TryFindResource(resourceKey) as Geometry; } icon.IconGeometry = geometry ?? Geometry.Empty; } }
2. 更新 Generic.xaml 模板
将 Path.Data 绑定到新的 IconGeometry 属性:
<Path Fill="{TemplateBinding IconColor}" Data="{TemplateBinding IconGeometry}"/>
最终使用
两种方式下,MainWindow.xaml 的用法保持不变:
<uiLib:Icon IconHeight="100" IconColor="White" IconName="Icon_Close"/>
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

