temporizador regresiva cuenta cronometro consola c# wpf timer countdown

c# - regresiva - ¿Cómo hacer un temporizador de cuenta atrás wpf?



temporizador c# consola (2)

Puede usar la clase DispatcherTimer ( msdn ).

Duración del tiempo que puede mantener en la estructura TimeSpan ( msdn ).

Si desea formatear TimeSpan en hh:mm:ss , debe invocar el método ToString con el argumento "c" ( msdn ).

Ejemplo:

XAML:

<Window x:Class="CountdownTimer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <TextBlock Name="tbTime" /> </Grid> </Window>

Código detrás:

using System; using System.Windows; using System.Windows.Threading; namespace CountdownTimer { public partial class MainWindow : Window { DispatcherTimer _timer; TimeSpan _time; public MainWindow() { InitializeComponent(); _time = TimeSpan.FromSeconds(10); _timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Normal, delegate { tbTime.Text = _time.ToString("c"); if (_time == TimeSpan.Zero) _timer.Stop(); _time = _time.Add(TimeSpan.FromSeconds(-1)); }, Application.Current.Dispatcher); _timer.Start(); } } }

Quiero crear el temporizador de cuenta atrás de wpf que muestre el resultado como hh: mm: ss en el cuadro de texto, agradecería cualquier ayuda.


No hay nada de malo en utilizar DispatcherTimer para este fin. Sin embargo, en mi humilde opinión, el nuevo paradigma async / async basado en TPL async código que es más fácil de escribir y leer. También es mejor usar siempre buenas prácticas MVVM para programas WPF, en lugar de establecer valores de elemento UI directamente desde código subyacente.

Aquí hay un ejemplo de un programa que implementa un temporizador de cuenta atrás como se describe en la pregunta, pero utilizando estas prácticas más modernas ...

El modelo de vista es, por supuesto, donde reside la mayor parte del código interesante, e incluso allí lo principal es el método simple _StartCountdown() , que implementa la cuenta atrás real:

ViewModel.cs:

class ViewModel { private async void _StartCountdown() { Running = true; // NOTE: UTC times used internally to ensure proper operation // across Daylight Saving Time changes. An IValueConverter can // be used to present the user a local time. // NOTE: RemainingTime is the raw data. It may be desirable to // use an IValueConverter to always round up to the nearest integer // value for whatever is the least-significant component displayed // (e.g. minutes, seconds, milliseconds), so that the displayed // value doesn''t reach the zero value until the timer has completed. DateTime startTime = DateTime.UtcNow, endTime = startTime + Duration; TimeSpan remainingTime, interval = TimeSpan.FromMilliseconds(100); StartTime = startTime; remainingTime = endTime - startTime; while (remainingTime > TimeSpan.Zero) { RemainingTime = remainingTime; if (RemainingTime < interval) { interval = RemainingTime; } // NOTE: arbitrary update rate of 100 ms (initialized above). This // should be a value at least somewhat less than the minimum precision // displayed (e.g. here it''s 1/10th the displayed precision of one // second), to avoid potentially distracting/annoying "stutters" in // the countdown. await Task.Delay(interval); remainingTime = endTime - DateTime.UtcNow; } RemainingTime = TimeSpan.Zero; StartTime = null; Running = false; } private TimeSpan _duration; public TimeSpan Duration { get { return _duration; } set { _UpdateField(ref _duration, value); } } private DateTime? _startTime; public DateTime? StartTime { get { return _startTime; } private set { _UpdateField(ref _startTime, value); } } private TimeSpan _remainingTime; public TimeSpan RemainingTime { get { return _remainingTime; } private set { _UpdateField(ref _remainingTime, value); } } private bool _running; public bool Running { get { return _running; } private set { _UpdateField(ref _running, value, _OnRunningChanged); } } private void _OnRunningChanged(bool obj) { _startCountdownCommand.RaiseCanExecuteChanged(); } private readonly DelegateCommand _startCountdownCommand; public ICommand StartCountdownCommand { get { return _startCountdownCommand; } } public ViewModel() { _startCountdownCommand = new DelegateCommand(_StartCountdown, () => !Running); } public event PropertyChangedEventHandler PropertyChanged; private void _UpdateField<T>(ref T field, T newValue, Action<T> onChangedCallback = null, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, newValue)) { return; } T oldValue = field; field = newValue; onChangedCallback?.Invoke(oldValue); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }

Como se señaló en los comentarios, lo anterior funcionará como está, pero si desea resultados específicos, es útil tener implementaciones de IValueConverter para ajustar la salida para satisfacer las necesidades específicas del usuario. Aquí hay algunos ejemplos de ellos:

UtcToLocalConverter.cs:

class UtcToLocalConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return null; if (value is DateTime) { DateTime dateTime = (DateTime)value; return dateTime.ToLocalTime(); } return Binding.DoNothing; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return null; if (value is DateTime) { DateTime dateTime = (DateTime)value; return dateTime.ToUniversalTime(); } return Binding.DoNothing; } }

TimeSpanRoundUpConverter.cs:

class TimeSpanRoundUpConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (!(value is TimeSpan && parameter is TimeSpan)) { return Binding.DoNothing; } return RoundUpTimeSpan((TimeSpan)value, (TimeSpan)parameter); } private static TimeSpan RoundUpTimeSpan(TimeSpan value, TimeSpan roundTo) { if (value < TimeSpan.Zero) return RoundUpTimeSpan(-value, roundTo); double quantization = roundTo.TotalMilliseconds, input = value.TotalMilliseconds; double normalized = input / quantization; int wholeMultiple = (int)normalized; double fraction = normalized - wholeMultiple; return TimeSpan.FromMilliseconds((fraction == 0 ? wholeMultiple : wholeMultiple + 1) * quantization); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }

Y, por supuesto, algunos XAML para definir la UI (donde ninguno de los elementos de la interfaz de usuario tiene nombres, ni el código subyacente necesita acceder a ninguno de ellos explícitamente):

MainWindow.xaml:

<Window x:Class="TestSO16748371CountdownTimer.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:l="clr-namespace:TestSO16748371CountdownTimer" xmlns:s="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <l:ViewModel/> </Window.DataContext> <Window.Resources> <l:UtcToLocalConverter x:Key="utcToLocalConverter1"/> <l:TimeSpanRoundUpConverter x:Key="timeSpanRoundUpConverter1"/> <s:TimeSpan x:Key="timeSpanRoundTo1">00:00:01</s:TimeSpan> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <TextBlock Text="Duration: "/> <TextBox Text="{Binding Duration}" Grid.Column="1"/> <TextBlock Text="Start time:" Grid.Row="1"/> <TextBlock Text="{Binding StartTime, Converter={StaticResource utcToLocalConverter1}}" Grid.Row="1" Grid.Column="1"/> <TextBlock Text="Remaining time:" Grid.Row="2"/> <TextBlock Text="{Binding RemainingTime, StringFormat=hh//:mm//:ss, Converter={StaticResource timeSpanRoundUpConverter1}, ConverterParameter={StaticResource timeSpanRoundTo1}}" Grid.Row="2" Grid.Column="1"/> <Button Content="Start Countdown" Command="{Binding StartCountdownCommand}" Grid.Row="3" VerticalAlignment="Top"/> </Grid> </Window>