WPF内置属性的LocalizedCategoryAttribute显示一致性问题
问题根因
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

