Avalonia 学习之 属性

发布时间 2023-04-18 18:03:28作者: MChuang

前言

在WPF工作期间,WPF架构师面临一个有趣的问题。每个视觉对象都必须定义数百个(如果不是数千个)属性,其中大多数属性每次都有默认值。为每个对象中的每个属性定义一个支持字段将导致大量内存消耗,尤其是不必要的,因为每次这些属性中约有90%将具有默认值。

所以,为了解决这个问题,他们想出了附加属性。附加属性不是将属性值存储在对象内的支持字段中,而是将值存储在一种static hashtable或Dictionary(或Map)中,其中值由可能具有这些属性的各种对象索引。只有具有非默认属性值的对象在hashtable中,如果对象的条目不在hashtable中,则假定该对象的属性具有默认值。附加属性的静态哈希表实际上可以在任何类中定义。

在我们写WPF程序的时候, 是可以定义依赖属性和附加属性的
依赖属性: 当您需要单独创建控件时, 并且希望控件的某个部分能够支持数据绑定时, 你则可以使用到依赖属性。
通过DependencyProperty.Register注册依赖属性
附加属性: 这种情况很多, 正因为WPF当中并不是所有的内容都支持数据绑定, 但是我们希望其支持数据绑定, 这样我们就可以创建基于自己声明的附加属性,添加到元素上, 让其元素的某个原本不支持数据绑定的属性间接形成绑定关系。
通过 DependencyProperty.RegisterAttached注册附加属性

在我们写Avalonia框架的时候,也会有类似的属性,来解决绑定等问题, 它们分别是
AttachedProperty 附加属性
StyledProperty 样式属性
DirectProperty 直接属性

附加属性

附加属性相对来说比较独立,可以随便放到一个类文件中,甚至可以统一全部放到一个类文件中,只是将属性放在了由Class对象的索引的一些static Dictionary对象中,定义完还可以为属性指定一些默认值,这样说的话, 是不是这个Class对象定义为Static的比较好一点?

定义一个附加属性,可以通过代码片段 avap 来快速定义

   #region Thick Attached Avalonia Property
        public static double GetThick (AvaloniaObject obj)
        {
            return obj.GetValue(ThickProperty);
        }

        public static void SetThick (AvaloniaObject obj, double value)
        {
            obj.SetValue(ThickProperty, value);
        }

        public static readonly AttachedProperty<double> ThickProperty =
            AvaloniaProperty.RegisterAttached<object, IControl, double>
            (
                "Thick",
                 3.0 // 默认值
            );
        #endregion

注册方式为 AvaloniaProperty.RegisterAttached

在使用的时候,需要首先在窗体中引入相应的命名空间 如
xmlns:ap="clr-namespace:AvaloniaMinDemo.AttachedProperties"

<ListBoxItem  ap:AttachedProperties.InitImg="avares://AvaloniaMinDemo/Images/icon1.png"
ap:AttachedProperties.SelectImg="{Binding **}">

样式属性

样式属性与附加属性基本相同,只是它们只能定义在使用他们的同一个类中,如用户控件、控件模板中,相应的,这个控件也就具有了这个属性, 我们在引用这个控件的时候,是可以设置这个属性的。

定义一个样式属性,可以通过代码片段 avsp 来快速定义

  #region Thick Styled Avalonia Property
        public double Thick
        {
            get { return GetValue(ThickProperty); }
            set { SetValue(ThickProperty, value); }
        }

        public static readonly StyledProperty<double> ThickProperty =
            AvaloniaProperty.Register<MainWindow, double>
            (
                nameof(Thick)
            );
        #endregion Thick Styled Avalonia Property

使用的时候,直接当控件属性来使用

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		Thick="8"

有时候,我们新建的控件的属性可能已存在于另一个控件上, 背景 就是 一个很好的示例。 若要注册在另一个控件上定义的属性,请调用StyleProperty.AddOwner:
可以通过 代码片段 avow 来创建一个Border控件的CornerRadiusProperty属性 给当前的窗口 如

        #region CornerRadius shared Styled Avalonia Property
        public CornerRadius CornerRadius
        {
            get { return GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
            Border.CornerRadiusProperty.AddOwner<MainWindow>();
        #endregion CornerRadius shared Styled Avalonia Property

直接属性

直接属性的属性获取器是标准的C#属性获取器,它是样式属性的轻量化版本,通常我们在不需要样式或者总是需要定义值的时候,就定义一个直接属性
可以通过代码片段avdr来创建

        #region Thick Direct Avalonia Property
        private double _thick = default;

        public static readonly DirectProperty<MainWindow, double> ThickProperty =
            AvaloniaProperty.RegisterDirect<MainWindow, double>
            (
                nameof(Thick),
                o => o.Thick,
                (o, v) => o.Thick = v
            );

        public double Thick
        {
            get => _thick;
            set
            {
                SetAndRaise(ThickProperty, ref _thick, value);
            }
        }

        #endregion Thick Direct Avalonia Property

直接属性还可以添加数据验证,enableDataValidation: true 如:

public static readonly DirectProperty<TextBox, string?> TextProperty =
    TextBlock.TextProperty.AddOwnerWithDataValidation<TextBox>(
        o => o.Text,
        (o, v) => o.Text = v,
        defaultBindingMode: BindingMode.TwoWay,
        enableDataValidation: true);

总结

最常用的应该还是样式属性,对于哪些多个控件 共有的属性,才会去定义附加属性。

直接属性和样式属性类似,都只能在 本窗体、控件中定义。
他们三个都派生自AvaloniaProperty类。

属性 注册方法 代码片段
附加属性 AvaloniaProperty.RegisterAttached avap
样式属性 AvaloniaProperty.Register avsp
StyleProperty.AddOwner avow
直接属性 AvaloniaProperty.RegisterDirec avdr