65.9K
CodeProject 正在变化。 阅读更多。
Home

外部或独立进度条

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2012 年 11 月 12 日

CPOL

7分钟阅读

viewsIcon

19555

downloadIcon

767

创建一个简单、灵活、独立的进度条,可以轻松地添加到任何类中。

下载源文件

下载演示项目

 

介绍 

这篇文章的诞生源于我一次漫长而沮丧的寻找,我本以为这是一件简单的事情。但事情总是这样开始的,不是吗?我只是想简单地将一个独立的进度条添加到某个类中,该类会执行一个耗时变化、有时很长的工作。我无法使用 Windows 窗体工具箱中的 `ProgressBar` 来获得良好的结果。我将不得不把大部分项目代码写在主窗体中,而这在很多层面上都是错误的。

我搜索了很长时间,找到了一篇出色的文章,提供了一个可能的解决方案。这篇文章的标题是:**“处理渐进式操作”**,作者是 _Erik_。它提供了我想要的解决方案,但对于我正在进行的简单项目来说,它太复杂了。我需要一些不那么繁琐的东西,而且很可能不是完全的面向对象,因为我不想涉及接口。他文章中的代码非常适合大型、复杂的项目,但不是这个小项目。它看起来会非常不成比例,因为项目中绝大部分代码都将处理在一个不属于主窗体的类中生成进度条的“简单问题”。

顺便说一句:我计划在更大型的项目中使用 Erik 文章中的内容,因为它非常适合这种情况。

需要解决的问题 

创建一个简单、灵活、独立的 `ProgressBar`,可以轻松地添加到任何类中。

概述 

我已尽力使这个项目尽可能具有即插即用性。毕竟,这确实是我最初想要的。所以,我设置了六个使其工作的相关文件。如果您愿意,可以跳到本文的结尾,直接按照说明将 StandAloneProgressBar 插入到您的项目中。我已经在我自己的几个不同项目上进行了尝试,以确保这些说明有效。当然,我无法预见所有情况,所以需要您根据需要对代码进行任何必要的调整。如果您认为我可以在文章中进行任何改进,请告诉我。顺便说一下,通过添加第二个 ProgressBar 控件和更多标签,您可以使其更加复杂。

我包含了一个小型的演示项目,它除了访问三个网站并访问它们的 RSS Feed 页面列表之外,没有做任何事情。我添加了 `StandAloneProgressBar` 窗体到项目中,以便在程序访问不同网站时显示进度条。在我的演示中,我访问了三个不同的网站,因为我的 PC 和连接速度非常快,否则我只能看到进度条一两秒钟,或者一个网站就足够了。顺便说一句,如果对此感兴趣,该演示还包含了一个非常有用的 **Linq to XML** 示例。

基本上,这归结为添加 `StandAloneProgressBar` 的窗体,并将三个文本文件的内容插入到您项目中的正确位置。

使用方法  

文件说明   

  • StandAloneProgressBar.cs - Windows 窗体类,是供其他类使用的进度条。当然,它包含两个附属文件:  
    • StandAloneProgressBar.Designer.cs
    • StandAloneProgressBar.resx
  • Property and Event Declarations.txt - 包含公共属性和事件声明的文本添加。
  • InitialValues.txt - 包含所有必需的变量初始化,并调用我们进度条的启动事件。
  • StartTheProgressBar.txt - 实例化我们的进度条并调用它。

StandAloneProgressBar.cs 

这是我们将从当前应用程序内部调用的 Windows 窗体类。它的图片在本文章的开头。它是一个非常简单的窗体,由两个 `Labels` 和一个 `ProgressBar` 控件组成。窗体就只有这些。

以下代码是我们使事件正常工作所需的所有内容。它包含了我们将要在使用 StandAloneProgressBar 的类中声明的三个事件的实际代码。Eric 做得非常出色。

