【问题标题】:Display pop up with animated gif while loading file加载文件时显示带有动画 gif 的弹出窗口
【发布时间】:2014-05-30 23:20:46
【问题描述】:

我的程序的目标是将一个目录加载到我的树视图节点中,这需要 10 秒以上(因为该目录驻留在远程 PC 中)。在此期间,我将添加一个等待的弹出图像。我复制了这个帖子的答案:How can i show an image while my application is loading 到我的代码,比如:

   private void treeView2_AfterSelect(object sender, TreeViewEventArgs e)
   {
        Form f = new Form();
        f.Size = new Size(20, 25);
        Image im = Image.FromFile(@"C:\Documents and Settings\JiangC\Documenti\Immagini\loader.gif");
        f.FormBorderStyle = FormBorderStyle.None;
        f.MinimizeBox = false;
        f.MaximizeBox = false;
        f.AutoSize = true;
        PictureBox pb = new PictureBox();

        //pb.Dock = DockStyle.Fill;
        pb.SizeMode = PictureBoxSizeMode.AutoSize;
        pb.Image = im;
        pb.Location = new Point(50, 50);
        f.Controls.Add(pb);
        f.Show();
       // Application.DoEvents();
        BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);            
        f.Close();
    }

我想要做的是在加载过程中(当 Buildtree() 方法正在实现时)带有图片框 pb 的表单 f 将被显示,并且在加载后它们就消失了。

第一个问题在“pb.Dock = DockStyle.Fill;”中 如果我取消注释,这个表单 f 将不会在加载过程中显示(但我可以在屏幕底部看到它的最小化窗口)。只有当我取消这条线时,表格 f 才能显示出来。 第二个问题是“f.Show();”当显示形式 f 时,完全不显示图片框 pb(只是一个空洞)。如果我将其修改为“f.ShowDialog();”,则可以显示带有图片的表格f。 然而,第三个问题来了,形式 f 将永远存在,我的函数“BuildTree();”根本没有实施!
第四个问题是如果我添加“Application.DoEvents();”行,它工作得很好,在加载过程中会显示带有图片框的表单 f,加载后 f 会消失,但 f 中的图片是gif,在这种情况下,gif 根本没有动画!

所以有人可以帮我解决这个问题吗?

这是我的 BuildTree 函数代码:

private void BuildTree(DirectoryInfo[] directoryInfo, TreeNodeCollection addInMe, TreeNode clicked)
{

    for (int i = 0; i < directoryInfo.Length; i++)
    {              
        var files = directoryInfo[i].GetFiles();
        int length = files.Length;
        clicked.Nodes.Add(directoryInfo[i].Name);
        List<TreeNode> dateNode = new List<TreeNode>();
        string[] allDates = new string[length];

        try
        {
            for (int j = 0; j < length; j++)
            {
                allDates[j] = files[j].Name.Substring(11, 6);
            }

        }
        catch (Exception e)
        { }


        string[] date = allDates.Distinct().ToArray();

        for (int k = 0; k < date.Length; k++) //From here to the end
        {
            // is my code loading file
            dateNode.Add(clicked.Nodes.Add(date[i]));
            for (int j = 0; j < length; j++)
            {
                // curNode.Nodes.Add(file.FullName, file.Name);               

                if (files[j].Name.Substring(11, 6) == date[i])
                {
                    dateNode[i].Nodes.Add(files[j].FullName, files[j].Name);
                }

            }

        }

        foreach (DirectoryInfo subdir in directoryInfo[i].GetDirectories())
        {
            BuildTree(subdir, clicked.Nodes);
        }

    }

}

【问题讨论】:

  • 为什么要动态构建表单?您可以创建一个普通表单并使用它。
  • 嗯,我要添加的弹出图像是带有加载效果的gif,我认为这是合理的。

标签: c# winforms


【解决方案1】:

这取决于您的 BuildTree() 方法是否可以在后台线程上执行。 因为您正在传递对 TreeView 控件属性的引用,所以我假设该代码必须在 UI 线程上执行。 在这种情况下,我唯一可以建议您的是调用 Application.DoEvents();在 BuildTree() 方法中,例如在循环中。

此外,如果您在 BuildTree() 方法中进行任何长期调用以获取数据,则应在后台线程上进行这些调用并以任何方便的方式与 UI 同步。

