2010-08-27 29 views
4

设置:带进度条和标签的主MDI表单。Winforms更新用户界面异步模式 - 需要概括

代码主要形式。

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操作我不得不一次又一次地写相同的代码。有没有什么办法可以推广或更新用户界面的其他更好的方法。

回答

4

这真的只是如果你的操作都可以表示为一个单一的委托类型的简单重构问题。例如: -

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早期的回答表明),这是没有必要的。就个人而言,我会避免这两个可读性和性能方面的原因,但情况因人而异...

+0

这似乎好多了,我会实施这种方法,并更新如果我什么都疯狂!! ..谢谢。 – 2010-09-05 20:11:20

2

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

另一种方法是创建按钮,自定义按钮继承您的自定义按钮可以拥有这些特性守住细节。根据你的喜好选择。

+0

有趣。使用第一种方法,我猜想需要反射来通过存储在字典中的名称来调用方法。 – 2010-08-27 07:43:43

+0

不是,您可以将委托本身存储在字典中。 – VinayC 2010-08-27 08:14:42

1

我累了一遍又一遍解决这个确切的同样的问题,所以我写了一个类的一般设置为解决此问题。基本思想是这样的:

  • 创建一个带有你关心的字段的抽象Progress对象,比如statusMessage。

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

  • 在您的特定用途,子进步与根据需要添加字段。然后子类Job保存你想要做的事情,并实现UpdateProgress。

  • 在UI中,创建一个定时器,其主要电话的工作。 UpdateProgress然后更新ui。

定时器对于ui更新来说足够好,并且采用pull-and-sync-ui模型比实现push-changes模型更简单。 Pull-and-sync-ui避免了大量的交叉线程问题,并使UI进入怪异状态。

奖金:让你的进度条对象的INotifyProperyChanged,做数据绑定。 (这就是为什么你可能不想使用IDictionary)。

+0

看起来像一个相当沉重的方法..定时器,锁,线程..可以提供一些伪代码清晰? – 2010-09-03 10:45:45