.NET WinUI3中PropertyChanged触发后调用属性Setter是否正常?
关于WinUI3中ListView SelectedItem双向绑定后Setter被重复调用的疑问
我正在学习.NET和WinUI3,发现修改ListView的SelectedItem并触发PropertyChanged时,该属性的Setter会被再次调用。想请教这是否属于正常行为,还是我操作有误?
相关代码
MainViewModel类
public class MainViewModel : BindableBase { #region Fields private MediaItem selectedMediaItem; public MediaItem SelectedMediaItem { get { Debug.WriteLine("This is SelectedMediaItem's getter."); return selectedMediaItem; } set { Debug.WriteLine("Calling SetProperty from SelectedMediaItem setter"); SetProperty(ref selectedMediaItem, value); Debug.WriteLine("Done calling SetProperty"); } } }
BindableBase类
public class BindableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { Debug.WriteLine($"Invoking PropertyChanged from OnPropertyChanged, property being changed is: {propertyName}"); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); Debug.WriteLine("Done Invoking PropertyChanged"); } protected bool SetProperty<T>(ref T originalValue, T newValue, [CallerMemberName] string propertyName = null) { if (Equals(originalValue, newValue)) { Debug.WriteLine($"Values are the same, property name is {propertyName}, original value is {(originalValue as MyMediaCollection.Model.MediaItem)?.Name}, new value is {(newValue as MyMediaCollection.Model.MediaItem)?.Name}"); return false; } Debug.WriteLine($"Values are not the same(good), property name is {propertyName}, original value is {(originalValue as MyMediaCollection.Model.MediaItem)?.Name}, new value is {(newValue as MyMediaCollection.Model.MediaItem)?.Name}"); originalValue = newValue; Debug.WriteLine("Calling OnPropertyChanged from SetProperty"); OnPropertyChanged(propertyName); return true; } }
XAML代码
<ListView Grid.Row="1" ItemsSource="{x:Bind ViewModel.Items}" SelectedItem="{x:Bind ViewModel.SelectedMediaItem, Mode=TwoWay}" Background="LightGoldenrodYellow"> <ListView.HeaderTemplate> <DataTemplate> <Grid ColumnDefinitions="100,*"> <Border BorderBrush="BlueViolet" BorderThickness="0,0,0,1"> <TextBlock Text="Medium" Margin="5,0,0,0" FontWeight="Bold"/> </Border> <Border Grid.Column="1" BorderBrush="BlueViolet" BorderThickness="0,0,0,1"> <TextBlock Text="Title" Margin="5,0,0,0" FontWeight="Bold"/> </Border> </Grid> </DataTemplate> </ListView.HeaderTemplate> <ListView.ItemTemplate> <DataTemplate x:DataType="model:MediaItem"> <Grid ColumnDefinitions="100, *"> <TextBlock Text="{x:Bind Path=MediumInfo.Name}"/> <TextBlock Grid.Column="1" Text="{x:Bind Path=Name}"/> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>
调试输出
This is SelectedMediaItem's getter. Calling SetProperty from SelectedMediaItem setter Values are not the same(good), property name is SelectedMediaItem, original value is , new value is The Mummy Calling OnPropertyChanged from SetProperty Invoking PropertyChanged from OnPropertyChanged, property being changed is: SelectedMediaItem This is SelectedMediaItem's getter. Calling SetProperty from SelectedMediaItem setter Values are the same, property name is SelectedMediaItem, original value is The Mummy, new value is The Mummy Done calling SetProperty Done Invoking PropertyChanged Done calling SetProperty
可以看到,在"Invoking..."和"Done Invoking..."之间,SelectedMediaItem的Setter被再次调用,想确认该现象是否正常,或是我存在理解误区。
解答
这是正常行为,属于WinUI3双向绑定机制的默认同步流程,你的代码操作没有错误。
原因解释:
- 当你通过UI选择ListView项时,首先会触发ViewModel中
SelectedMediaItem的Setter,更新属性值并调用PropertyChanged通知UI。 - 收到
PropertyChanged通知后,ListView会同步自身选中状态,同时为了确保ViewModel和UI的状态一致,会将当前选中的项回传给ViewModel的SelectedMediaItem属性,导致Setter再次被调用。 - 你的
SetProperty实现已经通过Equals判断值是否相同,当值一致时直接返回false,不会重复触发PropertyChanged,也不会造成无限循环,这是正确的处理方式。
这种重复调用是框架用来保证双向绑定一致性的常规逻辑,不会影响功能,无需额外修改代码。
内容的提问来源于stack exchange,提问作者Andrew Karpinski
相关产品推荐
相关产品推荐