public partial class StandAloneProgressBar : Form
    {
        public StandAloneProgressBar()
        {
            InitializeComponent();
 
            // Instantiate the class the StandAloneProgressBar is in so we can call its Methods 
            // and use its Public Properties and Events.
            RSSFeed myDemo = new RSSFeed();
 
            // This Event, we defined in the class RSSFeed, displays the properly
            // initialized ProgressBar with these values.
            myDemo.OperationStart += (sender, e) =>
                {
                    lblMainTitle.Text = myDemo.MainTitle;   // Updates the text for lblMainTitle.
                    lblSubTitle.Text = myDemo.SubTitle;     //  Updates the text for lblSubTitle.
                    pgbMainBar.Maximum = myDemo.TotalSteps;     // Divides the ProgressBar up into 
                                                                    // the number of TotalSteps.
                    pgbMainBar.Value = myDemo.CurrentProgress;   // This sets the "step" 
                                                                     // the ProgressBar draws.

                    Refresh();  // Forces the control to redraw itself.
                };
 
            // This Event, we defined in RSSFeed, is just updating the "step" of the ProgressBar.
            myDemo.OperationProgress += (sender, e) =>
                {
                    lblSubTitle.Text = "Visiting the RSS Feed page for " + myDemo.SubTitle;
                    pgbMainBar.Value = myDemo.CurrentProgress;  //  This sets the "step" 
                                                                    // the ProgressBar draws.

		  // Processes all Windows messages currently in the message queue.
                    Application.DoEvents();
                };
 
            // This Event, we defined in RSSFeed, terminates the ProgressBar.
            myDemo.OperationEnd += (sender, e) => Close();
 
            // Subscribe to the Shown event of the Form.
            // This actually calls the method in RSSFeed that we want the ProgressBar to show up for.
            Shown += (sender, e) => myDemo.GetRSSFeed();
 
        } // end method StandAloneProgressBar

    } // end class StandAloneProgressBar

Property and Event Declarations.txt 

这个文本文件包含了所有公共属性和事件声明。特别值得关注的是公共属性 `CurrentProgress`。它的写法是为了在调用类的代码中提供灵活性,而无需每次在新类中使用它时都要进行编辑。

#region Public Properties
        
        public string MainTitle { get; set; }  // Will be the text placed in lblMainTitle
        public string SubTitle { get; set; }   // Will be the text placed in lblSubTitle
                
        public int TotalSteps { get; set; }    // This is how many times the operation is performed.
					          // Divides the ProgressBar into equal segments of 
                                                  // this percentage size.
        public int CurrentStep { get; set; }  // Tracks current position of the ProgressBar.

        public int CurrentProgress            // This calculates the new current position of 
                                                 // the ProgressBar .
        {
            get
            {
                int ret = 0;
 
                if (TotalSteps > 0)
                {
                    double currentStep = CurrentStep;
                    double totalSteps = TotalSteps;
                                        
                    ret = Convert.ToInt32((currentStep / totalSteps) * totalSteps);
                }
 
                return ret;
            }
        }
 
        #endregion
                
        #region Define the Events
 
        // Shows the ProgressBar with the proper initial values.
        protected void OnOperationStart(EventArgs e)
        {
            if (OperationStart != null)
                OperationStart(this, e);
        }
 
        // Updates the bar position itself.
        protected void OnOperationProgress(EventArgs e)
        {
            if (OperationProgress != null)
                OperationProgress(this, e);
        }
 
        // Terminates the ProgressBar.
        protected void OnOperationEnd(EventArgs e)
        {
            if (OperationEnd != null)
                OperationEnd(this, e);
        }
 
        #endregion
 
        #region Declare the Events
 
        public event EventHandler OperationStart;   
        public event EventHandler OperationProgress; 
        public event EventHandler OperationEnd; 
 
        #endregion

StartTheProgressBar.txt  

这个文本文件只有几行代码,**必须替换**当前对使用 StandAloneProgessBar 的类的调用。从下面的内容可以看出,我已经捕获了旧调用被注释掉并被两行新代码替换的整个实例。

