如何自动为Avalonia应用生成StyledProperty和DirectProperty?

摘要:最近学习了源生成器,遂仿照CommunityToolkitWindows中的DependencyPropertyGenerator写了个生成器,可自动生成Avalonia中的StyledProperty和DirectProperty Nu
最近学习了源生成器,遂仿照CommunityToolkit/Windows中的DependencyPropertyGenerator写了个生成器,可自动生成Avalonia中的StyledProperty和DirectProperty NuGet:https://www.nuget.org/packages/PropertyGenerator.Avalonia Github:https://github.com/zxbmmmmmmmmm/PropertyGenerator 先决条件 Avalonia版本:≥ 11.3.0 由于使用了field关键字和部分属性,需要在项目文件内将LangVersion设置为preview StyledProperty 在需要生成StyledProperty的部分属性上添加GeneratedStyledProperty特性即可 [GeneratedStyledProperty] public partial int Count { get; set; } 生成的代码: StyledProperty<int> CountProperty = AvaloniaProperty.Register<MainWindow, int>(name: nameof(Count)); public partial int Count { get => GetValue(CountProperty); set => SetValue(CountProperty, value); } StyledProperty不支持直接设置默认值,需要使用以下写法 [GeneratedStyledProperty(10)] public partial int Count { get; set; } 生成的代码: Avalonia.StyledProperty<int> CountProperty = AvaloniaProperty.Register<MainWindow, int>(name: nameof(Count), defaultValue: 10); public partial int Count { get => GetValue(CountProperty); set => SetValue(CountProperty, value); } StyledProperty的所有功能都被支持(仅作展示) [GeneratedStyledProperty( DefaultValueCallback = nameof(DefaultValueCallback), DefaultValue = true, Validate = nameof(Validate), Coerce = nameof(Coerce), EnableDataValidation = true, Inherits = true, DefaultBindingMode = BindingMode.TwoWay)] public partial bool? IsStarted { get; set; } private static bool DefaultValueCallback() { return true; } private static bool Validate(bool? value) { return true; } private static bool? Coerce(AvaloniaObject x, bool? y) { return true; } 生成的代码: StyledProperty<bool?> IsStartedProperty = AvaloniaProperty.Register<MainWindow, bool?>( name: nameof(IsStarted), defaultValue: DefaultValueCallback(), validate: Validate, coerce: Coerce, enableDataValidation: true, inherits: true, defaultBindingMode:BindingMode.TwoWay); public partial bool? IsStarted { get => GetValue(IsStartedProperty); set => SetValue(IsStartedProperty, value); } DirectProperty 和GeneratedStyledProperty的写法相似: [GeneratedDirectProperty] public partial IEnumerable? Items { get; set; } DirectProperty可以被直接初始化 [GeneratedDirectProperty] public partial IEnumerable? Items { get; set; } = new AvaloniaList<object>(); 支持自定义DirectProperty的 Getter 和Setter [GeneratedDirectProperty(Getter = nameof(Getter), Setter = nameof(Setter))] public partial IEnumerable? Items { get; set; } public static IEnumerable? Getter(MainWindow o) => o.Items; public static void Setter(MainWindow o, IEnumerable? v) => o.Items = v; 生成的代码: public static readonly DirectProperty<MainWindow, IEnumerable?> ItemsProperty = AvaloniaProperty.RegisterDirect<MainWindow, IEnumerable?>( name: nameof(Items), getter: Getter, setter: Setter); public partial IEnumerable? Items { get => field; set => SetAndRaise(ItemsProperty, ref field, value); } OnPropertyChanged 使用GeneratedStyledProperty或者GeneratedDirectProperty时,会自动生成部分方法用以通知属性更改 partial void OnCountPropertyChanged(int newValue); partial void OnCountPropertyChanged(int oldValue, int newValue); partial void OnCountPropertyChanged(AvaloniaPropertyChangedEventArgs e); protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); switch (change.Property.Name) { case nameof(Count): OnCountPropertyChanged(change); OnCountPropertyChanged((int)change.NewValue); OnCountPropertyChanged((int)change.OldValue, (int)change.NewValue); break; } } 可以直接使用这些方法直接处理属性的变化: partial void OnCountPropertyChanged(int newValue) { // 处理属性变化... } 如果代码已重写OnPropertyChanged并包含其他逻辑,则可以通过DoNotGenerateOnPropertyChanged特性关闭此功能: [DoNotGenerateOnPropertyChanged] public partial class MainWindow : Window { ... } 也可以在整个程序集上禁用此功能 [assembly: DoNotGenerateOnPropertyChanged]