VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > VB.net教程 >
  • 在VB.Net WinForms应用程序中使用加载进度更新启动画面

Since .Net 2.0, Visual Basic has made it easy to create a splash screen and set it via the "Splash Screen" drop down in the Project Properties.  A splash screen set in this manner is automatically created, displayed and closed by the framework itself.  The default minimum display time is two seconds, and the splash will stay open longer if necessary until the main form has completely loaded.  All of this is well documented on MSDN, commonly known, and discussed pretty extensively in tutorials easily found on the internet.

从.Net 2.0开始,Visual Basic使得创建启动画面并通过项目属性中的“启动画面”下拉菜单变得容易。 以这种方式设置的启动屏幕由框架本身自动创建,显示和关闭。 默认的最小显示时间是两秒钟,如果需要,在主窗体完全加载之前,启动画面将保持打开状态的时间更长。 所有这些在MSDN上都有很好的记录,众所周知,并且在Internet上容易找到的教程中对此进行了广泛的讨论。

The "Splash Screen" Setting in Project Properties:

项目属性中的“启动画面”设置:

Splash Screen set via Project --> Properties

What isn't commonly found, though, are good examples of how to update the splash screen with progress information from the main form as it loads.  Many examples and tutorials simply use a static splash screen and leave it at that.  MSDN provides an example of how to "update the splash screen with status information" in the MSDN documentation of: My.Application.SplashScreen() property.

但是,通常找不到的很好的示例是如何在加载初始窗体时使用来自主窗体的进度信息来更新初始屏幕。 许多示例和教程仅使用静态启动画面,然后将其保留。 MSDN在My.Application.SplashScree的MSDN文档中提供了一个如何n()属性 。

In that example, the code is being run from the Application.Startup() event, and changes to the splash screen are done in a direct manner:

在该示例中,代码是从Application.Startup()事件运行的,并且对初始屏幕的更改以直接的方式完成:


  1.  
    Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
  2.  
    ' Get the splash screen.
  3.  
    Dim splash As SplashScreen1 = CType(My.Application.SplashScreen, SplashScreen1)
  4.  
    ' Display current status information.
  5.  
    splash.Status = "Current user: " & My.User.Name
  6.  
    End Sub

小心! 提前多线程... (Careful!  Multi-threading ahead... )

The code is misleading because it implies that we can do this from anywhere.  What isn't well documented or being made clear is that the splash screen and the main form of an application actually run in two different threads.  Attempts to use similar code from the Load() event of your main form will result in varying degrees of success depending upon your version of Visual Studio.  The code does demonstrate that "My.Application.SplashScreen" can be used to obtain a reference to the instance of the splash screen displayed automatically by the framework.  Well use that little nugget later...

该代码具有误导性,因为它暗示我们可以在任何地方进行此操作。 没有充分记录或弄清楚的是, 启动屏幕和应用程序的主要形式实际上在两个不同的线程中运行。 尝试使用主窗体的Load()事件中的类似代码将导致不同程度的成功,具体取决于您的Visual Studio版本。 该代码确实演示了“ My.Application.SplashScre en”可用于获取框架自动显示的初始屏幕实例的引用。请稍后使用该小块...

Since the splash screen and the main form are in two different threads, the correct approach to communicating between them is to use the Invoke() method with a delegate.  This is covered in the MSDN article, How to: Make Thread-Safe Calls to Windows Forms Controls.

由于初始屏幕和主窗体位于两个不同的线程中,因此在它们之间进行通信的正确方法是将Invoke()方法与委托一起使用。 MSDN文章“ 如何:对Windows窗体控件进行线程安全的调用”中对此进行了介绍。

Many don't even think to use this approach, though, since cross-thread communication is not normally an issue when dealing with two forms.  The splash screen, then, is an exception to the rule!

但是,许多人甚至都不考虑使用这种方法,因为在处理两种形式时,跨线程通信通常不是问题。 初始屏幕是规则的例外!

The pattern outlined in the article above is as follows:

上面的文章中概述的模式如下:


  1.  
    Delegate Sub SetTextCallback([text] As String)
  2.  
     
  3.  
    Private Sub SetText(ByVal [text] As String)
  4.  
    If Me.textBox1.InvokeRequired Then
  5.  
    Dim d As New SetTextCallback(AddressOf SetText)
  6.  
    Me.Invoke(d, New Object() {[text]})
  7.  
    Else
  8.  
    Me.textBox1.Text = [text]
  9.  
    End If
  10.  
    End Sub

We use InvokeRequired() to determine if the calling thread is different from the thread that created the control.  If yes, then we create an instance of the delegate that points to the same exact method.  Next we use Invoke() to run the delegate on the thread that created the control and pass the parameters using an array of Object.  This actually results in a recursive call since the delegate points to the same method.  On the second run InvokeRequired() will return false and the Else block will execute where the control can be safely updated.

