WPF:从后台线程发布更新UI

时间:2010-05-18 18:00:40

标签: wpf multithreading

我的代码启动后台线程。后台线程进行更改并希望主线程中的UI更新。启动线程然后等待的代码看起来像:

                Thread fThread = new Thread(new ThreadStart(PerformSync));

                fThread.IsBackground = true;
                fThread.Start();

                fThread.Join();

                MessageBox.Show("Synchronization complete");

当后台想要更新UI时,它会设置一个StatusMessage并调用以下代码:

    static StatusMessage _statusMessage;
    public delegate void AddStatusDelegate();
    private void AddStatus()
    {
        AddStatusDelegate methodForUIThread = delegate
        {
            _statusMessageList.Add(_statusMessage);
        };

        this.Dispatcher.BeginInvoke(methodForUIThread, System.Windows.Threading.DispatcherPriority.Send);
    }

_statusMessageList是一个ObservableCollection,它是ListBox的源。

调用AddStatus方法但主线程上的代码永远不会执行 - 也就是说,在执行线程时,_statusMessage不会添加到_statusMessageList。但是,一旦完成(fThread.Join()返回),主线程上的所有堆叠调用都将被执行。

但是,如果我在调用fThread.Start()和fThread.Join()之间显示一个消息框,那么状态消息会正确更新。

在等待线程终止时,我需要更改以便主线程中的代码执行(UI更新)?

感谢。

3 个答案:

答案 0 :(得分:6)

fThread.Join会导致主线程阻塞,直到后台线程完成。只要主线程被阻止,就无法更新UI。

你需要做的是这样的事情(未经测试,但你应该明白这一点):

void someUiEventThatCausesTheBackgroundProcessingToStart() {
    Thread fThread = new Thread(new ThreadStart(PerformSync));
    fThread.IsBackground = true;

    // disable some UI components here, so that the user cannot
    // start the thread a second time
    ...

    fThread.Start();

    // *don't* call Thread.Join here, so that your main thread does not block!
}

void PerformSync() {
    try {
        // do your stuff here
        ...
    } finally {
        Dispatcher.Invoke(new Action(ProcessingDone));
    }
}

void ProcessingDone() {
    // re-enable the UI components
    ...

    MessageBox.Show("Synchronization complete");
}

当然,在WPF中,禁用/启用UI组件最好使用一些IsBusyProcessing依赖项属性来完成,该属性与所讨论的UI元素的触发器绑定,但这是另一个故事......

编辑:作为另一种选择,您可能需要check out BackgroundWorker类,其中包含在主线程中自动触发的ProgressChangedRunWorkerCompleted个事件。

答案 1 :(得分:4)

fThread.IsBackground = true;
fThread.Start();

fThread.Join();

对Join()的调用阻塞了你的主线程,直到后台线程完成。因此,当另一个线程正在运行时,UI线程无法执行任何操作。你不需要调用Join()。如果您需要在后台线程完成后在主线程上执行某些操作,请找到另一种方法。

编辑:如果在调用Start和Join之间显示一个消息框,它的工作原理是因为主线程正在运行消息框的消息循环,而不是在Join调用时被阻止。

答案 2 :(得分:1)

BackgroundWorker是这项工作的正确工具。它并不比绝对必要的复杂,为您提供进度报告所需的所有钩子,并提供了大量有关如何使用它的信息(例如this answer)。