出于可重用性的原因,我通常希望将一个或多个UserControl
封装成一个。在其他框架/库中,这似乎更常见/更难实现。我认为,在大多数使用Xaml的框架中,由于MVVM方法,定义自定义控件并不常见,这可能是没有有用的文档的原因关于这个话题,有很多模糊相似的问题,但我没有找到一个能简明扼要地阐述实现这一目标的理想方法的问题。
背景
这就是我在布拉佐的生活方式。使用Parameter
属性定义属性会导致单向绑定,并在传递的参数更改时更新组件。
[Parameter]
public bool Value { get; set; }
如果需要双向绑定,则必须显式定义相应的回调,并使用@bind-
语法在父级内部配置双向绑定。
[Parameter]
public EventCallback<bool> ValueChanged { get; set; }
private async Task SetValue(bool value)
{
if (Value != value)
{
Value = value;
await ValueChanged.InvokeAsync(value);
}
}
在React中,这非常类似,尽管父级必须显式传递事件回调,因为没有真正的双向绑定。
问题
我不知道如何在Xaml中做到这一点。我认为在自定义控件上定义属性的常用方法是注册的DependencyProperty
。到目前为止,我的方法是:
- 定义
DependencyProperty
- 将属性的值绑定到包装控件的值;
- 如果是只读的,则使用单向绑定
- 如果它在自定义控件中是可编辑的,则将内部控件的值双向绑定到属性
这是一个滑块的示例,在它旁边显示它的离散索引。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Slider x:Name="IndexSlider"
Grid.Column="0"
TickFrequency="1"
Minimum="0"
Value="{x:Bind Value, Mode=TwoWay}"
Maximum="{x:Bind Maximum, Mode=OneWay}"
Foreground="Blue"/>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Right"
Grid.Column="1"
Margin="6,0,0,0"
Text="{x:Bind local:WrappedSlider.DiscretePaginate(IndexSlider.Value, Maximum), Mode=OneWay}"/>
</Grid>
public sealed partial class WrappedSlider : UserControl
{
public static readonly DependencyProperty ValueProperty = (/*...*/);
public double Value
{
get { return (double)GetValue(ValueProperty); }
set
{
SetValue(ValueProperty, value);
}
}
public static readonly DependencyProperty MaximumProperty = (/*...*/);
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set
{
SetValue(MaximumProperty, value);
}
}
public WrappedSlider()
{
InitializeComponent();
}
public static string DiscretePaginate(double currentIndex, double upperBound) => $"{currentIndex}/{upperBound}";
}
虽然这种方法适用于简单的控制(到目前为止的大多数情况下(,但有很多事情让我感到不安
我的问题
- 将控件的值绑定到传递给
DependencyProperty
的包装器(单向或双向(是一个问题,还是会产生任何陷阱?在我看来,真相不再是单一的来源,而是内部控制的价值以及DependencyProperty
的支持字段。是否可以确保对我的控件的实现一无所知的人能够可靠地双向访问我的控件(或者只读控件的单向绑定/不需要反向绑定时( x:Bind
语法是正确的方法吗?还是应该给内部控件一个x:Name
,并通过外部属性的getter/setter设置/获取其值?我注意到孩子们在使用{x:Bind Variable, Mode=OneWay}
和调用后面代码中的setter时偶尔不会更新,而使用MyControlsName.Value =
时会更新。或者我应该使用完全不同的方法INotifyPropertyChanged
在这里可行吗?这篇文章列出了各种优点,但在我看来,它是MVVM体系结构中ViewModel
专用的
我真的可以在这里输入一些信息。这似乎应该是不言自明的,但在广泛使用的MVVM方法之上,在Xaml中实现绑定的无数方法使这对有Blazor背景的人来说非常困难。
能否确保对我的控件的实现一无所知的人能够可靠地双向访问我的控件(或者只读控件的单向绑定/不需要反向绑定时(。
是,当您在属性和控件之间创建数据绑定时。这是一座桥梁,他们可以在这里相互连接,并接受来自另一方的改变。除非手动更改代码中的值,否则它是可靠的。
这里的x:Bind语法是正确的方法吗?还是应该给内部控件一个x:Name并通过外部属性的getter/setter设置/获取其值?
使用x:Bind或Binding语法是实现数据绑定的正确方法。
INotifyPropertyChanged在这里是否可行?
INotifyPropertyChanged
接口用于数据绑定。当您想要使用双向绑定时,您将需要实现此接口。这是一个数据绑定实现。通常,MVVM模式使用数据绑定,因此INotifyPropertyChanged
接口经常用于MVPM方式。