2016-04-22 107 views
0

我在从另一个线程访问UI时遇到了一些麻烦。 我了解有关跨线程限制的基础知识,但似乎无法编写能够工作的代码。更具体地说,我不能从静态方法(线程)访问ListView。了解跨线程控件访问C#

我试图使它与backgroundWorker一起使用。

这里是我的代码:

private void start_Click(object sender, EventArgs e) 
{ 
    ServicePointManager.DefaultConnectionLimit = 20; 
    var tasks = new List<Task<int>>(); 

    foreach (ListViewItem item in listView1.Items) 
    { 
     string currentUrl = item.SubItems[1].Text; 
     int i = item.Index; 
     tasks.Add(Task.Factory.StartNew(() => { return GetWebResponse(currentUrl, i); })); 
    } 
    Task.WaitAll(tasks.ToArray()); 
} 

private static int GetWebResponse(string url, int itemIndex) 
{ 
    int statusCode = 0; 
    HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);   

    Task<HttpWebResponse> responseTask = Task.Factory.FromAsync<HttpWebResponse>(httpWebRequest.BeginGetResponse, asyncResult => (HttpWebResponse)httpWebRequest.EndGetResponse(asyncResult), null); 

    backgroundWorker = new BackgroundWorker(); 
    backgroundWorker.WorkerReportsProgress = true; 
    backgroundWorker.WorkerSupportsCancellation = true; 
    backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); 
    backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); 
    backgroundWorker.RunWorkerAsync(); 


    return statusCode; 
} 

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    listView1.Items[0].ImageKey = "green"; 
} 

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while (!e.Cancel) 
    { 
     Thread.Sleep(5000); 
     backgroundWorker.ReportProgress(0); 
    } 
} 

此代码不起作用,因为backgroundWorker_DoWorkbackgroundWorker_ProgressChangedstatic,但如果我让他们静话,我无法访问listView1

编辑:得到它的工作。下面审查

public delegate void delUpdateListView(int itemIndex, int statusCode); 

public Form1() 
{ 
    InitializeComponent(); 
} 

private void start_Click(object sender, EventArgs e) 
{ 

    ServicePointManager.DefaultConnectionLimit = 20; 
    var tasks = new List<Task<int>>(); 

    foreach (ListViewItem item in listView1.Items) 
    { 
     string currentUrl = item.SubItems[1].Text; 
     int i = item.Index; 
     tasks.Add(Task.Factory.StartNew(() => { 
      //return GetWebResponse(currentUrl, i); 

      int statusCode = 0; 
      HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(currentUrl); 

      Task<HttpWebResponse> responseTask = Task.Factory.FromAsync<HttpWebResponse>(httpWebRequest.BeginGetResponse, asyncResult => (HttpWebResponse)httpWebRequest.EndGetResponse(asyncResult), null); 
      statusCode = (int)responseTask.Result.StatusCode; 

      object[] invParams = new object[2]; 
      invParams[0] = i; 
      invParams[1] = statusCode; 

      if (InvokeRequired) 
      { 
       BeginInvoke(new delUpdateListView(UpdateListView), invParams); 
      } 
      else 
      { 
       Invoke(new delUpdateListView(UpdateListView), invParams); 
      } 


      return statusCode; 

     })); 
    } 
    Task.WaitAll(tasks.ToArray()); 
} 

public void UpdateListView(int itemIndex, int statusCode) { 
    listView1.Items[itemIndex].ImageKey = "green"; 
} 
+0

[如何从C#中的其他线程更新图形用户界面?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in- c) – rinukkusu

回答

1

我看到一些问题在这里代码:

1)我不明白为什么GetWebResponse必须static。最简单的解决方案是将其作为实例方法。

2)为什么你仍然使用后台工作?

3)使用Task s并没有多大意义,然后在产卵后等待它们完成。这会阻止您的应用程序在它应该响应的位置。对于3):为了保持用户界面的响应和可更新性,禁用用户在产生任务之前不会点击的所有内容,向每个重新启用UI组件的任务添加一个继续操作。任务可以使用通常的Invoke调用来更新列表。

+0

谢谢。这帮助我使它工作。你可以查看我的编辑,看看它是否遵循最佳做法。我已经添加了您提及的Invoke调用 – Cornwell

+1

看起来更好:-)在任何情况下,您都在代码中使用了“Invoke”调用。代码应该像'if(InvokeRequired)DoStuffWithInvoke;否则DoStuffDirectlyWithoutInvoke;'。你可以使用一个匿名委托,比如'Invoke((Action)delegate(){UpdateListView(i,statusCode);});'并且不需要委托类型。 –

+0

好点,不知道为什么我加了第二个调用... – Cornwell