【问题标题】:Generalizing the interface to a progress bar in a GUI将界面概括为 GUI 中的进度条
【发布时间】:2010-02-25 01:50:59
【问题描述】:

我正在开发一个应用程序,它的主窗体底部有一个状态栏。状态栏包含一个进度条和一个标签,我想用它来向用户显示正在完成的某些工作的当前进度。状态栏还包含一个标签,我想像可点击的取消按钮一样使用它。

我之前已经创建过这样的异步接口,但它们始终基于单个操作,并使用后台工作程序。但是在这个新程序中,用户可能会调用许多不同的操作,我想使用同一个状态栏来显示所有这些操作的进度。所以我试图找出一种方法来概括和标准化状态栏中这些进度报告控件的接口。

在某些情况下,异步进程是使用 BackGroundWorker 创建的,但在其他情况下,我需要直接创建一个管理辅助线程。

这是我一直在思考的部分完成的骨架代码:

public partial class MyForm: System.Windows.Forms.Form
{
    ...SNIP...

    private void WorkProgressChanged(Object sender, EventArgs args)
    {
        ProgressChangedEventArgs backgroundWorkerArgs = args as ProgressChangedEventArgs;
        ProgressReport formReport = args as ProgressReport; //my own custom progress report class


       //tries to cast args to a Background worker args
        if (backgroundWorkerArgs != null)
        {
            // update UI based on backgroundWorkerArgs

        }
        else if (formReport != null)
        {
            // update UI basd on formReport

        }

        else 
        {
            //couldn't figure out what kind of progress report was sent
            //update UI based on args.ToString();

        }

    }


    private void cancelButtonToolStripLabel_Click(object sender, EventArgs e)
    {
        //calls cancel method of current processing
        if (this._currentWorkCancelAction != null)
        {
            _currentWorkCancelAction(); //envoke cancel requet
            cancelButtonToolStripLabel.Text = "Canceling"; //shows user that cancel request was made
            _currentWorkCancelAction = null; //disaccociates cancel button to prevent user from canceling twice
        }
    }


    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;


    }

....SNIP

}

所以状态栏是通过订阅 `WorkProgressChanged(Object sender, EventArgs args) 到某个事件来更新的 ',并最终在 'WorkProcessCompleted(Object sender, EventArgs args)' 被完成触发时重置。我的取消标签(按钮)还需要与委托方法关联并在稍后解除关联,该委托方法将请求取消当前正在进行的任何工作。

因此,每次工作完成时,都会发生很多事情。添加/删除事件订阅,更改委托引用等等。所以我开始想知道是否有某种方法可以将所有这些操作封装到一个或两个可重用的方法中,而不是为每个可能采取的操作编写重复的代码地方。

下面的InitWorkProcess() 方法显示了我认为这可能如何工作。虽然我很确定这不是使用 EventDescriptor 类的正确方法。我想不出任何其他方法来引用事件作为方法参数。也许这是不可能的?

    public void InitWorkProcess(EventDescriptor workProgressChangedEvent, EventDescriptor workCompletedEvent, System.Action requestCancel)
    { 
        //subscribe to progress changed
        workProgressChangedEvent.AddEventHandler(this, this.WorkProgressChanged);
        this._workProgressChangedEvent = workProgressChangedEvent;

        //subscribe to process completed
        workCompletedEvent.AddEventHandler(this, this.WorkProcessCompleted);
        this._workCompletedEvent = workCompletedEvent;


        //enable cancel button
        if (requestCancel != null)
        {
            cancelButtonToolStripLabel.Visible = true;
            this._currentWorkCancelAction = requestCancel;
        }
    }

...我会更改 WorkProgressComplete 事件处理方法以在工作完成时取消订阅事件关系。

    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;


        //unsubscribes WorkProcessCompleted() and WorkProgressChanged() methods
        this._workCompletedEvent.RemoveEventHandler(this, this._workCompletedEvent);
        this._workCompletedEvent = null;

        this._workProgressChangedEvent.RemoveEventHandler(this, this._workProgressChangedEvent);
        this._workProgressChangedEvent = null;
    }

有人对我应该如何设置有任何建议吗?我是否应该忘记 InitWorkProcess() 方法,而是为每个操作分别添加/删除所有事件/委托关系?还是完全有更好的方法?

【问题讨论】:

    标签: c# .net winforms multithreading asynchronous


    【解决方案1】:

    我认为进度条不应该直接订阅事件。

    它应该实现一个接口来公开进度条的所有相关功能,这可以处理跨线程调用,使控件更加线程安全。内部代码应该不知道任何外部类型,即封装。

    “接线”应该在控件的公共父级中,通过公共接口将事件映射到进度条上的更改。

    【讨论】:

      【解决方案2】:

      为什么不直接使用自定义线程中的 ProgressChangedEventArgs,并利用来自任何后台工作人员的公共事件对象...而不是尝试处理多种类型的进度报告对象?

      【讨论】:

      • 我不知道,好点。尽管我认为我最终会遇到同样的问题,即使我将其限制为仅 ProgressChangedEventArgs 也没有限制其 UserState 属性实际使用的类型。
      • 好吧,同样,您可以在 UserState 属性中使用一致的数据类型,并使用“as”关键字安全地转换为该类型。如果它为空,您可以将该事件作为未知进度事件忽略。否则,您可以对来自任何来源的所有进度事件使用单个实现。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      • 2017-02-04
      • 1970-01-01
      相关资源
      最近更新 更多