【问题标题】:Getting Data from Form Control in Thread从线程中的表单控件获取数据
【发布时间】:2013-07-22 07:36:38
【问题描述】:

从线程内的表单控件获取数据时遇到问题。我需要访问数据,然后对其进行修改。

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

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

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

编辑:

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

【问题讨论】:

  • 我一直发现我并不真正需要访问控制跨线程,而且 thread.sleep 几乎不是一个好主意。你想完成什么?
  • 只要确保listView1 是一个全局变量。
  • @PhongVo - 如果这是一个答案,你会很快得到否决
  • 线程睡眠被插入做一些测试,以复制网络访问速度。它不在我正在尝试做的实际实现中。

标签: c# multithreading winforms


【解决方案1】:

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

【讨论】:

  • 它仍然没有解释如何访问数据。它只是显示如何设置数据。
  • @JamesJeffery - 您的示例代码也只是设置了一个 Control.Property。
  • 问题是从 foreach 循环中的 ListView1 读取日期。
  • @JamesJeffery:访问您的控件 (ListView) 属性并阅读它们。问题出在哪里?
  • @JamesJeffery 将设置的代码替换为 get data code 应该可以工作
【解决方案2】:

我正在谈论的(快速编写的)示例,假设您不需要真正使用控件,我包含了一个基于 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);
   }
}

【讨论】:

    【解决方案3】:

    你不能做你想做的事。所有对 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();
    
        }
    

    【讨论】:

    • 我正在处理的线程是自定义表单的线程(将其用作对话框)。虽然我没有在描述中包含这一点。
    • 线程在哪里并不重要,因为只有一个 UI 线程。
    【解决方案4】:

    方法一:

    像 Tigra 描述的那样使用 Invoke。

    对于 Winform,这看起来像:

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

    源自:Link

    【讨论】:

    • Backgroundworker 会给出同样的问题(在RunWorkerEventArgs.Error 中被掩盖),方法2 基本上是链接 tigrans 答案
    • 对此答案没有真正的解释。展开它。
    • 给我一些时间我还在它上面;)
    • 顺便说一句,您使用的是 WPF 还是 WinForms?
    • 我正在使用 WinForms mate。
    猜你喜欢
    • 2013-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    相关资源
    最近更新 更多