代码之家  ›  专栏  ›  技术社区  ›  Brian Lyttle

Compact Framework/Threading-选择选项后,消息框显示在其他控件上

  •  4
  • Brian Lyttle  · 技术社区  · 16 年前

    我正在开发一个从外部服务器获取并安装大量更新的应用程序,需要一些线程方面的帮助。用户遵循此过程:

    • 点击按钮
    • 方法检查更新,返回count。
    • 如果大于0,则询问用户是否要使用messagebox.show()安装。
    • 如果是,它将运行一个循环,并对每个更新的run()方法调用beginInvoke(),以便在后台运行它。
    • 我的更新类有一些用于更新进度条等的事件。

    进度条更新很好,但消息框没有从屏幕上完全清除,因为更新循环在用户单击“是”后立即开始(请参见下面的屏幕截图)。

    • 在更新循环开始之前,我应该如何使消息框立即消失?
    • 我应该使用线程而不是beginInvoke()?
    • 我应该在一个单独的线程上执行初始更新检查并从该线程调用messagebox.show()吗?

    代码

    // Button clicked event handler code...
    DialogResult dlgRes = MessageBox.Show(
        string.Format("There are {0} updates available.\n\nInstall these now?", 
        um2.Updates.Count), "Updates Available", 
        MessageBoxButtons.YesNo, 
        MessageBoxIcon.Question, 
        MessageBoxDefaultButton.Button2
    );
    
    if (dlgRes == DialogResult.Yes)
    {
        ProcessAllUpdates(um2); 
    }
    
    // Processes a bunch of items in a loop
    private void ProcessAllUpdates(UpdateManager2 um2)
    {
        for (int i = 0; i < um2.Updates.Count; i++)
        {
            Update2 update = um2.Updates[i];
    
            ProcessSingleUpdate(update);
    
            int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);
    
            UpdateOverallProgress(percentComplete);
        }
    }
    
    // Process a single update with IAsyncResult
    private void ProcessSingleUpdate(Update2 update)
    {
        update.Action.OnStart += Action_OnStart;
        update.Action.OnProgress += Action_OnProgress;
        update.Action.OnCompletion += Action_OnCompletion;
    
        //synchronous
        //update.Action.Run();
    
        // async
        IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
    }
    

    截图

    Windows Mobile Bug

    3 回复  |  直到 9 年前
        1
  •  6
  •   John Sibly    16 年前

    您的用户界面没有更新,因为所有的工作都发生在用户界面线程中。 你的电话:

    this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); }) 
    

    在创建“this”(您的窗体)的线程(即用户界面线程)上说invoke update.action.run()。

    Application.DoEvents()
    

    确实会给UI线程重新绘制屏幕的机会,但我会尝试创建新的委托,并在此基础上调用BeginInvoke。

    这将在从线程池分配的独立线程上执行update.action.run()函数。然后,可以继续检查IAsyncResult,直到更新完成,在每次检查之后查询更新对象的进度(因为不能让其他线程更新进度条/ui),然后调用application.doEvents()。

    之后还应该调用endinvoke(),否则可能导致资源泄漏。

    我也会尝试在进度对话框上放置一个取消按钮,并添加一个超时,否则如果更新被卡住(或花费太长时间),那么您的应用程序将永远锁定。

        2
  •  1
  •   JamesSugrue    16 年前

    你试过把

    Application.DoEvents()
    

    在这里

    if (dlgRes == DialogResult.Yes)
    {
       Application.DoEvents(); 
       ProcessAllUpdates(um2); 
    }
    
        3
  •  1
  •   Quibblesome    16 年前

    @ John Sibly

    You can get away with not calling EndInvoke when dealing with WinForms without any negative consequences.

    我所知道的唯一一个记录在案的例外是在Windows窗体中,在Windows窗体中,正式允许您调用Control.BeginInvoke,而不必费心调用Control.EndInvoke。

    但是,在处理开始/结束异步模式的所有其他情况下,您应该假设它会泄漏,如您所述。