假设有一个枚举
enum SampleEnum
{
Item1,
Item2
}
然后有一个ComboBox
<ComboBox ItemsSource="{Binding SomeItemSource}"
SelectedItem="{Binding
Path=ItemX,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource ResourceKey=SomeConverter}}">
组合框的ViewModel为DataContext
ViewModel : INotifyPropertyChanged, ...
{
...
public SampleEnum ItemX
{
get => model.GetItemX();
set
{
model.SetItemX(value);
RaisePropertyChanged();
}
}
...
}
RaisePropertyChanged([CallerMemberName] string caller = "")
使用属性的名称调用PropertyChanged
。
但是
当我运行我的代码时,打开我的CheckBox
,选择一个项目,我得到以下行为:我的代码进入setter,设置模型的值,引发PropertyChanged,然后调用我的getter,检索值, 但它永远不会达到 ComboBox
。 ComboBox
显示我手动选择的值,而不是访问者返回的值。
E.g。如果您重写get => SampleEnum.Item2
以返回始终相同的值,ComboBox
仍将显示我在UI中选择的值,而不是访问者返回的值,即使我100%确定调用getter,而不是传递给Converter和Convrter的值也返回正确的值。
但是如果从任何其他地方调用RaisePropertyChanged(nameof(ItemX))
,ComboBox
会立即从访问者检索该值并显示它。
简而言之,ComboBox
如果从setter中调用,则会忽略PropertyChanged
,但在任何其他情况下,它都可以正常工作。直接指定属性的名称(而不是依赖于编译器服务)或在setter中连续调用多个RasiePropertyChanged不起作用。
通常,人们应该期望在组合框中选择的值和getter返回的值是相同的,但有时模型可以拒绝提供的值,而是返回默认值。不是最好的行为,但它是可能的。在这种情况下,用户将被误导为实际选择ComboBox
的哪个项目。
我只是想知道ComboBox忽略它的这个访问器有什么特别之处。
答案 0 :(得分:4)
tl; dr:您要做的是数据验证。这是一个已解决的问题:您可以使用IDataErrorInfo
在视图模型中实现验证,或者在ValidationRules的视图中实现验证。其中一个使用 WPF而不是反对它。与WPF合作几乎总是失去主张。
您还可以为ComboBox编写一个禁用无效项的ItemContainerStyle,或者您的viewmodel可以更新ComboBox项集合以排除当前无法选择的任何项。我更喜欢这种方法:而不是“在这里,你可以选择任何这些选项 - BZZZT,LOL,错误的选择!”,只有用可以选择的选项呈现它们似乎更友好。
如果您在做出选择后知道哪些选项有效,那么您几乎肯定也可以事先知道。
如果从setter调用,ComboBox会忽略PropertyChanged,但在任何其他情况下,它都可以正常工作。
这是对的。 ComboBox仍在处理从用户那里获得的更改,并且在您的setter完成后的一段时间内才会完成。 ComboBox将忽略它当前正在更新的属性上的任何PropertyChanged事件。这是设计的。
标准解决方法是以ApplicationIdle
优先级调用BeginInvoke(),并在委托中引发PropertyChanged。在ComboBox完全使用当前选择更改事件完成后,这将产生再次提升PropertyChanged的效果。但这并不是一个视角模特应该关注的事情。
正如你所说,“不是最好的行为”。最好先编写拒绝错误值的验证,而不是像上面那样编写一个奇怪的解决方法。