来自Background worker的UI访问问题

时间:2011-06-21 20:53:13

标签: c# wpf backgroundworker

我正在将项目从winforms转移到WPF。当我的代码基于WinForms时,我使用(this.InvokeRequired)检查线程是否具有访问权限。现在我根据我的Mainform使用以下代码:

    // this is the delegate declaration to Allow Background worker thread to write to Log Output
    delegate void LogUpdateCallBack(String LogMessage);

    // method to update the Log Window from the Background Thread
    public void LogUpdate(String LogMessage)
    {
        Console.WriteLine("Entering");
        if (!Application.Current.Dispatcher.CheckAccess())
        {
            Console.WriteLine("Thread doesn't have UI access");
            LogUpdateCallBack callback = new LogUpdateCallBack(LogUpdate);
            Application.Current.Dispatcher.Invoke(callback, LogMessage);
        }
        else
        {
            Console.WriteLine("Thread has UI access");
            listBox_Output.Items.Add(LogMessage);
            Console.WriteLine(LogMessage);
            // listBox_Output.TopIndex = listBox_Output.Items.Count - 1;
        }
        Console.WriteLine("Exiting");
    }

我遇到的问题是列表框没有更新。没有错误或异常,我尝试更新其他UI控件。 LogMessage正在写入Output窗口,所以我很难过。

以下是控制台输出示例:

Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here 
Exiting
Exiting
Entering
Thread doesn't have UI access
Entering
Thread has UI access
My LogMessage is output here 
Exiting
Exiting

我尝试更新其他UI控件只是为了检查我的列表框是否存在问题,但没有运气。

除了切换到CheckAccess()之外,我在新WPF代码中做出的唯一其他重大改变是将后台工作程序中运行的所有代码基于另一个类。我不确定这是否可能是部分问题?。

-

@JonRaynor

我尝试了你的想法:

Application.Current.Dispatcher.BeginInvoke(new LogUpdateCallBack(LogUpdate), LogMessage)

但是,如果输出

,我的列表框仍然没有更新
Console.WriteLine(listBox_Output);

我看到列表框数组在增长:

System.Windows.Controls.ListBox Items.Count:2020
System.Windows.Controls.ListBox Items.Count:2021
System.Windows.Controls.ListBox Items.Count:2022
System.Windows.Controls.ListBox Items.Count:2023
System.Windows.Controls.ListBox Items.Count:2024
System.Windows.Controls.ListBox Items.Count:2025

但形式没有变化。这非常令人困惑!。

3 个答案:

答案 0 :(得分:2)

我刚开始使用WPF,不得不从旧的WinForms方式重新学习。我一直在我的屏幕(表格)上使用BeginInvoke()和这种语法......

public delegate void WorkCompleted();

        /// <summary>
        /// Marks the end of the progress
        /// </summary>
        private void ProgressComplete()
        {
            if (!this.Dispatcher.CheckAccess())
            {
                this.Dispatcher.BeginInvoke(new WorkCompleted(ProgressComplete), System.Windows.Threading.DispatcherPriority.Normal, null);
            }
            else
            {
                this.buttonClose.Visibility = Visibility.Visible;
                this.progressBarStatus.IsIndeterminate = false;
            }
        }

答案 1 :(得分:1)

我终于解决了这个问题。

我的问题是,当我从另一个线程和另一个类调用LogUpdate方法时,因此我需要将包含listbox的主窗体的引用传递给此类,而不是创建主窗体的新实例在中学阶段。

所以不要在我的二级课程中使用此声明:

public MainWindow MainForm = new MainWindow();

我需要将表单的引用传递给辅助类中的方法:

    public void Plot(BackgroundWorker worker, MainWindow MainForm)

答案 2 :(得分:0)

在WPF中,所有UI控件都派生自DispatcherObject。这意味着您的ListBox控件本身就是一个支持调度的对象。尝试在该对象上调用Dispatcher,而不是依赖于表单本身。取出你的委托上的参数(因为你无论如何都可以访问LogMessage变量)它看起来像:

public void LogUpdate(string LogMessage)
{
    Console.WriteLine("Entering");
    if (listBox_Output.Dispatcher.CheckAccess())
    {
        listBox_Output.Dispatcher.Invoke(new LogUpdateCallBack(delegate
        {
            listBox_Output.Items.Add(LogMessage);
        }));
        Console.WriteLine(LogMessage);
    }
}