当前位置: 首页 > news >正文

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 + Propertypublic 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}}"/>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Unity教程(十六)敌人攻击状态的实现
  • Spring IoC 注解 总结
  • GitLab 迁移并推送代码仓库
  • 2024永久激活版 Studio One 6 Pro for mac 音乐创作编辑软件 完美兼容
  • FortiGate硬件高级测试指南
  • 数据结构——“二叉搜索树”
  • 条件编译代码记录
  • React 项目中,如何实现有效的内存管理和防止内存泄漏?
  • 二叉树(下)
  • 【CSS in Depth 2 精译_030】5.2 Grid 网格布局中的网格结构剖析(下)
  • 解决服务器首次请求异常耗时问题
  • 滚雪球学SpringCloud[6.1讲]: Spring Cloud Sleuth详解
  • CSS 布局技巧实现元素左右排列
  • 速戳!普通人利用AI商业变现的5种方式!
  • 从《中国数据库前世今生》看中国数据库技术的发展与挑战
  • 2017前端实习生面试总结
  • input的行数自动增减
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • MySQL用户中的%到底包不包括localhost?
  • nfs客户端进程变D,延伸linux的lock
  • storm drpc实例
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue.js框架原理浅析
  • Vue2.x学习三:事件处理生命周期钩子
  • Xmanager 远程桌面 CentOS 7
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 从零开始在ubuntu上搭建node开发环境
  • 仿天猫超市收藏抛物线动画工具库
  • 给github项目添加CI badge
  • 利用jquery编写加法运算验证码
  • 前端临床手札——文件上传
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 自定义函数
  • 《天龙八部3D》Unity技术方案揭秘
  • ​ssh免密码登录设置及问题总结
  • # include “ “ 和 # include < >两者的区别
  • # 利刃出鞘_Tomcat 核心原理解析(七)
  • #include
  • #vue3 实现前端下载excel文件模板功能
  • #宝哥教你#查看jquery绑定的事件函数
  • #前后端分离# 头条发布系统
  • $NOIp2018$劝退记
  • (007)XHTML文档之标题——h1~h6
  • (07)Hive——窗口函数详解
  • (C#)一个最简单的链表类
  • (javascript)再说document.body.scrollTop的使用问题
  • (笔试题)合法字符串
  • (编译到47%失败)to be deleted
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (四)React组件、useState、组件样式
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (原創) 如何將struct塞進vector? (C/C++) (STL)