Avalonia UI
首页支持GitHub 仓库English Doc
  • 👋欢迎
  • 文档
    • ⚡快速开始
      • IDE 支持
        • JetBrains Rider 设置
      • 使用 Avalonia 开发
        • Model-View-ViewModel 模式(MVVM)
        • 控件和布局
        • 数据绑定
        • 图像和动画
      • Windows
      • UserControls
      • 资产
      • 开发者工具
      • 错误和警告日志
      • 未处理的异常
      • 应用生命周期
    • 🔁数据绑定
      • 数据上下文
      • 变化通知
      • 绑定
      • 编译绑定
      • 与控件绑定
      • 转换绑定值
      • 绑定到命令
      • 绑定到任务和可观察对象
      • 使用代码进行绑定
      • 在控件模板中实现绑定
      • 绑定Classes
      • 创建和绑定到附加属性
      • Data Validation
    • 🎨样式
      • 样式
      • 选择器
      • 资源
      • 疑难解答
    • 🧰控件
      • AutoCompleteBox
      • Border
      • Buttons
        • Button
        • RepeatButton
        • RadioButton
        • ToggleButton
        • ButtonSpinner
        • SplitButton
        • ToggleSplitButton
      • Calendar
      • Canvas
      • Carousel
      • CheckBox
      • ComboBox
      • ContentControl
      • ContextMenu
      • Decorator
      • DataGrid
        • DataGridColumns
      • DatePicker
      • DockPanel
      • Expander
      • Flyouts
      • Grid
      • GridSplitter
      • Image
      • ItemsControl
      • ItemsRepeater
      • LayoutTransformControl
      • ListBox
      • MaskedTextBox
      • Menu
      • NativeMenu
      • NumericUpDown
      • Panel
      • ProgressBar
      • RelativePanel
      • ScrollBar
      • ScrollViewer
      • Separator
      • Slider
      • SplitView
      • StackPanel
      • TabControl
      • TabStrip
      • TextBlock
      • TrayIcon
      • TreeDataGrid
        • Creating a Hierarchical TreeDataGrid
        • Creating a Flat TreeDataGrid
        • TreeDataGrid column types
      • TimePicker
      • TextBox
      • ToolTip
      • TreeView
      • TransitioningContentControl
      • UserControl
      • Viewbox
      • Window
      • WrapPanel
    • 📚模板
      • 数据模板
      • 在代码中创建数据模板
      • 实现 IDataTemplate 接口
    • ✏️自定义控件
      • 控件类别
      • 定义属性
    • 🖱️输入
      • 路由事件
      • 剪贴板
      • 鼠标与触控设备
      • 快捷键
    • 🔑动画
      • 关键帧动画
      • 过渡
      • 页面过渡
    • 📐布局
      • 面板概述
      • Alignment、Margin 和 Padding
      • 创建自定义面板
    • 📦发布/分发
      • macOS
  • API 参考
    • 🗒️命名空间
      • Avalonia
      • Avalonia.Animation
        • Avalonia.Animation.Easings
        • Avalonia.Animation.Animators
      • Avalonia.Collections
      • Avalonia.Controls
      • Avalonia.Data
        • Avalonia.Data.Core.Plugins
        • Avalonia.Data.Core
        • Avalonia.Data.Converters
      • Avalonia.Diagnostics
      • Avalonia.Dialogs
  • 指南
    • 🐣基础
      • XAML 介绍
      • Code-behind
      • MVVM 架构
      • 在UI线程上操作
    • 🤿深入
      • 在树莓派上运行你的应用
      • 在树莓派上运行你的应用(使用Raspbian Lite)
      • ReactiveUI
        • 视图激活机制
        • 路由
        • 数据持久化
        • 绑定到 Sorted/Filtered 数据
    • 👩‍💻👩💻 开发人员指南
      • 🏭从源代码中构建 Avalonia
      • Avalonia 与 WPF 和 UWP 之间的比较
      • Debugging Previewer
      • Debugging the XAML compiler
      • macOS 开发
      • Release Process
      • 维护稳定的分支
  • 教程
    • 📋待办事项应用
    • 📻音乐商店应用
    • 🕸️在浏览器中运行
    • 📱为移动设备开发
  • 杂项
    • 👪社区
    • 🖥️WPF 开发者建议
    • 📋正在使用 Avalonia 的项目
    • ❔常见问题
由 GitBook 提供支持
在本页
  • Registering Styled Properties
  • Using a StyledProperty on Another Class
  • Readonly Properties
  • Attached Properties
  • Direct AvaloniaProperties
  • Using a DirectProperty on Another Class
  • When to use a Direct vs a Styled Property
  • DataValidation support

这有帮助吗?

在GitHub上编辑
  1. 文档
  2. 自定义控件

定义属性

上一页控件类别下一页输入

