Был как минимум один такой вопрос, на который был дан ответ: «Воспользуйтесь секундомером». Я бы с радостью сделал это, за исключением того, что Stopwatch блокирует пользовательский интерфейс, поэтому я вынужден использовать Timer. Но я не могу понять, как узнать истекшее время. Я должен отобразить это в пользовательском интерфейсе.

Проблема: подождите X секунд и сделайте что-нибудь, и отобразите прошедшее время.

Таймеры, просто, но не до истечения времени:

Timer timer = new Timer
            {
                Interval = 8000 //8 seconds
            };

timer.Enabled = true;
timer.Elapsed += DoSomething;

Секундомер, не очень приятный код, который блокирует пользовательский интерфейс, но может легко узнать прошедшее время с помощью Elapsed:

            Stopwatch st = new Stopwatch();
            st.Start();
            do
            {
               //wait 8 seconds
            } while (st.ElapsedMilliseconds < 8000);
            DoSomething2();

По сути, обе вещи не будут делать то, что я действительно хочу. И я уверен, что есть способ добиться того, что мне нужно.

-1
PeterParker 11 Окт 2021 в 16:34

2 ответа

Лучший ответ

подождите X секунд и сделайте что-нибудь, и отобразите прошедшее время

Это две разные цели. Таймер подходит для выполнения первого, но можно также использовать что-то вроде await Task.Delay(...), которое использует таймер внутри.

Для второй цели вы можете либо предположить, что таймер действительно истек в правильное время, либо измерить фактическое время самостоятельно с помощью секундомера. Но измерение времени отделено от времени ожидания. Например

var waitTime = TimeSpan.FromSeconds(8)
var startTime = DateTime.Now;
var stopwatch = Stopwatch.StartNew();
await Task.Delay(waitTime);
// Use waitTime for a rough estimation of the passed time
// (DateTime.Now - startTime ) for a measurement accurate to the system clock, often about 16ms
// stopwatch.Elapsed for an accurate measurement to the tick-frequency (usually much better than a microsecond)
0
JonasH 11 Окт 2021 в 14:02

Как ты сказал:

  1. Запустить.
  2. Подождите 8 секунд.
  3. Сделай что-нибудь другое.
  4. Показать прошедшее время.

Этого можно добиться с помощью следующего примера:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private readonly Stopwatch sw = new Stopwatch();

    private void BtnStart_Click(object sender, RoutedEventArgs e)
    {
        Task.Run(() => DoSomething());
    }

    private async Task DoSomething()
    {
        sw.Start();
        await Task.Delay(8000);

        DoSomething2();

        sw.Stop();
        Dispatcher.Invoke(() => 
            lblResult.Content = "Job complete. Elapsed: " + sw.Elapsed.ToString());
        sw.Reset();
    }

    private void DoSomething2()
    {
        // Do it, c'mon
    }
}

Когда нажата кнопка «Пуск» - вы запускаете задание в новом потоке, поэтому оно не повлияет на ваше окно зависанием или зависанием. Вы получите такой вывод:

enter image description here

А если вам нужно увидеть прогресс измерения времени во время выполнения чего-либо, вы можете добавить этот примерный блок сразу после запуска Stopwatch (sw.Start()):

//...
sw.Start();

Task.Run(() => 
{
    Dispatcher.Invoke(() => 
        logTextBlock.Text += "Something started.\n\n");

    while (sw.IsRunning)
    {
        Dispatcher.Invoke(() => 
            logTextBlock.Text += "Something running. Elapsed: " + sw.Elapsed.ToString() + "\n");
        Task.Delay(1000).Wait();
    }

    Dispatcher.Invoke(() => 
        logTextBlock.Text += "\nSomething complete. Elapsed: " + sw.Elapsed.ToString() + "\n");
    
    sw.Reset(); // Put reset from outer scope inside here       
});

await Task.Delay(8000);
// ...

Обратите внимание, что в этом случае sw.Reset() следует поместить в этот блок, а не за его пределами , как в первом примере.

И вы можете получить такой вывод:

enter image description here

0
Auditive 11 Окт 2021 в 14:49