0

I'm creating a wpf application which performs many tasks in the backgound, but still requires the UI to be responsive and to display the status of the various background tasks. It also has the option to not display the UI at all, in which case the status messages should be discarded without creating an instance of the main form at all.

What I've attempted is to remove

StartupUri="MainWindow.xaml"

from App.xaml. Then, in App.xaml.cs, I have

`

    public App()
    {
        Startup += new StartupEventHandler(App_Startup);
    }

    void App_Startup(object sender, StartupEventArgs e)
    {
        // Code which loads application settings is here

        if (pf.ShowUI)
        {
            MainWindow mainWindow = new MainWindow();
            mainWindow.Show();
        }

        // The background processes will begin here.

    }`

This shows the main form, if necessary, and starts all the background processes. This part works.

In order to send messages from the background to the UI, I've implemented a very basic messenger:

`

internal interface IMessageHandler
{
    void ReceiveMessage(string message);
}

internal class Messenger
{
    private static List<IMessageHandler> _handlers;

    internal static void AddHandler(IMessageHandler handler)
    {
        _handlers.Add(handler);
    }

    internal static void RemoveHandler(IMessageHandler handler)
    {
        try
        {
            _handlers.Remove(handler);
        }
        catch (Exception ex) {}
    }

    internal static void Broadcast (string message)
    {
        foreach (IMessageHandler handler in _handlers)
        {
            handler.ReceiveMessage(message);
        }
    }
}`

The main form implements the IMessageHandler interface, and adds itself to the Messenger as a handler when it starts up. Any process that needs to send a status to the main form just needs to call the Broadcast method of the messenger.

The problem I'm having, is that the messages are not being shown on the form until the background processes complete, and the UI is locked up until then as well.

The code in the UI which handles receiving messages is as follows:

`

    public void ReceiveMessage(string message)
    {
        Dispatcher.Invoke(DispatcherPriority.Normal,
                new Action<string>(AddText),
                message);
    }

    private void AddText(string text)
    {

        Label myLabel = new Label();
        myLabel.Content = text;
        stackPanel1.Children.Add(myLabel);
        if (stackPanel1.Children.Count > 5)
        {
            stackPanel1.Children.RemoveAt(0);
        }
    }`

Why are my background processes freezing my UI? What can I do to prevent it? And why is my UI not updating with the status messages?

Richard C
  • 2,176
  • 5
  • 30
  • 40
  • Consider using a pattern as MVVM, to bind your view to your model data. – HyLian Jan 27 '11 at 10:56
  • I had the very same problem and [this blog post](http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx) helped me to solve it . – Asha Jan 27 '11 at 10:59
  • It seems that what I needed to do was explicitly launch my background threads as new threads. I'd assumed that by just calling them from the end of my App_Startup method that they'd be on a separate thread to the UI. This seems to not be the case. As soon as I launched the process in a new Thread, all the other parts worked exactly as expected. Thanks for the link. – Richard C Jan 27 '11 at 13:56
  • thank you ,and happy that it solved your issue . – Asha Jan 27 '11 at 16:19

2 Answers2

0

Maybe this is your problem:

   Dispatcher.Invoke(DispatcherPriority.Normal,
                new Action<string>(AddText),
                message);

Try change this to,

   Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                new Action<string>(AddText),
                message);

Because when you use Invoke, the method gets executed and the application waits for it to complete, but with BeginInvoke the method is invoked Asychnronously and the application continues to execute while the method referenced in BeginInvoke is executed.

Read this: whether to use Invoke or BeginInvoke

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

Use the below code to avoid freezing the UI. In my application I have used a BackgroundWorker class. By changing anything on the form using code, a run time error is thrown.

I am using the below code to avoid this and it works perfectly for me.

Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
   rtf_status.AppendText("Validating XML against schema...Please wait\n");
}));

Note the part after the between braces ('{}') this is where you should place your code if you wish to change something on the form.

Prashant
  • 966
  • 9
  • 26
  • Essentially, this code is no different from mine, you're simply using an anonymous method where I'm using a named method. – Richard C Jan 27 '11 at 13:52