My question is similar to this one, I have pretty much the same code setup except I'm using BackgroundWorker
instead of WorkflowRuntime
. (And the answer doesn't appear to work for me)
In the past I have used Application.Current.Shutdown();
in the closing event of MainWindow
, however I was hoping that by properly disposing of this window which I've made a static resource I could perhaps not need that.
The problem is that if I exit via closing MainWindow
after all the background tasks terminate an empty BackgroundDialog
remains open.
public partial class BackgroundDialog : Window
{
private static BackgroundDialog _Dialog = new BackgroundDialog();
private static UIElementCollection TasksView { get { return _Dialog.BackgroundList.Children; } }
public static void Add(BackgroundItem item)
{
if (TasksView.Count == 0)
{
_Dialog.Show();
}
TasksView.Add(item);
}
public static void Remove(BackgroundItem item)
{
TasksView.Remove(item);
if (TasksView.Count == 0)
{
if (_Release)
{
FinishRelease();
}
else
{
_Dialog.Hide();
}
}
}
private static bool _Release = false;
private static void FinishRelease()
{
// FinishRelease may be called from a BackgroundWorker thread finishing
// This results in _Dialog.Close() not behaving as expected
// For more details: https://stackoverflow.com/questions/5659930/wpf-window-not-closing
_Dialog.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_Dialog.Close();
_Dialog = null;
}));
}
public static void Release(EventArgs e)
{
_Release = true;
if (TasksView.Count == 0)
{
FinishRelease();
}
else foreach (BackgroundItem Task in TasksView)
{
Task.Abort();
}
}
}
public partial class BackgroundItem : UserControl
{
public delegate void TaskHandler(BackgroundWorker Worker);
public interface IBackgroundTask
{
bool IsIndeterminate { get; }
int MaxProgress { get; }
string Title { get; }
string Description(int progress);
TaskHandler Exec { get; }
}
private BackgroundWorker Worker;
public BackgroundItem(IBackgroundTask task)
{
InitializeComponent();
Title.Text = task.Title;
Description.Text = task.Description(0);
Progress.Value = 0;
Progress.Minimum = 0;
Progress.Maximum = task.MaxProgress;
Progress.IsIndeterminate = task.IsIndeterminate;
BackgroundDialog.Add(this);
Worker = new BackgroundWorker()
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true,
};
Worker.DoWork += (object sender, DoWorkEventArgs e) =>
{
task.Exec?.Invoke(Worker);
};
Worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) =>
{
BackgroundDialog.Remove(this);
};
Worker.ProgressChanged += (object sender, ProgressChangedEventArgs e) =>
{
Progress.Value = e.ProgressPercentage;
Description.Text = task.Description(e.ProgressPercentage);
};
Worker.RunWorkerAsync();
Stop.Click += (object sender, RoutedEventArgs e) =>
{
Abort();
};
}
public void Abort()
{
Worker.CancelAsync();
Stop.IsEnabled = false;
StopText.Text = "Stopping";
}
}
public partial class MainWindow : Window
{
private class MyTask : BackgroundItem.IBackgroundTask
{
public bool IsIndeterminate => true;
public int MaxProgress => 100;
public string Title => "I'm Counting";
public BackgroundItem.TaskHandler Exec => (BackgroundWorker Worker) =>
{
for (int i = 0; i < 100; ++i)
{
if (Worker.CancellationPending)
{
break;
}
Worker.ReportProgress(i);
Thread.Sleep(500);
}
};
public string Description(int progress)
{
return progress.ToString();
}
}
public MainWindow()
{
InitializeComponent();
Loaded += (object sender, RoutedEventArgs e) => {
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
};
}
protected override void OnClosed(System.EventArgs e)
{
base.OnClosed(e);
BackgroundDialog.Release(e);
}
}