C# 从后台工作程序更新GUI

C# 从后台工作程序更新GUI,c#,multithreading,winforms,backgroundworker,C#,Multithreading,Winforms,Backgroundworker,问题的名称是:“从后台工作程序更新GUI”,但正确的名称是:“从后台工作程序更新GUI或从后台工作程序报告多个变量(整数除外)” 请让我解释一下我的情况。在一个程序中,我有一个分析信息的后台工作人员。作为这个分析的结果,表单GUI元素应该填充必要的数据。在GUI中,我想更新 2数据网格视图 1列表框 5个标签 据我所知-我只能通过后台工作程序的ReportProgress()方法以本机方式报告1int值。 所以问题是-如何通过ReportProgress()传递列表(+一些其他变量:stri

问题的名称是:“从后台工作程序更新GUI”,但正确的名称是:“从后台工作程序更新GUI或从后台工作程序报告多个变量(整数除外)”

请让我解释一下我的情况。在一个程序中,我有一个分析信息的后台工作人员。作为这个分析的结果,表单GUI元素应该填充必要的数据。在GUI中,我想更新

  • 2数据网格视图
  • 1列表框
  • 5个标签
据我所知-我只能通过后台工作程序的
ReportProgress()
方法以本机方式报告1
int
值。

所以问题是-如何通过
ReportProgress()
传递
列表(+一些其他变量:
string
int
)?基本上-我想用这些信息更新GUI,但“1整数”不行。。因此,要么可以通过
ReportProgress()
传递多个变量,要么可以从BackgroundWorker本身内部使用
调用来更新GUI。。就我个人而言,我不喜欢
调用
方法。。。你怎么看

以下是我的代码(请参阅注释):

private void按钮9\u单击(对象发送者,事件参数)//启动BW
{
bw.DoWork+=新DoWorkEventHandler(后台工作1_DoWork);
bw.RunWorkerCompleted+=新的RunWorkerCompletedEventHandler(backgroundWorker1\u RunWorkerCompleted);
bw.ProgressChanged+=新的ProgressChangedEventHandler(backgroundWorker1\U ProgressChanged);
bw.WorkerReportsProgress=true;
bw.workersupport扫描单元=真;
运行工作同步(10);
}
私有无效按钮10\u单击(对象发送者,事件参数)//取消BW
{
CancelAsync();
}
私有void backgroundWorker1\u DoWork(对象发送方,DoWorkEventArgs e)
{
int count=(int)e.参数;

对于(int i=1;i调用
ReportProgress
时,有一个
UserState
参数

var list_result = new List<List<string>>();

new backgroundWorker1.ReportProgress(0, list_result);
棘手的问题是,如何确定是传回
列表
,还是传回列表列表,还是传回单个字符串、数字等。您必须在
ProgressChanged
事件中测试每个可能性

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var myList = e.UserState as List<List<string>>;
    if (myList != null)
    {
        // use list
        return;
    }

    int myNumber;
    if (Int32.TryParse(e.UserState.ToString(), out myNumber))
    {
        // use number
        return;
    }

    var myString = e.UserState.ToString();
    // use string
}
void backgroundWorker1\u ProgressChanged(对象发送方,ProgressChangedEventArgs e)
{
var myList=e.UserState作为列表;
如果(myList!=null)
{
//使用列表
返回;
}
int myNumber;
if(Int32.TryParse(e.UserState.ToString(),out myNumber))
{
//使用编号
返回;
}
var myString=e.UserState.ToString();
//使用字符串
}

或者,您可以创建一个包含所有需要(或使用)的值的类,在后台运行所有内容来填充该类,然后将其传递给
RunWorkerCompleted
事件,然后从那里一次更新您的UI。

从另一个线程更新UI的基本模式是:

If controlItem.InvokeRequired Then
    controlItem.Invoke(Sub() controlItem.Text = textUpdateValue)
Else
    controlItem.Text = textUpdateValue
End If

这可以更新控件列表,而无需您通过ReportProgress传递任何内容。如果您希望从线程内更新控件,我认为您不需要检查InvokeRequest,因为它始终是必需的。但是,最佳做法可能是通过属性公开控件的设置,然后执行完整检查,以便您可以从任何地方调用它。

我编写了两个非常简单的方法,使您能够调用代码(仅在需要时),并且只需编写一次代码。我认为这使invoke的使用更加友好:

1) 开始觉醒

public static void SafeBeginInvoke(System.Windows.Forms.Control control, System.Action action)
{
    if (control.InvokeRequired)
        control.BeginInvoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
    else
        action();
}
2) 援引

可以这样称呼:

SafeInvoke(textbox, () => { textbox.Text = "text got changed"; });
或者你可以

System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;

(仅在调试模式下改变行为)看看你是否遇到了问题。
通常情况下,你实际上没有遇到问题。我花了相当长的时间才发现,要想不把事情搞得一团糟,调用是非常必要的。

你不喜欢调用
调用
?ReportProgress还有一个你可以传递的对象。@Savana我并不讨厌它。

我正在寻找一个替代方法从我目前的解决方案来看;)也许从BW转移到“新线程”将是一个解决方案..我个人不喜欢调用,因为我必须检查“如果需要调用,那么…”但是再一次-如果没有其他选择,那么我必须坚持使用这个方法。您的第一个代码片段创建了一个名为
list\u result
的变量,但是您将一个名为
myList
的未定义变量传递给后台工作人员的
ReportProgress
方法。您是想传递
list\u result
还是省去了一行或者…我们可以使用“e.ProgressPercentage”来检测对象的类型。如果(e.ProgressPercentage=1)=>对象是字符串。“2”->Obj是Int,那么做其他事情,“3”->。。。
public static void SafeInvoke(System.Windows.Forms.Control control, System.Action action)
{
    if (control.InvokeRequired)
        control.Invoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
    else
        action();
}
SafeInvoke(textbox, () => { textbox.Text = "text got changed"; });
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;