Avalonia:自定义控件
目录
引言
如果您想创建自己的控件,Avalonia中有三个主要的控件类型。首先要做的是选择最适合您使用场景的控件类型。
UserControls(用户控件) | 组合内置控件进行自定义UserControl.axaml |
TemplatedControls(模板控件) | 支持主题和定义样式,Avalonia定义的大多数标准控件属于此类型 |
Controls(基本控件) | 通过重写Visual.Render 方法使用几何图形进行绘制。TextBlock 和Image 等控件属于此类型 |
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); }
}
直接属性,不需要样式化,但允许值和绑定。例如 ItemControl 的 Items
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
:属性的默认绑定模式。可以设置为OneWay
、TwoWay
、OneTime
或OneWayToSource
。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)!