自学内容网 自学内容网

Avalonia:自定义控件

目录

引言

1、Avalonia的属性系统

1.1、自定义样式属性

1.2、只读属性、直接属性

1.2.1、数据验证支持

1.2.2、直接属性与样式属性决策 

1.3、附加属性

1.4、快速创建属性

2、自定义模板控件 TemplatedControl

2.1、绑定到父级

 2.2、TemplateBinding的双向绑定

 2.3、可以使用TemplateBinding的类型


引言

如果您想创建自己的控件,Avalonia中有三个主要的控件类型。首先要做的是选择最适合您使用场景的控件类型。

UserControls(用户控件)

组合内置控件进行自定义UserControl.axaml

TemplatedControls(模板控件)

支持主题和定义样式,Avalonia定义的大多数标准控件属于此类型

Controls(基本控件)

通过重写Visual.Render方法使用几何图形进行绘制。TextBlockImage等控件属于此类型

1、Avalonia的属性系统

1.1、自定义样式属性

如果希望它具有可以由_Avalonia UI_样式系统设置的属性,则需要定义一个静态只读字段并使用AvaloniaProperty.Register方法来注册样式化属性。样式化属性将在运行时和预览面板中均起作用。

using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;

namespace AvaloniaCCExample.CustomControls
{
    public class MyCustomControl : Control
    {
        //静态属性字段: Name + Property
        public static readonly StyledProperty<IBrush?> BackgroundProperty =
            Border.BackgroundProperty.AddOwner<MyCustomControl>();
        //实例公开访问属性
        public IBrush? Background
        {
            get { return GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
        //属性涉及的更改变化
        public sealed override void Render(DrawingContext context)
        {
            if (Background != null)
            {
                var renderSize = Bounds.Size;
                context.FillRectangle(Background, new Rect(renderSize));
            }
            base.Render(context);
        }
    }
}

使用AvaloniaProperty.Register注册的StyledProperty维护了一个优先级列表,其中包含允许样式工作的值和绑定。 

样式属性通过调用AddOwner来添加一个所有者,指定该属性所属对象,进行限定

1.2、只读属性、直接属性

要创建一个只读属性,您可以使用AvaloniaProperty.RegisterDirect方法。以下是Visual如何注册只读的Bounds属性:

public static readonly DirectProperty<Visual, Rect> BoundsProperty =
    AvaloniaProperty.RegisterDirect<Visual, Rect>(
        nameof(Bounds),
        o => o.Bounds);

private Rect _bounds;

public Rect Bounds
{
    get { return _bounds; }
    private set { SetAndRaise(BoundsProperty, ref _bounds, value); }
}

直接属性,不需要样式化,但允许值和绑定。例如 ItemControlItems

public static readonly DirectProperty<ItemsControl, IEnumerable> ItemsProperty =
    AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable>(
        nameof(Items),
        o => o.Items,
        (o, v) => o.Items = v);

private IEnumerable _items = new AvaloniaList<object>();

public IEnumerable Items
{
    get { return _items; }
    set { SetAndRaise(ItemsProperty, ref _items, value); }
}

 与样式化属性一样,您可以在直接属性上调用AddOwner来添加一个所有者进行限定

public static readonly DirectProperty<MyControl, IEnumerable> ItemsProperty =
    ItemsControl.ItemsProperty.AddOwner<MyControl>(
        o => o.Items,
        (o, v) => o.Items = v);

private IEnumerable _items = new AvaloniaList<object>();

public IEnumerable Items
{
    get { return _items; }
    set { SetAndRaise(ItemsProperty, ref _items, value); }
}

1.2.1、数据验证支持

