【问题标题】:C# - How to update Main UI from a thread in another classC# - 如何从另一个类中的线程更新主 UI
【发布时间】:2015-04-20 04:39:16
【问题描述】:

我实际上正在学习(艰难的方式)c#,并且已经为一个问题奋斗了好几天:

我正在使用 WPF (dotNet 4.0) 编写我的第一个 c# 应用程序。当我单击一个按钮时,将使用一个 BackgroundWorker 线程并从外部类调用一个方法,这样我的 UI 就不会冻结 -> 我的方法按预期运行。

然后我尝试从外部类更新 ListView 控件以获取某种进度(文本),但我失败了。

我了解我需要使用委托和调度程序来更新我的控件。

我尝试使用此处提供的解决方案 How to update UI from another thread running in another class 。 (由于我的代表率低,我无法对此发表评论)并且我错过了这个难题的某些部分。

YourEventArgs(status) 指的是什么?当我的方法在 BGW 中运行时,我无法触发事件并将内容传递回我的 UI。

到目前为止,我有这段代码(从答案更新):

namespace AppMain
{
    public partial class MainWindow
    {
        BackgroundWorker AppWorker = new BackgroundWorker();

        public MainWindow()
        {
            InitializeComponent();

            AppWorker.WorkerSupportsCancellation = true;
            AppWorker.DoWork += AppWorker_DoWork;
            AppWorker.RunWorkerCompleted += AppWorker_RunWorkerCompleted;
            }

        private void btnLoad_Click(object sender, RoutedEventArgs e)
        {
            lstTest.Items.Add("Processing data...");
            AppWorker.RunWorkerAsync();
        }

        public void AppWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            SetXmlData xml = new SetXmlData();
            xml.ProgressUpdate += (s, evt) =>
            {
                Dispatcher.BeginInvoke((Action)(() =>
                {
                    lstTest.Items.Add("this is a test : " + evt.myData); //how to retrieve the myData property from evt ?
                }));
            };
            xml.FlushData();
        }

        public void AppWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
            {
                lstTest.Items.Add("Done");
            }
            else
            {
                MessageBox.Show("Cancelled");
            }
        }
}
}

SetXmlData.cs

namespace AppMain
{
    public class SetXmlData
    {
    public event EventHandler ProgressUpdate;

        //update method
        public void update(object input)
        {
        if (ProgressUpdate != null)
        ProgressUpdate(this, new YourEventArgs { myData = (string)input });
        }

        //calculation method
        public void FlushData()
        {
            MessageBox.Show("this is a test !");
            update("test");
        }
    }

    public class YourEventArgs : EventArgs
    {
        public string myData { get; set; }
    }
}

感谢您的帮助。

【问题讨论】:

  • 如果您在 .net 4.0 中有任务,为什么要使用 BGW,可以由当前同步上下文提供以在其中执行?
  • 我一开始是在使用这个,但后来为了 ReportProgress 功能转移到了 BGW,最后你是对的,当我解决这个问题时,我会切换回 Task 也不是更好。

标签: c# wpf multithreading events delegates


【解决方案1】:

您可以简单地从FlushData() 方法调用ProgressUpdate 事件。

只需调用:

If (ProgressUpdate !=null )
{
    ProgressUpdate(this,new YourEventArgs())
}

thisevent 的来源实例。

您可以通过继承 EventArgs 类来创建 YourEventArgs。

  public class YourEventArgs : EventArgs
  {
     //Put any property that you want to pass back to UI here.
  }

event 在 UI 中出现时:

RaiseEvent.ProgressUpdate += (s, e) =>
        {
            Dispatcher.BeginInvoke((Action)(() => 
                                        { 
                                            lstTest.Items.Add("this is a test : "); 
                                            //Add items to your UI control here...
                                        }));
        };

e 的类型为YourEventArgs

附带说明,您永远不应该从不同的线程(例如您的示例中的后台工作线程)触摸 UI 线程。由于您的event-handler 已经在使用Dispatcher.BeginInvoke,因此很安全。

此外,您的 ProgressUpdate 事件应该在您的类 SetXmlData 中。

【讨论】:

  • 您好 ANewGuyInTown,感谢您的友好回答。阅读您的解决方案后,我已经更新了我的代码。到目前为止,我不得不在 DoWork 方法中移动订阅,如果我不这样做,处理程序总是设置为 null 并且不会引发事件?!是因为我需要从用于实例化 SetXmlData 类的线程中执行此操作吗?此外,当我尝试使用 evt.myData 时,我无法从 myData 属性中获取内容:“System.EventArgs”不包含“myData”的定义,并且没有扩展方法“myData”接受第一个参数可以找到“System.EventArgs”类型的
  • 将您的 ProgressUpdated 事件处理程序修改为 public event EventHandler<YourEventArgs> ProgressUpdate; 是的,您必须在实例化 SetXmlData 类的线程中执行此操作。
【解决方案2】:

尝试获取;设置;示例:

表格1:

public partial class Form1 : Form
{
    static public string gettext { get; set; }

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Class1.send(); //call to class function
        textBox1.Text = gettext; //items.add(gettext)
    }
}

第一类:

    class Class1
{
    static public void send()
    {
        Form1.gettext = "Marko"; //Set gettext to string "Marko"

    }
}

【讨论】:

    猜你喜欢
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多