C#/WPF视图无法更新:已尝试多种方案仍未解决,求技术帮助
问题:ObservableCollection变更无法同步到UI,NextPage方法仅构造函数调用时生效
我正在开发一个包含6个问答页面的简易启动器,用户可通过侧边按钮或对应单选按钮切换页面。目前模板绑定正常,按钮能正确调用目标方法,但ObservableCollection的变更无法反映到UI上。我是.NET新手,肯定遗漏了某些内容,恳请帮助。
问题出在**PageBarViewModel.NextPage()**方法:该方法能被正常调用执行,但只有在构造函数中调用时UI才会显示变化。
Launcher.xaml
<Window x:Class="automeas_ui.Launcher" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:automeas_ui" xmlns:view="clr-namespace:automeas_ui.MWM.View" xmlns:viewm="clr-namespace:automeas_ui.MWM.ViewModel" mc:Ignorable="d" Title="MainWindow" Height="600" Width="920" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Background="Transparent" AllowsTransparency="True"> <Window.DataContext> <viewm:PageBarViewModel/> </Window.DataContext> <Border Background="#272537" CornerRadius="10"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.25*"/> <ColumnDefinition Width="3*"/> <ColumnDefinition Width="0.25*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="0.45*"/> <RowDefinition Height="3*"/> <RowDefinition Height="0.25*"/> </Grid.RowDefinitions> <Button Grid.Row="0" Grid.Column="2" Width="50" Height="50" Content="❌" FontSize="32" Style="{StaticResource FOC_SquareButtonTheme}" Click="Button_Clicked"/> <Button Grid.Row="1" Grid.Column="2" Content="▷" Foreground="#323232" Background="Transparent" BorderThickness="0" FontSize="72" Command="{Binding Path=NpCommad}"/> <Button Grid.Row="1" Grid.Column="0" Content="◁" Foreground="#323232" Background="Transparent" BorderThickness="0" FontSize="72"/> <Label Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="44" FontFamily="Verdana" Content="Some text" Foreground="White"/> <view:PageBarView Grid.Column="1" Grid.Row="2"/> </Grid> </Border> </Window>
PageBarView.xaml
<UserControl x:Class="automeas_ui.MWM.View.PageBarView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:automeas_ui.MWM.View" xmlns:model="clr-namespace:automeas_ui.MWM.ViewModel" xmlns:viewm="clr-namespace:automeas_ui.MWM.ViewModel" d:DataContext="{d:DesignInstance Type=model:PageBarViewModel}" mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="800"> <UserControl.DataContext> <viewm:PageBarViewModel/> </UserControl.DataContext> <Border CornerRadius="20" Background="#323232" Margin="150,0,150,0"> <ItemsControl ItemsSource="{Binding Pages}" Grid.Column="1" Grid.Row="1"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" Margin="0,0,10,0" HorizontalAlignment="Center"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Viewbox Height="40" Margin="10,0,0,0"> <RadioButton GroupName="pgs" IsChecked="{Binding IsFocused, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> </Viewbox> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Border> </UserControl>
PageBarViewModel.xaml.cs
namespace automeas_ui.MWM.ViewModel { public class ViewedPage: INotifyPropertyChanged { private bool m_IsFocused; public bool IsFocused { get { return m_IsFocused; } set { m_IsFocused = value; OnPropertyChanged("IsFocused"); } } public ViewedPage(bool focused = false) { IsFocused = focused; } private void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler? PropertyChanged; } public class PageBarViewModel { // internal interface const int NumberOfPages = 5; // eof private TrulyObservableCollection<ViewedPage> _Pages; public TrulyObservableCollection<ViewedPage> Pages { get { return _Pages; } set { if (_Pages == value) return; _Pages = value; NotifyPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public PageBarViewModel() { Pages = new TrulyObservableCollection<ViewedPage>(); Pages.Add(new ViewedPage(true)); for (int i = 1; i < NumberOfPages; i++) { Pages.Add(new ViewedPage()); } } private ICommand? _npCommand; public ICommand NpCommad { get { if (_npCommand == null) { _npCommand = new JSRelayCommand( param => this.NextPage(), param => this.CanSave() ); } return _npCommand; } } private bool CanSave() { // Verify command can be executed here return true; } private void NextPage() { int pgn = 1; foreach (var item in Pages) { if (item.IsFocused) { item.IsFocused = false; break; } pgn++; } Pages[pgn].IsFocused = true; CollectionViewSource.GetDefaultView(Pages).Refresh(); } } }
已尝试的修复方案
- 使用ObservableCollection并为T实现INotifyChanged
- 通过增删集合元素强制更新
- 调用
CollectionViewSource.GetDefaultView(Pages).Refresh(); - 实现TrulyObservableCollection
- 取消单选按钮分组
修复方案
1. 解决DataContext实例不一致问题
你的Launcher窗口和PageBarView用户控件各自创建了独立的PageBarViewModel实例,导致按钮绑定的ViewModel与页面显示的ViewModel不是同一个对象。
修改PageBarView.xaml,移除内部的UserControl.DataContext定义,让它继承父窗口的DataContext:
<!-- 移除以下代码块 --> <!-- <UserControl.DataContext> <viewm:PageBarViewModel/> </UserControl.DataContext> -->
2. 修复NextPage方法的逻辑错误与越界风险
当前NextPage方法在选中最后一页时会触发索引越界异常,同时优化逻辑:
private void NextPage() { var currentIndex = Pages.ToList().FindIndex(p => p.IsFocused); // 没有选中项或已经是最后一页时直接返回 if (currentIndex == -1 || currentIndex >= Pages.Count - 1) return; // 取消当前页面选中状态 Pages[currentIndex].IsFocused = false; // 选中下一页 Pages[currentIndex + 1].IsFocused = true; }
3. 优化INotifyPropertyChanged实现
使用nameof避免硬编码属性名称,同时简化OnPropertyChanged方法:
public class ViewedPage: INotifyPropertyChanged { private bool m_IsFocused; public bool IsFocused { get => m_IsFocused; set { if (m_IsFocused == value) return; m_IsFocused = value; OnPropertyChanged(nameof(IsFocused)); } } public ViewedPage(bool focused = false) { IsFocused = focused; } private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler? PropertyChanged; }
4. 移除不必要的CollectionView刷新
当集合中的项实现INotifyPropertyChanged后,修改项的属性会自动通知UI更新,可删除CollectionViewSource.GetDefaultView(Pages).Refresh();这行代码。
内容的提问来源于stack exchange,提问作者Thops
相关产品推荐
相关产品推荐