[编辑] 我快速浏览了您的 BuildTree() 方法。我认为在这种情况下,最适合您的方法是:

  1. 声明额外的数据结构来保存填充 TreeView 所需的所有数据。
  2. 后台填写所有数据(可以使用BackgroundWorker) here is a good question 强调如何从 BackgroundWorker 获取结果
  3. 所有数据准备就绪后,创建所有节点并将其添加到 TreeView。这也需要一些时间,但必须在 UI 线程上完成。 为了保持 UI 响应,您可以在循环中调用 Application.DoEvents(),您将在其中将新节点添加到树中

treeView2_AfterSelect() 中的代码如下所示:

//...
f.Show();
BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += (ss, ee) =>
{
    ee.Result = PrepareData(directory); // prepare all data required to build the Tree
}

bw.RunWorkerCompleted += (ss, ee) => 
{
    BuildTreeByData(ee.Result, addInMe, clicked);
    f.Close();
}

bw.RunWorkerAsync();

【讨论】:

  • 您好 Woodman,感谢您的回复。我花了 2 天的时间研究背景工作者,才明白你的评论。你是对的,因为我的 BuildTree() 方法适用于 UI 线程,因为它应该加载文件并将它们显示在树视图上。如果我使用后台工作人员并将其放入 _DoWork 方法中,则会出现“跨线程操作无效”,我知道这是后台线程和 UI 线程之间的冲突。我非常相信您的建议“将后台线程与 UI 同步”将是解决方案。你能告诉我怎么做吗?我已经发布了我的 BuildTree() 方法。谢谢!
  • 感谢您的建议!我同意你的观点,将我的 BuildTree() 分成两部分,一部分用于 bw.Dowork,另一部分用于 bw.Runworkercompleted 是解决方案。经过几天的工作,我做到了。但是现在的问题是将dowork的结果传递给runworkercompleted。不幸的是,你的方法对我不起作用。 runworkercompleted 中的“ee”具有 RunWorkerCompletedEventArgs 类型,而 dowork 中的“ee”具有 DoWorkEventArgs 类型。一个简单的 convert.changetype 不起作用。从链接我不知道如何申请我的案子。你有什么主意吗?谢谢!
  • 好的,现在我解决了这个问题,方法是进行“List direct = (List)ee.Result” 之类的转换并在 runworkercompleted 中使用 direct。而已!再次感谢您的解决方案
【解决方案2】:

在调用 f.Show() 之前将此行添加到您的代码中:

f.Activated += (s, e) => BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);
// it's f.Shown for Windows Forms, Activated is for WPF

它将匿名事件处理程序订阅到您的 f 表单的 OnShow 事件。在这个处理程序中,您只需调用您的方法。

如果它不起作用,您将不得不在此处替换对 BuildTree 的调用以获取 BackgroundWorker call,因为您的 BuildTree 方法可能会访问 UI 控件,这是一个很大的禁忌。调用此代码而不是 f.ShowDialog:

BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += (ss, ee) =>
{
    f.ShowDialog(this);
    BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);
}

bw.RunWorkerCompleted += (ss, ee) => f.Close();
bw.RunWorkerAsync();

并像这样在 BuildTree 中更改对控件的每次访问和修改:

this.Invoke((MethodInvoker) (() =>
{
    // access you control here
}));

【讨论】:

  • 感谢您的回复。在尝试第二种方法之前,我会询问您提出的行,在我的程序中显示一条错误消息,说明 OnShow 不包含在 System.Windows.Forms.Form 中或参数不正确。不知道要不要在程序开头再加一条 using 语句?
  • 是的,这是一个错误,我认为您正在搜索 Shown 而不是 OnShow。
  • 是的,“显示”有效!然而结果很奇怪。如果我仍然使用 f.Show() 没有任何变化(f 表单和图片不显示),如果我使用 f.ShowDialog(),则当加载结束时,表单 f 和最初的图片不显示他们只是炫耀,不再消失。我想知道之后是否有办法关闭 f 。 (f.Close() 不起作用)
  • 更新了,试试我添加的关于BackgroundWorker的代码,告诉我。
  • 感谢后台工作人员方法。根据您的列表,我还没有弄清楚如何使用它。然而不幸的是,这个异常被抛出“跨线程操作无效:控件'treeView2'从创建它的线程以外的线程访问。”多线程的问题吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-15
  • 2012-03-15
  • 1970-01-01
相关资源
最近更新 更多