You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF内置属性的LocalizedCategoryAttribute显示一致性问题

解决PropertyGrid内置属性Category本地化随机失效问题

问题根因

FontFamily是控件继承来的内置属性,本身自带CategoryAttribute("Appearance")标记。你在自定义控件上给它加LocalizedCategoryAttribute后,这个属性的特性集合里会同时存在两个分类特性。PropertyGrid解析时没有固定优先级,导致有时候读默认的,有时候读你自定义的。而你的TextColor是自己添加的属性,没有默认分类特性,所以一直正常显示本地化值。

可行解决办法

方法1:重写内置属性,覆盖分类特性

不用直接给继承的FontFamily加特性,而是在自定义控件里重写这个属性,只标记你的本地化分类特性:

public override FontFamily FontFamily
{
    get => base.FontFamily;
    set => base.FontFamily = value;
}

重写后,这个属性的特性集合就只有你添加的LocalizedCategoryAttribute,不会再携带父类的默认分类标记,PropertyGrid自然只会读取本地化值。

方法2:自定义属性描述符,过滤重复分类特性

如果不想重写属性,可以通过自定义PropertyDescriptor清理特性集合,只保留你的本地化分类:

public class LocalizedCategoryPropertyDescriptor : PropertyDescriptor
{
    private readonly PropertyDescriptor _original;

    public LocalizedCategoryPropertyDescriptor(PropertyDescriptor original) : base(original)
    {
        _original = original;
    }

    public override AttributeCollection Attributes
    {
        get
        {
            var attrs = _original.Attributes.Cast<Attribute>().ToList();
            // 删除所有默认CategoryAttribute,仅保留自定义的LocalizedCategoryAttribute
            attrs.RemoveAll(a => a is CategoryAttribute && !(a is LocalizedCategoryAttribute));
            return new AttributeCollection(attrs.ToArray());
        }
    }

    // 其他PropertyDescriptor成员直接委托给原描述符
    public override bool CanResetValue(object component) => _original.CanResetValue(component);
    public override object GetValue(object component) => _original.GetValue(component);
    public override void ResetValue(object component) => _original.ResetValue(component);
    public override void SetValue(object component, object value) => _original.SetValue(component, value);
    public override bool ShouldSerializeValue(object component) => _original.ShouldSerializeValue(component);
    public override Type ComponentType => _original.ComponentType;
    public override bool IsReadOnly => _original.IsReadOnly;
    public override Type PropertyType => _original.PropertyType;
}

然后给自定义控件添加类型转换器,用这个描述符处理属性:

public class ExampleControlTypeConverter : TypeConverter
{
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        var originalProps = base.GetProperties(context, value, attributes);
        var modifiedProps = new PropertyDescriptorCollection(null);
        
        foreach (PropertyDescriptor prop in originalProps)
        {
            if (prop.Attributes.OfType<LocalizedCategoryAttribute>().Any())
            {
                modifiedProps.Add(new LocalizedCategoryPropertyDescriptor(prop));
            }
            else
            {
                modifiedProps.Add(prop);
            }
        }
        return modifiedProps;
    }
}

最后给控件标记这个转换器:

[TypeConverter(typeof(ExampleControlTypeConverter))]
public class exampleControl : Control
{
    // 你的控件逻辑代码
}

方法3:调整自定义特性优先级

修改LocalizedCategoryAttribute的TypeId,让它和默认CategoryAttribute区分开,理论上PropertyGrid会优先使用自定义特性。不过这个方法可靠性不如前两种,因为不同.NET版本的PropertyGrid解析逻辑可能存在差异,建议优先选择前两种方案。

验证

使用方法1的话,修改后直接编译运行,PropertyGrid中的FontFamily就会一直显示你设置的"Fuentes de Botones"分类。方法2则通过拦截属性描述过程,确保不会读取到默认的分类特性。

内容的提问来源于stack exchange,提问作者jnpowrie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:45:31