2013-07-22 30 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线程,因此线程的位置并不重要。 –