我们使用InvokeRequired()确定调用线程是否与创建控件的线程不同。 如果是,那么我们将创建一个指向相同精确方法的委托实例。 接下来,我们使用Invoke()在创建控件的线程上运行委托,并使用Object数组传递参数。 由于委托指向相同的方法,因此实际上会导致递归调用。 在第二次运行时,InvokeRequired()将返回false,并在可以安全更新控件的位置执行Else块。

So let's apply the same pattern to a splash screen.  Below is a simple setup consisting of a Borderless Form with a BackgroundImage, Label and a ProgressBar:

因此,让我们将相同的模式应用于初始屏幕。 下面是一个简单的设置,该设置由无边距窗体以及BackgroundImage,Label和ProgressBar组成:

Splash Screen Form Setup

Here is the code for frmSplashScreen:

这是frmSplashScreen的代码:


  1.  
    Public Class frmSplashScreen
  2.  
     
  3.  
    Private Delegate Sub UpdateProgressDelegate(ByVal msg As String, ByVal percentage As Integer)
  4.  
     
  5.  
    Public Sub UpdateProgress(ByVal msg As String, ByVal percentage As Integer)
  6.  
    If Me.InvokeRequired Then
  7.  
    Me.Invoke(New UpdateProgressDelegate(AddressOf UpdateProgress), New Object() {msg, percentage})
  8.  
    Else
  9.  
    Me.Label1.Text = msg
  10.  
    If percentage >= Me.ProgressBar1.Minimum AndAlso percentage <= Me.ProgressBar1.Maximum Then
  11.  
    Me.ProgressBar1.Value = percentage
  12.  
    End If
  13.  
    End If
  14.  
    End Sub
  15.  
     
  16.  
    Private Sub frmSplashScreen_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
  17.  
    ' Draw a Black Border around the Borderless Form:
  18.  
    Dim rc As New Rectangle(0, 0, Me.ClientRectangle.Width - 1, Me.ClientRectangle.Height - 1)
  19.  
    e.Graphics.DrawRectangle(Pens.Black, rc)
  20.  
    End Sub
  21.  
     
  22.  
    End Class

Note that I'm using "Me" instead of a control name to check for InvokeRequired().  The "Me" in this case represents the Form itself and is valid since Forms also Inherit from Control.  All controls run in the same thread as the form that contains them so this is a clean and safe method of checking.  Also note that in the Else block I am updating both the Label and the ProgressBar at the same time.  You don't need a separate method with an accompanying delegate for every control; just check against the form and update all controls at once.  The delegate being used receives both parameters and the Object array contains both parameters passed to the method.

请注意,我使用的是“ Me”而不是控件名称来检查InvokeRequired()。 在这种情况下,“我”代表表单本身,并且由于表单也从控件继承而有效。 所有控件都与包含它们的窗体在同一线程中运行,因此这是一种干净安全的检查方法。 还要注意,在其他块中,我同时更新了Label和ProgressBar。 您无需为每个控件都提供带有委托的单独方法。 只需对照表格检查并立即更新所有控件。 使用的委托接收两个参数,并且Object数组包含传递给方法的两个参数。

With that code in place, all the main form has to do is call the UpdateProgress() method and the splash screen will take care of the rest.  Remember, a reference to the splash screen instance can be obtained with "My.Application.SplashScreen".  So here is an example Load() event for a main form that updates the splash screen with made up statuses:

有了该代码之后,所有主要形式要做的就是调用UpdateProgress()方法,初始屏幕将处理其余的工作。 请记住,可以通过“ My.Application.SplashScre获取对初始屏幕实例的引用。 因此,这是一个主窗体的示例Load()事件,该事件用已更新的状态更新初始屏幕:


  1.  
    Public Class Form1
  2.  
     
  3.  
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  4.  
    Dim splash As frmSplashScreen = CType(My.Application.SplashScreen, frmSplashScreen)
  5.  
    Dim MadeUpSteps() As String = {"Initializing...", "Authenticating...", "Retrieving Widgets...", "Loading Components...", "Updating Doomahickies..."}
  6.  
     
  7.  
    For i As Integer = 0 To MadeUpSteps.Length - 1
  8.  
    splash.UpdateProgress(MadeUpSteps(i), CInt((i + 1) / MadeUpSteps.Length * 100))
  9.  
    System.Threading.Thread.Sleep(1500)
  10.  
    Next
  11.  
    End Sub
  12.  
     
  13.  
    End Class

The Splash Screen in Action!

初始屏幕动起来!

Idle-Mind-346085.flv 空闲心理346085.flv

This article hasn't really introduced anything new or mind blowing...it just puts two and two together to accomplish something that really should be much simpler!

本文并没有真正介绍任何新东西或令人发指的东西……它只是将两个和两个放在一起来完成实际上应该更简单的事情!

翻译自: https://www.experts-exchange.com/articles/3671/Updating-a-Splash-Screen-with-Loading-Progress-in-a-VB-Net-WinForms-Application.html


相关教程