listView的交叉线程问题

本文关键字:线程 问题 listView | 更新日期: 2023-09-27 17:49:51

好的,在每个人发布重复的帖子之前,让我告诉你,我已经看过所有其他的帖子,我仍然丢失了一些说使用委托或后台工作器等…但我如何使这个线程安全,我想删除自己的线程上的文件。

这里是我正在使用的代码。

private void button1_Click(object sender, EventArgs e)
{
    cleanFiles.RunWorkerAsync();
}
private void cleanFiles_DoWork(object sender, DoWorkEventArgs e)
{
    if (listView1.CheckedItems.Count != 0)
    {
        // If so, loop through all checked files and delete.
        for (int x = 0; x <= listView1.CheckedItems.Count - 1; x++)
        {
            string tempDirectory = Path.GetTempPath();
            foreach (ListViewItem item in listView1.CheckedItems)
            {
                string fileName = item.Text;
                string filePath = Path.Combine(tempDirectory, fileName);
                try
                {
                    File.Delete(filePath);
                }
                catch (Exception)
                {
                    //ignore files being in use
                }
            }
        }
        PaintListView(tFile);
        MessageBox.Show("Files removed");
        toolStripStatusLabel1.Text = ("Ready");
    }
    else
    {
        MessageBox.Show("Please put a check by the files you want to delete");
    }
}

listView的交叉线程问题

正如Reed提到的,除了UI线程本身,你不能从其他线程访问UI元素。因此,你需要传递一个委托Control.Invoke()来与UI线程一起执行,像这样

    private void cleanFiles_DoWork(object sender, DoWorkEventArgs e)
    {
        if (listView1.CheckedItems.Count != 0)
        {
            // If so, loop through all checked files and delete.
            for (int x = 0; x <= listView1.CheckedItems.Count - 1; x++)
            {
                string tempDirectory = Path.GetTempPath();
                foreach (ListViewItem item in listView1.CheckedItems)
                {
                    string fileName = item.Text;
                    string filePath = Path.Combine(tempDirectory, fileName);
                    try
                    {
                        File.Delete(filePath);
                    }
                    catch (Exception)
                    {
                        //ignore files being in use
                    }
                }
            }
            PaintListViewAndSetLabel();
        }
        else
        {
            ShowMessageBox();
        }
    }
    private void ShowMessageBox()
    {
        if(InvokeRequired)
        {
            this.Invoke(new Action(ShowMessageBox), new object[0]);
            return;
        }
        MessageBox.Show("Please put a check by the files you want to delete");
    }
    private void PaintListViewAndSetLabel()
    {
        if (InvokeRequired)
        {
            this.Invoke(new Action(PaintListViewAndSetLabel),new object[0]);
            return;
        }
        PaintListView(tFile);
        MessageBox.Show("Files removed");
        toolStripStatusLabel1.Text = ("Ready");
    }

问题是你不能直接从后台线程访问ListView (listView1)的任何属性(即:cleanFiles_DoWork方法中的任何内容)。用户界面控件不能在用户界面线程以外的任何线程上访问。

相反,你应该在调用DoWork之前制作一个要"清理"的项目列表,并通过RunWorkerAsync重载传递给一个对象,并通过DoWorkEventArgs.Argument在你的方法中检索它。

这将允许你传递一个项目列表来处理,在后台处理它们,然后当你完成时更新你的列表。

使用来自后台worker的控件是个坏主意,我最近在使用TreeView控件时遇到了同样的问题。因此,线程安全调用Windows窗体控件的解决方案是微软的一篇文章。主要思想是使用控件的InvokeRequired属性检查安全性,如果有必要,通过Invoke方法运行方法调用,这是线程安全的。