【问题标题】:Winforms Updating UI Asynchronously Pattern - Need to GeneralizeWinforms 异步更新 UI 模式 - 需要概括
【发布时间】:2011-04-04 16:08:42
【问题描述】:

设置:带有进度条和标签的主 MDI 表单。

主窗体中的代码。

    public delegate void UpdateMainProgressDelegate(string message, bool isProgressBarStopped);

            private void UpdateMainProgress(string message, bool isProgressBarStopped)
            {
                // make sure we are running on the right thread to be
                // updating this form's controls.
                if (InvokeRequired == false)
                {
                    // we are running on the right thread.  Have your way with me!
                    bsStatusMessage.Caption = message + " [ " + System.DateTime.Now.ToShortTimeString() + " ]";
                    progressBarStatus.Stopped = isProgressBarStopped;
                }
                else
                {
                    // we are running on the wrong thread.  
                    // Transfer control to the correct thread!                
                    Invoke(new ApplicationLevelValues.UpdateMainProgressDelegate(UpdateMainProgress), message, isProgressBarStopped);
                }
            }

子表单

private readonly ApplicationLevelValues.UpdateMainProgressDelegate _UpdateMainForm;
private void btnX_Click(object sender, EventArgs e)
        {
            _UpdateMainForm.BeginInvoke("StartA", false, null, null);
            try
            {
                if(UpdateOperationA())
                { _UpdateMainForm.BeginInvoke("CompletedA", true, null, null); }
                else
                { _UpdateMainForm.BeginInvoke("CanceledA", true, null, null); }
            }
            catch (System.Exception ex)
            {
                _UpdateMainForm.BeginInvoke("ErrorA", true, null, null);
                throw ex;
            }
        }

它工作得很好,但是对于 N 个按钮或 N 个操作,我必须一次又一次地编写相同的代码。有什么方法可以概括这一点或任何其他更好的方法来更新 UI。

【问题讨论】:

    标签: c# winforms user-interface asynchronous progress-bar


    【解决方案1】:

    如果您的操作都可以表示为单个委托类型,这实际上只是一个简单的重构问题。例如:

    private void RunOperationWithMainProgressFeedback(
        Func<bool> operation,
        string startMessage,
        string completionMessage,
        string cancellationMessage,
        string errorMessage)
    {
        this._UpdateMainForm.BeginInvoke(startMessage, false, null, null);
        try
        {
            if (operation.Invoke())
            {
                this._UpdateMainForm.BeginInvoke(completionMessage, true, null, null);
            }
            else
            {
                this._UpdateMainForm.BeginInvoke(cancellationMessage, true, null, null);
            }
        }
        catch (Exception)
        {
            this._UpdateMainForm.BeginInvoke(errorMessage, true, null, null);
            throw;
        }
    }
    
    private void btnX_Click(object sender, EventArgs e)
    {
        this.RunOperationWithMainProgressFeedback(
            this.UpdateOperationA,
            "StartA",
            "CompletedA",
            "CanceledA",
            "ErrorA");
    }
    

    虽然可以使用字典来存储参数值(如 VinayC 早期答案中所建议的那样),但这不是必需的。就我个人而言,出于可读性和性能方面的原因,我会避免使用它,但是 ymmv...

    【讨论】:

    • 这看起来好多了,如果我有任何事情发生,我会实施这种方法并更新!!...谢谢。
    【解决方案2】:

    您可以创建一个字典/列表,其中包含每个按钮的相关详细信息。这些详细信息将包括操作名称和调用操作的委托等。现在您可以为所有将查找字典以获取操作名称和要执行的操作的按钮设置通用事件处理程序。

    另一种方法是创建从按钮继承的自定义按钮,自定义按钮可以具有这些属性来保存上述详细信息。根据自己的喜好选择。

    【讨论】:

    • 有趣。对于第一种方法,我猜想通过存储在字典中的名称来调用方法。
    • 不是真的,您可以将委托本身存储在字典中。
    【解决方案3】:

    我厌倦了一遍又一遍地解决这个完全相同的问题,所以我编写了一组通用类来解决这个问题。基本思路是这样的:

    • 创建一个包含您关心的字段的抽象 Progress 对象,例如 statusMessage。

    • 创建一个抽象的 Job 对象。 Job 就像一个线程,但是除了一个 Main 和一个 Start 之外,它还有一个方法 UpdateProgress(Progress p)。 UpdateProgress 将当前进度复制到 p。它通常是唯一会获取锁的地方。

    • 在您的特定用法中,将 Progress 子类化并根据需要添加字段。然后继承 Job 来保存你想做的事情,并实现 UpdateProgress。

    • 在 UI 中,创建一个计时器,其主要调用作业。 UpdateProgress 然后更新 ui。

    通常,计时器对于 UI 更新来说已经足够了,采用 pull-and-sync-ui 模型比采用 push-changes 模型更简单。 Pull-and-sync-ui 避免了很多跨线程问题,避免了 UI 进入奇怪的状态。

    奖励:将 Progress 对象设为 INotifyProperyChanged 并进行数据绑定。 (这就是您可能不想使用 IDictionary 的原因)。

    【讨论】:

    • 似乎是一个相当大的方法.. 计时器、锁、线程.. 可以提供一些伪代码来清楚说明吗?
    猜你喜欢
    • 1970-01-01
    • 2015-06-04
    • 2017-03-26
    • 1970-01-01
    • 2018-12-01
    • 2014-02-16
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多