代码之家  ›  专栏  ›  技术社区  ›  Barta

WPF MVVM不更新视图

  •  0
  • Barta  · 技术社区  · 10 月前

    我正在开发一个需要秒表的应用程序。我想用文本块显示经过的时间。我的问题是,视图没有更新,但我可以使用ICommands。

    我在这里分配 DataContext

    public partial class ProgressPage : Grid
    {
        public ProgressPage(ProjectInfo projectInfo)
        {
            DataContext = new ProgressViewModel(projectInfo);
            InitializeComponent();
        }
    }
    

    这是我的 ViewModel

    public class ProgressViewModel : INotifyPropertyChanged
    {
        private ProjectInfo projectInfo;
        private ICommand _start;
        private DispatcherTimer timer = new()
        {
            Interval = TimeSpan.FromSeconds(1)
        };
    
        public event PropertyChangedEventHandler? PropertyChanged;
    
        public ProjectInfo ProjectInfo
        {
            get { return projectInfo; }
            set
            {
                projectInfo = value;
                NotifyPropertyChanged();
            }
        }
    
        private string elapsedTimeString;
    
        public string ElapsedTimeString
        {
            get { return elapsedTimeString; }
            set
            {
                elapsedTimeString = value;
                NotifyPropertyChanged();
            }
        }
    
        public ICommand StartCommand
        {
            get
            {
                _start ??= new RelayCommand(
                        p => true,
                        p => Start());
                return _start;
            }
        }
    
        private void Start()
        {
            ProjectInfo.CurrentStartedTime = DateTime.Now;
            timer.Start();
        }
    
        public ProgressViewModel(ProjectInfo projectInfo)
        {
            ProjectInfo = projectInfo;
            timer.Tick += Tick;
        }
    
        private void Tick(object? sender, EventArgs e)
        {
            ElapsedTimeString = (DateTime.Now - ProjectInfo.CurrentStartedTime).ToString(@"hh\:mm\:ss");
        }
    
        private void NotifyPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(name));
        }
    }
    

    这是我的 XAML

    <TextBlock Text="{Binding ElapsedTimeString}"
               Style="{StaticResource ProgressText}" />
    

    这是一张捕捉到断点的图片 break point caught

    命令正在工作,计时器正在工作 NotifyPropertyChanged 方法也在工作(name参数也正确),但视图没有更新。

    我该怎么解决这个问题?

    3 回复  |  直到 10 月前
        1
  •  1
  •   Sheriff    10 月前

    尝试改变” 无效的 “与 在里面 PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(name)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    当我更改它并调用start方法时,文本块Text被更新了。

        2
  •  0
  •   ezechiel    10 月前

    也许试着添加

    <TextBlock Text="{Binding ElapsedTimeString, UpdateSourceTrigger=PropertyChanged}"
               Style="{StaticResource ProgressText}" />
    
        3
  •  0
  •   EldHasp    10 月前

    XAML不正确或没有完全反映逻辑。 您在开头指定了数据上下文分配片段 DataContext = new ProgressViewModel(projectInfo); ,但在XAML中,您可以从参考资料中获取源代码。这两种方式有点矛盾。

    我还怀疑您没有完全正确地设置将TimeSpan转换为字符串的格式。

    看看我的实现示例。它基于以下课程: An example of my implementation of base classes (.Net 8): BaseInpc, RelayCommand, RelayCommandAsync, RelayCommand, RelayCommandAsync.

    using Simplified;
    
    namespace SOTopics2025.SO.Barta.question79330621
    {
        public class Stopwatch : BaseInpc
        {
            public TimeSpan ElapsedTime { get; private set; }
            public DateTime StartTime { get; private set; }
    
            public bool IsEnabled { get; private set; }
            private readonly Timer timer;
    
            public Stopwatch()
            {
                timer = new Timer(OnTick);
                StartCommand = new RelayCommand
                (
                    () =>
                    {
                        if (IsEnabled)
                            return;
                        StartTime = DateTime.Now;
                        IsEnabled = true;
                        ElapsedTime = TimeSpan.Zero;
    
                        RaisePropertyChanged(string.Empty);
                        StartCommand!.RaiseCanExecuteChanged();
                        StopCommand!.RaiseCanExecuteChanged();
    
                        timer.Change(10, 10);
                    },
                    () => !IsEnabled
                );
    
                StopCommand = new RelayCommand
                (
                    () =>
                    {
                        if (IsEnabled)
                        {
                            IsEnabled = false;
    
                            RaisePropertyChanged(string.Empty);
                            StartCommand.RaiseCanExecuteChanged();
                            StopCommand!.RaiseCanExecuteChanged();
    
                            timer.Change(Timeout.Infinite, Timeout.Infinite);
                        }
                    },
                    () => IsEnabled
                );
            }
    
            private void OnTick(object? state)
            {
                ElapsedTime = DateTime.Now - StartTime;
                RaisePropertyChanged(nameof(ElapsedTime));
            }
    
            public RelayCommand StartCommand { get; }
            public RelayCommand StopCommand { get; }
        }
    }
    
    <Window x:Class="SOTopics2025.SO.Barta.question79330621.StopwatchTestWindow"
            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:SOTopics2025.SO.Barta.question79330621"
            mc:Ignorable="d"
            Title="StopwatchTestWindow" Height="250" Width="400">
        <Window.DataContext>
            <local:Stopwatch/>
        </Window.DataContext>
        <UniformGrid Columns="2">
            <TextBlock Text="{Binding StartTime}" VerticalAlignment="Center" HorizontalAlignment="Center">
                <TextBlock.Style>
                    <Style TargetType="TextBlock">
                        <Setter Property="Foreground" Value="Red"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsEnabled}" Value="True">
                                <Setter Property="Foreground" Value="Green"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
            <TextBlock Text="{Binding ElapsedTime, StringFormat=hh\\:mm\\:ss\\.ff}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            <Button Content="Start" Padding="15 5" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding StartCommand, Mode=OneWay}"/>
            <Button Content="Stop" Padding="15 5" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding StopCommand, Mode=OneWay}"/>
        </UniformGrid>
    </Window>
    

    enter image description here