如何创建高级自定义控件
从自定义控件指南中摘录的内容。
这是Border
控件如何定义其Background
属性的方式:
AvaloniaProperty.Register
方法还接受其他一些参数:
defaultValue
:为属性设置默认值。请确保只传递值类型和不可变类型,因为传递引用类型将导致所有注册了该属性的实例使用同一个对象。inherits
:指定属性的默认值应来自父控件。defaultBindingMode
:属性的默认绑定模式。可以设置为OneWay
、TwoWay
、OneTime
或OneWayToSource
。validate
:一个类型为Func<TOwner, TValue, TValue>
的验证/强制函数。该函数接受正在设置属性的类的实例和值,并返回强制后的值,或者对于无效值抛出异常。
一个样式化属性类似于其他XAML框架中的
DependencyProperty
。
属性的命名约定及其对应的
AvaloniaProperty
字段的命名是重要的。字段的名称始终是属性的名称,后面附加了Property
后缀。
在另一个类上使用StyledProperty
有时,您想要添加到自定义控件的属性已经存在于另一个控件上,Background
就是一个很好的例子。要注册在另一个控件上定义的属性,您需要调用StyledProperty.AddOwner
:
public static readonly StyledProperty<IBrush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<Panel>();
public Brush Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
注意:与WPF/UWP不同,属性必须在类上注册,否则无法在该类的对象上设置属性。但这可能会在将来发生改变。
只读属性
要创建一个只读属性,您可以使用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); }
}
可以看到,只读属性被存储为对象的字段。在注册属性时,传递了一个getter,用于通过GetValue
访问属性值,然后使用SetAndRaise
通知属性更改的监听器。
附加属性
附加属性的定义与样式化属性几乎相同,只是它们使用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);
}
直接的Avalonia属性
顾名思义,RegisterDirect
不仅用于注册只读属性。您还可以将一个_setter_传递给RegisterDirect
,将标准的C#属性公开为Avalonia属性。
使用AvaloniaProperty.Register
注册的StyledProperty
维护了一个优先级列表,其中包含允许样式工作的值和绑定。然而,对于许多属性来说,这是不必要的,比如ItemsControl.Items
——它永远不会被样式化,使用样式化属性的开销是不必要的。
以下是ItemsControl.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); }
}
直接属性是样式化属性的轻量级版本,支持以下功能:
- AvaloniaObject.GetValue
- AvaloniaObject.SetValue(非只读属性)
- PropertyChanged
- Binding(仅具有LocalValue优先级)
- GetObservable
- AddOwner
- Metadata
它们不支持以下功能:
- 验证/强制(尽管可以在属性setter中完成)
- 覆盖默认值。
- 继承的值