最后更新于2年前

这有帮助吗?

If you are creating a control, you will want to define properties on your control. You do this by defining AvaloniaPropertys for your control. Avalonia properties consist of two parts: the property definition and the CLR getter/setter for the property.

Registering Styled Properties

Unless you have a good reason not to, you should define properties on your control as styled properties. Styled properties ensure that your property will work correctly with Avalonia's .

You register a styled property by calling AvaloniaProperty.Register and storing the result in a static readonly field. You then create a standard C# property to access it.

Here's how the Border control defines its Background property:

public static readonly StyledProperty<Brush> BackgroundProperty =
    AvaloniaProperty.Register<Border, Brush>(nameof(Background));

public Brush Background
{
    get { return GetValue(BackgroundProperty); }
    set { SetValue(BackgroundProperty, value); }
}

The AvaloniaProperty.Register method also accepts a number of other parameters:

  • defaultValue: This gives the property a default value. Be sure to only pass value types and immutable types here as passing a reference type will cause the same object to be used on all instances on which the property is registered.

  • inherits: Specified that the property's default value should come from the parent control.

  • defaultBindingMode: The default binding mode for the property. Can be set to OneWay, TwoWay, OneTime or OneWayToSource.

  • validate: A validation/coercion function of type Func<TOwner, TValue, TValue>. The function accepts the instance of the class on which the property is being set and the value and returns the coerced value or throws an exception for an invalid value.

A styled property is analogous to a DependencyProperty in other XAML frameworks.

The naming convention of the property and its backing AvaloniaProperty field is important. The name of the field is always the name of the property, with the suffix Property appended.

Using a StyledProperty on Another Class

Sometimes the property you want to add to your control already exists on another control, Background being a good example. To register a property defined on another control, you call StyledProperty.AddOwner:

public static readonly StyledProperty<IBrush> BackgroundProperty =
    Border.BackgroundProperty.AddOwner<Panel>();

public Brush Background
{
    get { return GetValue(BackgroundProperty); }
    set { SetValue(BackgroundProperty, value); }
}

Note: Unlike WPF/UWP, a property must be registered on a class otherwise it cannot be set on an object of that class. This may change in future, however.

Readonly Properties

To create a readonly property you use the AvaloniaProperty.RegisterDirect method. Here is how Visual registers the readonly Bounds property:

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); }
}

As can be seen, readonly properties are stored as a field on the object. When registering the property, a getter is passed which is used to access the property value through GetValue and then SetAndRaise is used to notify listeners to changes to the property.

Attached Properties

Here's how Grid defines its Grid.Column attached property:

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);
}

Direct AvaloniaProperties

As its name suggests, RegisterDirect isn't just used for registering readonly properties. You can also pass a setter to RegisterDirect to expose a standard C# property as a Avalonia property.

A StyledProperty which is registered using AvaloniaProperty.Register maintains a prioritized list of values and bindings that allow styles to work. However, this is overkill for many properties, such as ItemsControl.Items - this will never be styled and the overhead involved with styled properties is unnecessary.

Here is how ItemsControl.Items is registered:

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); }
}

Direct properties are a lightweight version of styled properties that support the following:

  • AvaloniaObject.GetValue

  • AvaloniaObject.SetValue for non-readonly properties

  • PropertyChanged

  • Binding (only with LocalValue priority)

  • GetObservable

  • AddOwner

  • Metadata

They don't support the following:

  • Validation/Coercion (although this could be done in the property setter)

  • Overriding default values.

  • Inherited values

Using a DirectProperty on Another Class

In the same way that you can call AddOwner on a styled property, you can also add an owner to a direct property. Because direct properties reference fields on the control, you must also add a field for the property:

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); }
}

When to use a Direct vs a Styled Property

In general you should declare your properties as styled properties. However, direct properties have advantages and disadvantages:

Pros:

  • No additional object is allocated per-instance for the property

  • Property getter is a standard C# property getter

  • Property setter is a standard C# property setter that raises an event.

Cons:

  • Cannot inherit value from parent control

  • Cannot take advantage of Avalonia's styling system

  • Property value is a field and as such is allocated whether the property is set on the object or not

So use direct properties when you have the following requirements:

  • Property will not need to be styled

  • Property will usually or always have a value

DataValidation support

If you want to allow a property to validate the data and show validation error messages, the property must be implemented as a DirectProperty and validation support must be enabled (enableDataValidation: true).

Example of a property with DataValidation enabled

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

Example: TextBox.TextProperty property re-uses TextBlock.TextProperty but adds validation support

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

are defined almost identically to styled properties except that they are registered using the RegisterAttached method and their accessors are defined as static methods.

You can add support

If you want to you can also enable data validation. In this case use AddOwnerWithDataValidation.

✏️
styling system
Attached properties
data validation
re-use a direct property of another class