2

I have an application that parses a large XML file and builds WPF UI controls based on that content. This task usually takes about 15 - 30 seconds. In order to inform the user about a running task, I display a simple intermediate progress dialog window, like:

Thread progressDialogThread = new Thread(() =>
{
    Window window = new Window
    {
        Content = new ProgressDialog(),
        Height = 100,
        Width = 150,
        WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen
    };
    window.ShowDialog();
});

progressDialogThread.SetApartmentState(ApartmentState.STA);
progressDialogThread.IsBackground = true;
progressDialogThread.Start();

buildUI();

progressDialogThread.Abort();

This works, but I sometimes get a ThreadAbortException on progressDialogThread.Start(), when the XML should be parsed once again.

Does anyone know a better approach to "close" the progress dialog?

Since the controls have to be built on the main UI thread, I can not use the backgroundworker...

The progress dialog itself in XAML looks like:

<UserControl x:Class="MyDialog.ProgressDialog"
        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:MyDialog"
        mc:Ignorable="d"
        Background="{DynamicResource MaterialDesignPaper}"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}"
        Height="100" Width="150">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Label HorizontalAlignment="Center">Please wait</Label>
        <ProgressBar
          Style="{StaticResource MaterialDesignCircularProgressBar}"
          Value="0"
          IsIndeterminate="True" Width="40" Height="41" Margin="55,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </StackPanel>
</UserControl>
GSerg
  • 76,472
  • 17
  • 159
  • 346
PythonNoob
  • 914
  • 1
  • 7
  • 15
  • Turn your `buildUI` into an `async` method? Have the code inside `progressDialogThread` store the reference to `window` in a shared place from where you can call `.Close` on it? – GSerg Jun 01 '19 at 11:24
  • This is because you are calling `Abort()`, here is an example on how to use `Abort()` properly: [link](https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadabortexception?view=netframework-4.8) – muaz Jun 01 '19 at 11:44

2 Answers2

5

Do not abort a thread, that should be avoided.

If you want to close a Window, you call Close on it.

Oh, it is created by another thread? Alright, that is why the Window has a Dispatcher, you use BeginInvoke and it will run the callback on that thread.

In fact, there is a chance you do not need to create the Window in another thread※. You can create it in the main thread and have a background thread interact with it via BeginInvoke.

※: If you have the main thread busy and want the Window in another thread so the main thread does not block it (or viceversa), you probably should be using a BackgroundWorker (as Caius Jard suggests) instead of having a UI thread busy.

Theraot
  • 31,890
  • 5
  • 57
  • 86
2

Things like this are exactly what BackgroundWorker is for, and why it knows the difference between the UI thread and the work thread. Use a background worker, parse your XML file in the DoWork event handler (it will run on the background thread) and regularly report progress using the ReportProgress method while proceeding through DoWork's loop. The ProgressChanged handler code will execute on the UI thread, and it should poke the progress dialog to provide a status update on the process

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • But it's not possible to add ui controls from another thread rather than the UI Thread. – PythonNoob Jun 01 '19 at 11:16
  • I'm not really sure what your comment means; add them to what? – Caius Jard Jun 01 '19 at 11:16
  • Still not making much sense – Caius Jard Jun 01 '19 at 11:17
  • Of course it makes sense... I want to add new controls, like TextBlock, Label etc. to a window, so you can't do this in a backgroundworker. https://stackoverflow.com/questions/3794420/why-is-only-the-ui-thread-allowed-to-modify-the-ui – PythonNoob Jun 01 '19 at 11:20
  • Why would you want to do that in a background worker? Surely you build your UI in design time, then you make an instance of it and show it - so show it, then start your bgworker, and let the bgworker report progress, and update the shown ui. I mean.. you can build the UI in runtime, but don't do it inside the DoWork of the background worker; do it on the main UI thread (the thing that starts the bgworker) if youre going to do it anywhere – Caius Jard Jun 01 '19 at 11:34
  • `Surely you build your UI in design time` - the very point of the question is that the UI is to be built at runtime. `you can build the UI in runtime, but don't do it inside the DoWork of the background worker; do it on the main UI thread` - that's what the OP currently does, and it takes 15-30 seconds, which is the premise of the question. – GSerg Jun 01 '19 at 11:38