注意最后一行代码:`sp.ShowDialog();`。它指的是 `StandAloneProgressBar.cs` 中最后一行代码。在那里,您需要替换为调用您的进度条方法的实际调用。(参见下面的第 8 步)

	// Use this event to trigger the whole thing.
        private void button1_Click(object sender, EventArgs e)
        {
            
            //// Instantiates the class.  Allows us to make a call to our procedure.
            //RSSFeed myRSS = new RSSFeed();

            //// Calls the procedure we want to execute.
            //myRSS.GetRSSFeed();

            // Instantiate the ProgressBar object.
            StandAloneProgressBar sp = new StandAloneProgressBar();
 
            // This will bring up a blank invisible ProgressBar,
            // but it starts the whole process in StandAloneProgressBar.
            sp.ShowDialog();
        }

InitialValues.txt 

这四个变量是我们版本的进度条正常工作所需的值。请更改变量 `TotalSteps`!我有些尴尬地承认,我在测试演示时未能遵循这个指示。初始化之后是调用 StandAloneProgressBar 的 `Start Event` 的那一行。我把它放在那里是为了消除错过必需函数调用的可能性。

	    // Set initial values
            TotalSteps = 10;    // You need to CHANGE THIS to what you need.  Preferably a variable.
            CurrentStep = 0;
            MainTitle = "Testing the ProgressBar";
            SubTitle = "";
 
            // Show the ProgressBar with its initial conditions.
            OnOperationStart(EventArgs.Empty);

将 StandAloneProgressBar 插入项目步骤说明

      1) 将所有三个 `StandAloneProgressBar` 文件和三个文本文件复制到您当前的工作目录。
      2) 在解决方案资源管理器窗口中右键单击项目名称,然后选择“添加现有项…”
        选择您想要复制的所有文件,然后单击“确定”。
      3) 编辑需要我们的进度条工作的**类**。
        将 `Property and Event Declarations.txt` 文本文件中包含的所有文本复制到您需要进度条工作的类的开头。
      4) 编辑 `StandAloneProgressBar.cs` 代码。
        重构**命名空间**以匹配您当前项目的命名空间。
        查找并替换:需要使用此进度条的**类**的名称(在第 20 行或附近)。
          建议将 `myDemo` 的名称重构为更适合您项目的名称。
      5) 将 `InitialValues.txt` 的内容复制到使用此进度条的**方法**的开头。
        这其中也包含了初始化我们进度条的调用。
        将公共属性 `TotalSteps` 更改为您正确的计数。注意:这应该是一个变量。
        将公共属性 `MainTitle` 和 `SubTitle` 更改为您想要的文本。
      6) 在**重复过程**的合适位置添加这三行代码来更新我们进度条的进度:
        SubTitle = “(您想在子标题中显示的名称,或不显示)”;
        CurrentStep++;
        OnOperationProgress(EventArgs.Empty);
      7) 将这两行代码添加到使用我们进度条的方法的末尾。这将终止它。
        // 操作完成,终止进度条。
        OnOperationEnd(EventArgs.Empty);
      8) 在 `StandAloneProgressBar.cs` 中,代码的最后(第 47 行),以窗体的 `Shown Event` 开头,将调用的方法名称从 `mydemo.GetRSSFeed()` 更改为使用它的方法。在本例中的演示中,它是 `myDemo.GetRSSFeed()`。
      9) 注释掉当前对使用我们进度条的方法的调用,并在其下方复制 `StartTheProgressBar.txt` 的内容。这两行代码将替换该调用。
      10) 您已经准备好尝试了。

关注点

这是一个有趣的挑战,因为我不得不采用 Erik 精妙的代码,将其设置为可用于接口,并通过逆向工程来确定其核心本质。我也从他的代码中学到了很多。另外,我想再说一次,演示程序中有非常好的 **Linq to XML** 代码示例,用于提取 RSS Feed 页面的数据。它位于 `RSSFeed.cs` 文件中。

历史   

最终草稿提交于 2012/11/11。

© . All rights reserved.