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
TimerStateclass is designed to be easily serialized/deserialized with .NET's built-inSystem.Text.Jsonlibrary. We store elapsed time as total seconds (sinceTimeSpancan 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_Tickwith your original code:lblTime.Content = DateTime.Now.ToLongTimeString();— the start/stop/resume controls will still function the same way.
内容的提问来源于stack exchange,提问作者Juju22 Nimza
相关产品推荐
相关产品推荐

