2013-07-22 28 views
1

從線程中獲取表單控件中的數據時出現問題。我需要訪問數據,然後修改它。從線程中獲取窗體控件中的數據

以下行不通我知道,但我用它作爲例子,看看我想要做什麼。

Thread t = new Thread(() => { 
    foreach (ListViewItem row in listView1.Items) 
    { 
     row.SubItems[0].Text = "Checking"; 
     Thread.Sleep(2000); 
    } 
}); 
t.Start(); 

我讀過關於進行線程安全調用的MSDN文檔,但我似乎無法訪問實際的列表視圖控件。我見過的示例使用委託來「更新」控件,但在更新數據之前,我需要訪問控件中的數據。

編輯:

我想看到一個例子,或者鏈接到一個例子,詳細介紹瞭如何獲得訪問在foreach循環ListView1的形式控制。

+2

我一直髮現我並不真正需要訪問控制交叉線程,thread.sleep也幾乎不是一個好主意。你試圖完成什麼? – Sayse

+0

使用調度程序http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx – wudzik

+0

只要確保'listView1'是一個全局變量。 –

回答

3

您需要使用Invoke pattern,以便能夠訪問任何 UI元素或其主線程之外的其他線程的屬性。 Windows上的所有UI控件都在主線程上運行,以在屏幕上顯示的OS和UI之間正確處理消息鏈。

+0

它仍然不能解釋如何訪問數據。它只顯示如何設置數據。 –

+0

@JamesJeffery - 您的示例代碼僅設置了Control.Property。 –

+0

問題是在foreach循環中從ListView1中讀取日期。 –

0

方法1:

使用調用像蒂格蘭描述。

對於此的WinForms會是什麼樣子:

 Thread t = new Thread(() => 
     { 
      if (!Dispatcher.CurrentDispatcher.CheckAccess()) 
      { 
       Dispatcher.CurrentDispatcher.BeginInvoke(
        new Action(() => 
        { 
         foreach (ListViewItem row in listView1.Items) 
         { 
          row.SubItems[0].Text = "Checking"; 
          Thread.Sleep(2000); 
         } 
        }), 
        DispatcherPriority.ApplicationIdle, 
        null); 
      } 
      else 
      { 
       foreach (ListViewItem row in listView1.Items) 
       { 
        row.SubItems[0].Text = "Checking"; 
        Thread.Sleep(2000); 
       } 
      } 
     }); 
     t.Start(); 

的的checkAccess()如果從UI線程否則爲false稱爲調用返回true。

Dispatcher類位於「WindowsBase」NET中的「System.Windows.Threading」命名空間中。 https://stackoverflow.com/a/4429009/1469035

編輯:大會

調度信息從複製更改代碼的WinForms。 編輯:代碼固定。

方法2:

使用回調:

未經測試的代碼:從除UI線程其他線程控制

public partial class Form1 : Form 
{ 
    private delegate void SetCallback(ListViewItem row, string text); 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void SomeMethod() 
    { 
     Thread t = new Thread(() => 
     { 
      foreach (ListViewItem row in listView1.Items) 
      { 
       if (listView1.InvokeRequired) 
       { 
        SetCallback d = new SetCallback(SetText); 
        this.Invoke(d, new object[] { row, "Checking" }); 
       } 

       Thread.Sleep(2000); 
      } 
     }); 
     t.Start(); 
    } 

    private void SetText(ListViewItem row, string text) 
    { 
     row.SubItems[0].Text = text; 
    } 
} 

AFAIK只讀訪問被允許的WinForms。所以你可以檢查你想要的任何Control-Property,並將所需的信息傳遞給Delegate。

即使閱讀工具以這種方式工作,您也可以創建另一個具有返回值的委託。該invoke()方法返回一個對象:

與此類似:

private delegate object GetCallback(ListViewItem row); 
private object o; 

... 

GetCallback g = new GetCallback(GetText); 
         o = this.Invoke(g, new object[] { row }); 


    private string GetText(ListViewItem row) 
    { 
     return row.SubItems[0].Text; 
    } 

來源於:Link

+2

Backgroundworker會給出同樣的問題(在RunWorkerEventArgs.Error中隱藏),方法2基本上連接tigrans回答 – Sayse

+0

對此答案沒有真正的解釋。展開它。 –

+0

給我一些時間,我仍然在它;) –

1

的(快寫)的例子,我說的是,這是假定你不需要爲了真正使用這些控件,我加入了一個基於Tigran鏈接的功能

Thread t = new Thread(() => UpdateText(listBox1.Items)); 
t.Start(); 

private void UpdateText(ListBox.ObjectCollection items) 
{ 
    foreach (var item in items) 
    { 
     SetText(item.ToString()); 
     Thread.Sleep(1000); 
    } 
} 
0

你不能做你想做的事。所有對UI的訪問和更新都必須在UI線程中進行。這是強制性的。 你可以做的是將原始數據寫入UI的緩存,然後在完成所有處理後處理緩存和回調到UI。

public class CacheData { 
     private object row; 

     public CacheData(object row) 
     { 
      //initialization 
     } 

     public static ProcessedData ProcessData(List<CacheData> dataToProcess) 
     { 
      return new ProcessedData(); 
     } 
    } 

    public class ProcessedData { } 

    private void AccessControl() 
    { 
     ListView list = new ListView(); 
     List<CacheData> cache = new List<CacheData>(); 

     //Filling the cache on UI 
     foreach (var row in list.Items) 
     { 
      cache.Add(new CacheData(row)); 
     } 

     //Process result async and then invoke on UI back 
     System.ComponentModel.BackgroundWorker bg = new System.ComponentModel.BackgroundWorker(); 
     bg.DoWork += (sender,e) => { 
      e.Result = CacheData.ProcessData(cache); 
     }; 
     bg.RunWorkerCompleted += (sender, e) => { 

      //If you have started your bg from UI result will be invoked in UI automatically. 
      //Otherwise you should invoke it manually. 
      list.Dispatcher.Invoke((Action) delegate { 
       //pass e.result to control here) 
      },null); 
     }; 

     bg.RunWorkerAsync(); 

    } 
+0

我正在處理的線程是自定義窗體(將其用作對話框)。雖然我沒有在描述中包含這些內容。 –

+0

由於只有一個UI線程,因此線程的位置並不重要。 –