我正在开发利用MVVM范例的UWP应用。我的视图包含一个简单的TextBox,其Text
属性绑定到各自的ViewModel属性:
<TextBox Text="{Binding Path=Radius, Mode=TwoWay}"/>
自然地,我已经将ViewModel分配给页面的DataContext
:
public sealed partial class ExamplePage : Page
{
private ExamplePageVM viewModel;
public ExamplePage()
{
this.InitializeComponent();
viewModel = new ExamplePageVM();
DataContext = viewModel;
}
}
在ViewModel中,我执行某种输入验证,即。 e。如果用户在TextBox中插入无效的float值,我想将TextBox重置为默认值(例如零):
class ExamplePageVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private float radius;
public string Radius
{
get => radius.ToString();
set
{
if (radius.ToString() != value)
{
if (!float.TryParse(value, out radius)) radius = 0;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Radius)));
}
}
}
}
更改TextBox中的值会导致按预期方式调用setter。同样,PropertyChanged
事件也相应地被调用。但是,在setter执行完成之后,TextBox仍然包含无效数据,这意味着视图未正确更新。
根据对this帖子的第一条评论,解决此问题的方法是使用<TextBox Text="{x:Bind viewModel.Radius, Mode=TwoWay}"/>
而不是上面显示的Binding
方法。为什么呢?在这种情况下,Binding
和x:Bind
有什么区别?
答案 0 :(得分:2)
绑定到TextBox.Text
是一个非常特殊的情况,因为Microsoft决定最常见的情况是,当控件失去焦点时(而不是每个输入文本都发生更改),应该更新绑定。这允许两件事:
在没有公开可用的UWP源代码的情况下,MS开发人员可能会为您提供更可靠的见解,但即使通过DependencyObject.RegisterPropertyChangedCallback
直接跟踪EditBox.TextProperty
,也可以将更改与绑定源进行比较让您期望,TextBox
实际上不是在通常的情况下直接绑定到依赖项属性更改,而是实际上还有一种中间人实现,它处理TextProperty
更新如何以及何时影响和存在受DataContext
或受{Binding}
或{x:Bind}
约束的基础类属性的影响。
请注意,{x:Bind}
和{Binding}
是非常不同的机制,尤其是第一种是编译时,第二种是运行时,这意味着它们在内部需要不同的实现,并且取决于框架开发人员,以确保他们表现出相同的行为。
现在,在测试场景中,您正在尝试验证并可能更改绑定数据源中的属性值,期望TextBox
将显示所需的值,这与{x:Bind}
相同,但是不能与{Binding}
一起使用。
显然,您发现了{x:Bind}
和{Binding}
实现的行为有所不同的情况。我进行了相同的测试,并完全证实了您的发现。
答案 1 :(得分:1)
您可能需要自行设置UpdateTrigger,因为TextBox通常会在调用焦点丢失时更新源。
您可以更改行为UpdateSourceTrigger = PropertyChanged。
<TextBox Text="{x:Bind AnswerText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding AnswerText, UpdateSourceTrigger=PropertyChanged}"/>
如果此方法不起作用,则可能要防止通过keydown事件输入与数字不同的数字。您可以将其外包给用户控件以重复使用。
希望这会有所帮助。