You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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="&#9655;"
                Foreground="#323232"
                Background="Transparent"
                BorderThickness="0"
                FontSize="72" Command="{Binding Path=NpCommad}"/>
        <Button Grid.Row="1"
                Grid.Column="0"
                Content="&#9665;"
                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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 14:03:13