В моей WPF-программе Main() я запускаю трудоемкий метод асинхронно. Когда этот метод запущен, я запускаю дополнительное окно, содержащее ProgressBar, которое я обновляю с помощью IProgress.
Ниже приведен пример моей установки.
ОСНОВНАЯ программа:
public partial class MainWindow : Window
{
private ProgressBarWindow pbwWindow = null;
public MainWindow()
{
InitializeComponent();
}
private void RunMethodAsync(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbwWindow = new ProgressBarWindow("Processing...");
pbwWindow.Owner = this;
pbwWindow.Show();
});
TimeConsumingMethod(progress);
}
private void TimeConsumingMethod(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
// Thread.Sleep() represents actual time consuming work being done.
Thread.Sleep(100);
progress.Report(i);
}
}
private async void btnRun_Click(object sender, RoutedEventArgs e)
{
IProgress<int> progress;
progress = new Progress<int>(i => pbwWindow.SetProgressUpdate(i));
await Task.Run(() => RunMethodAsync(progress));
}
}
Мой ProgressBarWindow, который содержит индикатор выполнения, выглядит так:
public partial class ProgressBarWindow : Window
{
Stopwatch stopwatch = new Stopwatch();
BackgroundWorker worker = new BackgroundWorker();
public string ElapsedTimeString { get; set; }
public ProgressBarWindow(string infoText)
{
InitializeComponent();
SetTimer();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
StartTimer();
}
private void SetTimer()
{
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += (s, e) =>
{
while (!worker.CancellationPending)
{
worker.ReportProgress(0, stopwatch.Elapsed);
Thread.Sleep(1000);
}
};
worker.ProgressChanged += (s, e) =>
{
TimeSpan elapsedTime = (TimeSpan)e.UserState;
ElapsedTimeString = string.Format("{0}:{1}:{2}", elapsedTime.Minutes, elapsedTime.Seconds, elapsedTime.Milliseconds);
};
}
private void StartTimer()
{
stopwatch.Start();
worker.RunWorkerAsync();
}
private void StopTimer()
{
stopwatch.Stop();
worker.CancelAsync();
}
public void SetProgressUpdate(int progress)
{
pbLoad.Value = progress;
if (progress >= 100)
{
StopTimer();
Close();
}
}
}
Я позаимствовал логику StopWatch из этого ответа SO. Затем в моем ProgressBarWindow у меня есть TextBlock, который я использовал Binding следующим образом, как сказано в ответе выше.
<TextBlock Name="tbElapsedTime" Text="{Binding ElapsedTimeString}"/>
Теперь, когда я запускаю программу, метод выполняется, и индикатор выполнения обновляется просто отлично. Однако мой TextBlock, который должен обновляться с истекшим временем, не обновляется.
Чтобы убедиться, что мой таймер работает нормально, я обновил значение TextBlock непосредственно следующим образом вместо Binding, и он работал, как ожидалось, и отображал истекшее время:
worker.ProgressChanged += (s, e) =>
{
TimeSpan elapsedTime = (TimeSpan)e.UserState;
ElapsedTimeString = string.Format("{0}:{1}:{2}", elapsedTime.Minutes, elapsedTime.Seconds, elapsedTime.Milliseconds);
tbElapsedTime.Text = ElapsedTimeString;
};
Итак, я предполагаю, что моя проблема связана с привязкой и, возможно, использованием BackgroundWorker в окнах, которые уже запускаются асинхронно? Как я могу это исправить, чтобы использовать DataBinding?
ElapsedTimeString
. Значение изменится, но ваша привязка не будет обновляться, если вы не уведомите слой пользовательского интерфейса. - person Ginger Ninja   schedule 28.07.2017