1

从线程内从表单控件获取数据时出现问题。我需要访问数据,然后修改它。

我知道以下内容不起作用,但我以它为例来看看我正在尝试做什么。

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

我已阅读有关进行线程安全调用的 MSDN 文档,但我似乎无法访问实际的列表视图控件。我见过的示例使用委托来“更新”控件,但在更新控件中的数据之前,我需要访问控件中的数据。

编辑:

我想查看一个示例或示例链接,详细说明如何在 foreach 循环中访问 ListView1 表单控件。

4

4 回答 4

3

您需要使用Invoke 模式,以便能够从主 UI 线程以外的线程访问任何UI 元素或其属性。Windows 上的所有 UI 控件始终运行在主线程上,以正确处理 OS 和屏幕上呈现的 UI 之间的消息链。

于 2013-07-22T07:40:32.190 回答
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);
   }
}
于 2013-07-22T08:38:28.010 回答
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();

    }
于 2013-07-22T08:50:23.687 回答
0

方法一:

像 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 线程调用 CheckAccess() 调用,则返回 true,否则返回 false。

Dispatcher 类位于“WindowsBase”NET 中的“System.Windows.Threading”命名空间中。集会

调度员信息复制自:https ://stackoverflow.com/a/4429009/1469035

编辑:将代码更改为 WinForms。编辑:代码已修复。

方法二:

使用回调:

未经测试的代码:

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;
    }
}

Winforms 中允许从 UI 线程以外的线程对控件进行只读访问。因此,您可以检查所需的任何控制属性并将所需的信息传递给代表。

即使 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;
    }

来源于: 链接

于 2013-07-22T07:43:22.730 回答