.NET 6 WinUI 3 Community Toolkit DataGrid 编程更新值后UI不刷新如何解决?
我在.NET 6 WinUI 3项目中使用Community Toolkit DataGrid,表格内包含一个按钮。点击按钮会触发事件,调用ViewModel中的方法更新绑定到按钮文本的Selected属性——该属性属于DataGrid选中项SelectedAssignUsersGrid对应的TempUser对象。
需求是:当Selected属性通过代码修改时,DataGrid UI中的按钮文本能同步更新。但目前遇到的问题是:大多数情况下属性变更后UI不更新,表现异常;双击按钮时偶尔会变化,但显示的值和实际属性值也经常不一致。
想知道这是DataGrid的固有问题,还是有可行方法让UI随绑定属性的变化同步更新?
相关代码
XAML DataGrid代码
<controls:DataGrid x:Name="dataGridAssignUsers_Temp" Margin="12" GridLinesVisibility="All" HorizontalGridLinesBrush="PowderBlue" VerticalGridLinesBrush="PowderBlue" BorderBrush="PowderBlue" ScrollViewer.HorizontalScrollBarVisibility="Auto" AutoGenerateColumns="False" SelectedItem="{x:Bind ViewModel.SelectedAssignUsersGrid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{x:Bind ViewModel.AssignUsersGrid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <controls:DataGrid.Columns> <controls:DataGridTemplateColumn Header="Select" Width="75"> <controls:DataGridTemplateColumn.CellTemplate> <DataTemplate x:DataType="viewmodels:ProjectViewModel"> <Grid> <Button Click="DataGridAssignUserSelect_Click" HorizontalContentAlignment="Left" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <TextBlock> <Run FontSize="12" Text="{Binding Selected,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> </TextBlock> </Button> </Grid> </DataTemplate> </controls:DataGridTemplateColumn.CellTemplate> </controls:DataGridTemplateColumn> <controls:DataGridTextColumn IsReadOnly="True" Header="AD User Name" Width="250" Binding="{Binding UserAdazureAduserName}" FontSize="14" /> <controls:DataGridTextColumn IsReadOnly="False" Header="User Initials" Width="250" Binding="{Binding AppUserInitials}" FontSize="14" /> </controls:DataGrid.Columns> </controls:DataGrid>
后端事件代码
private void DataGridAssignUserSelect_Click(object sender, RoutedEventArgs e) { ViewModel.SelectAssignableUser(); dataGridAssignUsers.UpdateLayout(); }
ViewModel代码
public partial class ProjectViewModel : ObservableObject, IDisposable { ... [ObservableProperty] private ObservableCollection<TempUser> _assignUsersGrid; //Project users datagrid source. [ObservableProperty] public TempUser _selectedAssignUsersGrid = new(); //Item selected in the datagrid. public void SelectAssignableUser() { if (SelectedAssignUsersGrid is null) return; SelectedAssignUsersGrid.Selected = !SelectedAssignUsersGrid.Selected; } ... }
绑定的实体类代码
public class TempUser { public bool Selected { get; set; } public string AppUserInitials { get; set; } public string UserAdazureAduserName { get; set; } public TempUser() { } }
问题原因及解决方法
核心原因
TempUser类没有实现属性变更通知机制,UI无法感知Selected属性的变化,这是导致UI不同步的根本原因,和DataGrid本身无关。
解决步骤
1. 让TempUser支持属性变更通知
利用Community Toolkit的ObservableObject简化实现,修改TempUser类:
public partial class TempUser : ObservableObject { [ObservableProperty] private bool _selected; [ObservableProperty] private string _appUserInitials; [ObservableProperty] private string _userAdazureAduserName; public TempUser() { } }
如果不想依赖Community Toolkit,也可以手动实现INotifyPropertyChanged接口:
public class TempUser : INotifyPropertyChanged { private bool _selected; public bool Selected { get => _selected; set { if (_selected != value) { _selected = value; OnPropertyChanged(nameof(Selected)); } } } private string _appUserInitials; public string AppUserInitials { get => _appUserInitials; set { if (_appUserInitials != value) { _appUserInitials = value; OnPropertyChanged(nameof(AppUserInitials)); } } } private string _userAdazureAduserName; public string UserAdazureAduserName { get => _userAdazureAduserName; set { if (_userAdazureAduserName != value) { _userAdazureAduserName = value; OnPropertyChanged(nameof(UserAdazureAduserName)); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public TempUser() { } }
2. 修正DataTemplate的x:DataType
当前DataTemplate的x:DataType="viewmodels:ProjectViewModel"是错误的,DataGrid每行的上下文是TempUser对象,需修改为:
<DataTemplate x:DataType="models:TempUser">
(注意替换models为你实际的命名空间)
3. 优化按钮点击逻辑(可选)
当前实现依赖DataGrid的SelectedItem,若点击按钮时该行未被选中,会导致修改失败。可以直接从按钮的DataContext获取对应实体:
private void DataGridAssignUserSelect_Click(object sender, RoutedEventArgs e) { if (sender is Button btn && btn.DataContext is TempUser user) { user.Selected = !user.Selected; // 无需手动调用UpdateLayout,属性变更通知会自动更新UI // dataGridAssignUsers.UpdateLayout(); } }
内容的提问来源于stack exchange,提问作者E. A. Bagby

