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

WPF C#桌面应用中Timer的启动、停止、恢复实现及状态JSON序列化方案咨询

Alright, let's get your WPF DispatcherTimer up and running with proper start/stop/resume controls, status feedback, and JSON state tracking. Here's a step-by-step solution tailored to your code:


1. Update Your XAML for Status Bar and Button Events

First, let's clean up the layout and add the status bar + click handlers for your buttons. This will make the UI more intuitive and hook up the frontend to our backend logic:

<Window x:Class="PingApplication.MainWindow"
        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:PingApplication"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="750">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/> <!-- For timer display -->
            <RowDefinition Height="Auto"/> <!-- For control buttons -->
            <RowDefinition Height="28"/> <!-- For status bar -->
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- Timer Display Label -->
        <Label Name="lblTime" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center"
               Content="00:00:00"/>

        <!-- Control Buttons Panel -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
            <Button MinWidth="80" Margin="3" Content="Start" Click="Start_Click"/>
            <Button MinWidth="80" Margin="3" Content="Stop" Click="Stop_Click"/>
            <Button MinWidth="80" Margin="3" Content="Resume" Click="Resume_Click"/>
        </StackPanel>

        <!-- Status Bar for User Feedback -->
        <StatusBar Grid.Row="2">
            <TextBlock Name="txtStatus" Text="Timer is stopped"/>
        </StatusBar>
    </Grid>
</Window>

2. Implement the C# Backend Logic

Now let's add the timer control logic, status updates, and JSON serialization. We'll use class-level variables to track state, and add helper methods to handle state transitions:

using System;
using System.Windows;
using System.Windows.Dispatcher;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace PingApplication
{
    // Serializable enum to track timer state
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum TimerStatus
    {
        Stopped,
        Running,
        Paused
    }

    public partial class MainWindow : Window
    {
        // Class-level timer and state variables
        private DispatcherTimer _timer;
        private TimeSpan _elapsedTime;
        private DateTime? _pauseStartTime;
        private TimerStatus _currentStatus;

        public MainWindow()
        {
            InitializeComponent();
            InitializeTimer();
            UpdateStatus(TimerStatus.Stopped);
        }

        private void InitializeTimer()
        {
            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromSeconds(1);
            _timer.Tick += Timer_Tick;
            _elapsedTime = TimeSpan.Zero;
            _currentStatus = TimerStatus.Stopped;
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            // Update elapsed timer display (replace with DateTime.Now if you want system time)
            _elapsedTime += _timer.Interval;
            lblTime.Content = _elapsedTime.ToString(@"hh\:mm\:ss");
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            // Reset and start a fresh timer session
            _elapsedTime = TimeSpan.Zero;
            lblTime.Content = _elapsedTime.ToString(@"hh\:mm\:ss");
            _timer.Start();
            UpdateStatus(TimerStatus.Running);
        }

        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            // Stop and fully reset the timer
            _timer.Stop();
            _elapsedTime = TimeSpan.Zero;
            lblTime.Content = _elapsedTime.ToString(@"hh\:mm\:ss");
            _pauseStartTime = null;
            UpdateStatus(TimerStatus.Stopped);
        }

        private void Resume_Click(object sender, RoutedEventArgs e)
        {
            if (_currentStatus == TimerStatus.Paused)
            {
                // Calculate time spent paused and add to elapsed time
                if (_pauseStartTime.HasValue)
                {
                    var pauseDuration = DateTime.Now - _pauseStartTime.Value;
                    _elapsedTime += pauseDuration;
                    _pauseStartTime = null;
                }
                _timer.Start();
                UpdateStatus(TimerStatus.Running);
            }
            else if (_currentStatus == TimerStatus.Stopped)
            {
                // Give user feedback if they try to resume a stopped timer
                MessageBox.Show("Timer is stopped. Click Start to begin a new session.");
            }
        }

        private void UpdateStatus(TimerStatus status)
        {
            _currentStatus = status;
            // Update status bar text based on current state
            switch (status)
            {
                case TimerStatus.Running:
                    txtStatus.Text = "Timer is running";
                    break;
                case TimerStatus.Stopped:
                    txtStatus.Text = "Timer is stopped";
                    break;
                case TimerStatus.Paused:
                    txtStatus.Text = "Timer is paused";
                    break;
            }
        }

        // Example: Save timer state to JSON when window closes
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            SaveTimerState();
            base.OnClosing(e);
        }

        // Serialize timer state to JSON
        private void SaveTimerState()
        {
            var timerState = new TimerState
            {
                Status = _currentStatus,
                ElapsedTimeTotalSeconds = _elapsedTime.TotalSeconds,
                PauseStartTime = _pauseStartTime
            };

            string json = JsonSerializer.Serialize(timerState, new JsonSerializerOptions { WriteIndented = true });
            // Uncomment below to save to a file
            // System.IO.File.WriteAllText("TimerState.json", json);
            MessageBox.Show($"Timer state saved:\n{json}");
        }

        // Deserialize timer state from JSON (example)
        private void LoadTimerState()
        {
            // Uncomment below to load from a file
            // if (System.IO.File.Exists("TimerState.json"))
            // {
            //     string json = System.IO.File.ReadAllText("TimerState.json");
            //     var timerState = JsonSerializer.Deserialize<TimerState>(json);
            //     if (timerState != null)
            //     {
            //         _currentStatus = timerState.Status;
            //         _elapsedTime = TimeSpan.FromSeconds(timerState.ElapsedTimeTotalSeconds);
            //         _pauseStartTime = timerState.PauseStartTime;
            //         lblTime.Content = _elapsedTime.ToString(@"hh\:mm\:ss");
            //         UpdateStatus(_currentStatus);
            //         if (_currentStatus == TimerStatus.Running)
            //         {
            //             _timer.Start();
            //         }
            //     }
            // }
        }
    }

    // Serializable class to hold timer state data
    public class TimerState
    {
        public TimerStatus Status { get; set; }
        public double ElapsedTimeTotalSeconds { get; set; } // Store as seconds for easy serialization
        public DateTime? PauseStartTime { get; set; }
    }
}

3. Key Details to Note

  • Timer Continuity: When pausing, we record the exact time the pause started. Resuming calculates the duration of the pause and adds it to the elapsed time, so the timer picks up right where it left off.
  • Status Feedback: The status bar updates automatically whenever the timer changes state, so users always know what the timer is doing.
  • JSON Serialization: The TimerState class is designed to be easily serialized/deserialized with .NET's built-in System.Text.Json library. We store elapsed time as total seconds (since TimeSpan can be tricky to serialize directly) and include all necessary state data to restore the timer later.
  • Flexibility: If you want to display the current system time instead of an elapsed timer, just replace the logic in Timer_Tick with your original code: lblTime.Content = DateTime.Now.ToLongTimeString(); — the start/stop/resume controls will still function the same way.

内容的提问来源于stack exchange,提问作者Juju22 Nimza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 14:29:08