【问题标题】:Get a Control's Property from Another Thread从另一个线程获取控件的属性
【发布时间】:2012-05-21 06:22:28
【问题描述】:

我想从我的表单中的BackgroundWorker 获取控件的属性:

foreach (ListViewItem i in ListView.CheckedItems) { //error: Cross-thread operation not valid: Control 'ListView' accessed from a thread other than the thread it was created on.
    //do something with i
}

谁能推荐最简单的方法来做到这一点?

【问题讨论】:

  • 如果您可以使用BackgroundWorker 表单中的每个控件都已经可读...
  • 请向我们展示您正在使用的代码...
  • ListView.CheckedItems 对我来说没有意义。是错字吗(你的意思是 ListView1.CheckedItems 还是类似的东西)

标签: c# .net vb.net winforms multithreading


【解决方案1】:

试试这样的:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void OnClick(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void OnDoWork(object sender, DoWorkEventArgs e)
        {
            foreach (ListViewItem i in GetItems(listView1))
            {
                DoSomething(i);
            }
        }

        private IEnumerable<ListViewItem> GetItems(ListView listView)
        {
            if (InvokeRequired)
            {
                var func = new Func<ListView, IEnumerable<ListViewItem>>(GetItems);
                return (IEnumerable<ListViewItem>)Invoke(func, new[] { listView });
            }
            // Create a defensive copy to avoid iterating outsite UI thread
            return listView.CheckedItems.OfType<ListViewItem>().ToList();
        }

        private void DoSomething(ListViewItem item)
        {
            if (InvokeRequired)
            {
                var action = new Action<ListViewItem>(DoSomething);
                Invoke(action, new[] { item });
                return;
            }
            // Do whatever you want with i
            item.Checked = false;
        }
    }
}

但是,您的问题非常笼统。如果您分享更多细节,也许会有更简单或更好的解决方案。

【讨论】:

    【解决方案2】:

    让我再试一次......

    1.) 将 ListView 拖到窗体上

    2.) 将 BackgroundWorker 拖到表单上

    3.) 创建一个遍历 ListViewItem 集合的方法

    private void LoopThroughListItems()
    {
        foreach (ListViewItem i in listView1.CheckedItems)
            DoSomething(); 
    
    }
    

    4.) 添加代码以在 BackgroundWorker 的 DoWork 事件中调用 LoopThroughListItems()

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        LoopThroughListItems();
    }
    

    5.) 在您的表单加载中 - 在主线程上执行代码(它可以工作)然后在 backgroundWorkder 线程上(它失败)

    private void Form1_Load(object sender, EventArgs e)
    {
        // Try it on the UI Thread - It works
        LoopThroughListItems();
    
        // Try it on a Background Thread - It fails
        backgroundWorker1.RunWorkerAsync();
    
    }
    

    6.) 修改您的代码以使用 IsInvokeRequired/Invoke

    private void LoopThroughListItems()
    {
    
        // InvokeRequired == True when executed by non-UI thread
        if (listView1.InvokeRequired)
        {
            // This will re-call LoopThroughListItems - on the UI Thread
            listView1.Invoke(new Action(LoopThroughListItems));
            return;
        }
    
        foreach (ListViewItem i in listView1.CheckedItems)
            DoSomething(); 
    }
    

    7.) 再次运行应用程序 - 现在它可以在 UI 线程和非 UI 线程上运行。

    解决问题。检查 IsInvokeRequired/Invoking 是一种您会习惯的常见模式(这就是它包含在所有控件中的原因)。如果你到处都在做这件事,你可以做一些聪明的事情并把它包起来——如下所述:Automating the InvokeRequired code pattern

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-13
      相关资源
      最近更新 更多