 如果要允许属性验证数据并显示验证错误消息,则该属性必须实现为DirectProperty,并且必须启用验证支持enableDataValidation: true

public static readonly DirectProperty<MyControl, int> ValueProperty =
    AvaloniaProperty.RegisterDirect<MyControl, int>(
        nameof(Value),
        o => o.Value,
        (o, v) => o.Value = v, 
        enableDataValidation: true);

如果要重用另一个类的直接属性,也可以启用数据验证。在这种情况下,请使用AddOwnerWithDataValidation。 

示例:TextBox.TextProperty属性重用TextBlock.TextProperty,但添加了验证支持

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

1.2.2、直接属性与样式属性决策 

通常情况下,应将属性声明为样式化属性。但是,直接属性具有优点和缺点:

优点:

  • 每个实例不需要额外的对象来存储属性
  • 属性getter是标准的C#属性getter
  • 属性setter是引发事件的标准C#属性setter
  • 您可以添加数据验证支持

缺点:

  • 无法从父控件继承值
  • 无法利用Avalonia的样式系统
  • 属性值是一个字段,因此无论属性是否在对象上设置,都会被分配内存

因此,当满足以下要求时,请使用直接属性:

  • 属性不需要样式化
  • 属性通常或总是具有值

1.3、附加属性

附加属性的定义与样式化属性几乎相同,只是它们使用RegisterAttached方法进行注册,并且它们的访问器被定义为静态方法。

以下是Grid如何定义其Grid.Column附加属性:

public static readonly AttachedProperty<int> ColumnProperty =
    AvaloniaProperty.RegisterAttached<Grid, Control, int>("Column");

public static int GetColumn(Control element)
{
    return element.GetValue(ColumnProperty);
}

public static void SetColumn(Control element, int value)
{
    element.SetValue(ColumnProperty, value);
}

附加属性的容器类。必须继承自AvaloniaObject

AvaloniaProperty.RegisterAttached方法还接受其他一些参数:

  • defaultValue:为属性设置默认值。请确保只传递值类型和不可变类型,因为传递引用类型将导致所有注册了该属性的实例使用同一个对象。
  • inherits:指定属性的默认值应来自父控件。
  • defaultBindingMode:属性的默认绑定模式。可以设置为OneWayTwoWayOneTimeOneWayToSource
  • validate:一个类型为Func<TOwner, TValue, TValue>的验证/强制函数。该函数接受正在设置属性的类的实例和值,并返回强制后的值,或者对于无效值抛出异常。

1.4、快速创建属性

 在vs中利用snippet可以快速帮我们创建属性代码,Avalonia本身提供了许多代码片段,例如附加属性、直接属性、路由时间、样式属性等。

2、自定义模板控件 TemplatedControl

2.1、绑定到父级

当你创建一个控件模板并且想要绑定到模板化的父级时,你可以使用以下方式:

<TextBlock Name="tb" Text="{TemplateBinding Caption}"/>

<!-- 这与以下方式相同 -->
<TextBlock Name="tb" Text="{Binding Caption, RelativeSource={RelativeSource TemplatedParent}}"/>

TemplateBinding 只接受单个属性而不是属性路径,所以如果你想要使用属性路径进行绑定,你必须使用第二种语法:

<!-- 这样是行不通的,因为 TemplateBinding 只接受单个属性 -->
<TextBlock Name="tb" Text="{TemplateBinding Caption.Length}"/>

<!-- 在这种情况下必须使用以下语法 -->
<TextBlock Name="tb" Text="{Binding Caption.Length, RelativeSource={RelativeSource TemplatedParent}}"/>

 2.2、TemplateBinding的双向绑定

由于性能原因,TemplateBinding 只支持 OneWay 模式(与WPF相同),如果在控件模板中需要 TwoWay 绑定,则需要使用完整的语法

{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}

 2.3、可以使用TemplateBinding的类型

TemplateBinding 只能在 IStyledElement 上使用。

<!-- 这样是行不通的,因为 GeometryDrawing 不是 IStyledElement。 -->
<GeometryDrawing Brush="{TemplateBinding Foreground}"/>

<!-- 在这种情况下必须使用以下语法。 -->
<GeometryDrawing Brush="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"/>

原文地址:https://blog.csdn.net/weixin_42253874/article/details/142375310

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!