在线程内从表单控件获取数据时出现问题。我需要访问数据,然后修改它。
以下不起作用我知道,但我用它作为一个例子来看看我想要做什么。
Thread t = new Thread(() => {
foreach (ListViewItem row in listView1.Items)
{
row.SubItems[0].Text = "Checking";
Thread.Sleep(2000);
}
});
t.Start();
我已经阅读了关于进行线程安全调用的MSDN文档,但我似乎无法访问实际的列表视图控件。我见过的例子使用委托来“更新”控件,但在更新控件中的数据之前,我需要访问控件中的数据。
编辑:
我想看一个示例或示例链接,详细说明如何在foreach循环中访问ListView1表单控件。
答案 0 :(得分:3)
您需要使用Invoke pattern,以便能够从主UI线程以外的线程访问任何 UI元素或其属性。 Windows上的所有UI控件都在主线程上运行,以便在屏幕上显示的OS和UI之间正确处理消息链。
答案 1 :(得分: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);
}
}
答案 2 :(得分:0)
方法1:
像Tigran描述的那样使用Invoke。
对于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();
如果从UI-Thread调用,则CheckAccess()调用返回true,否则返回false。
Dispatcher类位于“WindowsBase”NET中的“System.Windows.Threading”命名空间中。装配
从https://stackoverflow.com/a/4429009/1469035
复制的调度程序信息编辑:将代码更改为WinForms。 编辑:代码已修复。
方法2:
使用回叫:
未经测试的代码:
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中允许从UI-Thread以外的线程访问控件。因此,您可以检查所需的任何Control-Property,并将所需信息传递给Delegate。
即使Reading doents以这种方式工作,您也可以创建另一个具有返回值的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
答案 3 :(得分: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();
}