如何使用后台线程中的信息更新WPF窗口?

时间:2011-01-27 10:55:12

标签: c# wpf multithreading user-interface

我正在创建一个wpf应用程序,它在后面执行许多任务,但仍然需要UI响应并显示各种后台任务的状态。它还可以选择不显示UI,在这种情况下,应该丢弃状态消息而不创建主表单的实例。

我试图删除

StartupUri="MainWindow.xaml"

来自App.xaml的

。然后,在App.xaml.cs中,我有

`

    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.

    }`

如果需要,这会显示主窗体并启动所有后台进程。这部分有效。

为了将消息从后台发送到用户界面,我实现了一个非常基本的信使:

`

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);
        }
    }
}`

主窗体实现了IMessageHandler接口,并在启动时将自身添加到Messenger作为处理程序。需要向主表单发送状态的任何进程只需要调用信使的Broadcast方法。

我遇到的问题是,在后台进程完成之前,消息不会显示在表单上,​​并且UI也会被锁定。

处理接收消息的UI中的代码如下:

`

    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);
        }
    }`

为什么我的后台进程会冻结我的UI?我该怎么做才能防止它?为什么我的UI没有使用状态消息进行更新?

3 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,this blog post帮助我解决了问题。

答案 1 :(得分:0)

也许这是你的问题:

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

尝试将此更改为,

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

因为当您使用Invoke时,该方法会被执行并且应用程序等待它完成,但是使用BeginInvoke方法将被异步调用,并且应用程序继续执行{{1}中引用的方法{1}}已执行。

阅读本文:whether to use Invoke or BeginInvoke

答案 2 :(得分:0)

使用以下代码以避免冻结UI。在我的应用程序中,我使用了BackgroundWorker类。通过使用代码更改表单上的任何内容,将引发运行时错误。

我使用下面的代码来避免这种情况,它对我来说非常适合。

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

请注意括号之间的部分('{}')如果您希望更改表单上的内容,则应放置代码。