如何在窗体加载其控件(或更新它们)时在Windows窗体上显示“加载”叠加?

我正在寻找一种有效的方法来通知用户某个表单当前正在加载(或更新)它的UI,这将需要几秒钟。

这可能发生在初始加载或更新时。 由于它是非常密集和修改ui控件,这必须在ui线程上完成,因此阻止用户。

没有改变光标,我希望获得与ajax页面类似的效果,整个区域由半透明面板覆盖,中间有动画齿轮。

你有没有做过类似的事情? 或者你知道我应该咨询的有趣网站吗?

非常感谢

看看这篇文章的一个很好的答案,模仿WinForms上的Ajax风格

Javascript像WinForms的模态窗口。
Javascript像WinForms的模态窗口

这是一个自定义表格,可以做你想要的……改变你的口味:

public partial class ModalLoadingUI : Form { #region Constants private readonly Color BackgroundFadeColor = Color.FromArgb(50, Color.Black); #endregion #region Constructors public ModalLoadingUI() { InitializeComponent(); } #endregion #region Properties ///  /// Gets or Sets the main form that will be used as a background canvas for the loading form. ///  public Form BackgroundForm { get; set; } ///  /// Gets or Sets the text to displayed as the progress text. ///  public string Title { get { return label1.Text; } set { label1.Text = value; } } ///  /// Gets or Sets the value of the progress bar. ///  public int? Progress { get { if (progressBar1.Style == ProgressBarStyle.Marquee) { return null; } else { return progressBar1.Value; } } set { if (value == null) { progressBar1.Style = ProgressBarStyle.Marquee; progressBar1.Value = 100; label2.Visible = false; } else { progressBar1.Style = ProgressBarStyle.Continuous; progressBar1.Value = value.Value; label2.Text = string.Format("{0}%", value); label2.Visible = true; } } } ///  /// Gets or Sets a value to indicate if the background form should be faded out. ///  public bool UseFadedBackground { get; set; } ///  /// Gets or Sets a value to indicate if the splash box is to be displayed. ///  public bool UseSplashBox { get { return picShadow.Visible; } set { if (value == true) { picShadow.Visible = true; panel1.Visible = true; } else { picShadow.Visible = false; panel1.Visible = false; } } } #endregion #region Base Events private void ModalLoadingUI_Load(object sender, EventArgs e) { if (this.BackgroundForm != null) { this.Location = this.BackgroundForm.Location; } } private void ModalLoadingUI_VisibleChanged(object sender, EventArgs e) { if (this.Visible == true) { if (this.BackgroundForm != null) { this.Location = this.BackgroundForm.Location; } } if (System.Diagnostics.Debugger.IsAttached == true) { this.TopMost = false; } else { this.TopMost = true; } } private void ModalLoadingUI_Shown(object sender, EventArgs e) { } #endregion #region Public Methods ///  /// Paints the background form as the background of this form, if one is defined. ///  public void CaptureBackgroundForm() { if (this.InvokeRequired) { this.BeginInvoke(new MethodInvoker(CaptureBackgroundForm)); return; } if (this.BackgroundForm == null) { return; } var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmpScreenshot); try { // COPY BACKGROUND int x = this.BackgroundForm.Left; int y = this.BackgroundForm.Top; var size = this.BackgroundForm.Size; g.CopyFromScreen(x, y, 0, 0, size, CopyPixelOperation.SourceCopy); // FADE IF DESIRED if (this.UseFadedBackground == true) { var rect = new Rectangle(0, 0, size.Width, size.Height); g.FillRectangle(new SolidBrush(BackgroundFadeColor), rect); } // PAINT SPLASH BOX SHADOW IF DESIRED if(this.UseSplashBox == true) { PaintPanelShadow(g); } } catch (Exception e) { g.Clear(Color.White); } this.BackgroundImage = bmpScreenshot; } ///  /// Paints a shadow around the panel, if one is defined. ///  /// The graphics object to paint into private void PaintPanelShadow(Graphics g) { var shadowImage = picShadow.Image; var x = panel1.Left + (panel1.Width / 2) - (shadowImage.Width / 2); var y = panel1.Top + (panel1.Height / 2) - (shadowImage.Height / 2); g.DrawImage(shadowImage, x, y, shadowImage.Width, shadowImage.Height); } #endregion } 

您可以通过inheritanceSWFPanel并重写CreateParams属性来创建透明面板:

 protected override CreateParams CreateParams { get { CreateParams createParams = base.CreateParams; createParams.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT return createParams; } } 

覆盖OnPaint以添加半透明叠加层:

 protected override void OnPaint(PaintEventArgs e) { e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, 0,0,0)), this.ClientRectangle); } 

将此面板设置在表单上的Dock.Fill上,而不是其他控件上。 加载结束时隐藏它。

请注意,Winforms不允许子控件实际上是透明的。 正如其他人已经发布了一个单独的透明窗口是可能的 – 但管理混乱。

便宜的方式

  • 将所有控件拖动到一个面板中并使其成为窗口的大小(易于更改)
  • 操作时:隐藏该面板。 使用面板.DrawToBitmap方法设置表单背景图像。
  • 显示进度条,使用’doevents’,隐藏它。
  • 清晰的背景图像,重新显示面板。

更好的方法:

进步类 – 消费者

我会给你一个我写过的用户控件,并且已经在许多不同的程序中使用了你想要的。 这是一个简单的消费者示例,您可以粘贴到表单的代码中(是的,它只是无缘无故地创建了一堆新按钮):

 Public Class Form1 Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed End ''// use a flag if you would like a more graceful way to handle this. End Sub WithEvents ucProgress As New Progress ''// just doing it this way so I don''//t have to paste designer code. Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown Controls.Clear() Controls.Add(ucProgress) Me.ucProgress.pb.Visible = False ucProgress.StartProgress() Try ucProgress.Message = "Starting up..." Application.DoEvents() Me.ucProgress.pb.Visible = True Me.ucProgress.pb.Maximum = 21 Me.ucProgress.pb.Value = 0 For i As Integer = 0 To 20 Dim btn As New Button btn.Top = +i * 3 btn.Left = i * 8 btn.Text = CStr(i) btn.Enabled = False ''// ONLY HAVE TO DO FOR CTLS RIGHT ON MAIN FORM ucProgress.EnabledStates.Add(btn, True) ''// ONLY HAVE TO DO FOR CTLS RIGHT ON MAIN FORM Controls.Add(btn) btn.BringToFront() System.Threading.Thread.Sleep(200) Application.DoEvents() ucProgress.pb.Value += 1 ucProgress.Message = "Processing item# " & i.ToString If Me.ucProgress.Cancel Then MsgBox("Cancelled - not all loaded.") Me.ucProgress.Cancel = False Exit For End If Next Catch ex As Exception MsgBox(ex.ToString, , "Error loading something") Finally ucProgress.EndProgress() End Try End Sub End Class 

进步类 – 定义

这是class级。 “设计师”代码是内嵌粘贴的,您可以将其保留在那里。 该类在运行时禁用控件,因此您只能取消。 它在GUI线程上运行。 您可以禁用取消选项。 在使用者中是一个处理新添加的控件的示例,因此它们不会显示启用,但在进度结束时会启用。

 Option Explicit On Option Strict On Public Class Progress Inherits System.Windows.Forms.UserControl #Region "Code for the Designer.vb class" Sub New() InitializeComponent() End Sub ''//Form overrides dispose to clean up the component list.  _ Protected Overrides Sub Dispose(ByVal disposing As Boolean) If disposing AndAlso components IsNot Nothing Then components.Dispose() End If MyBase.Dispose(disposing) End Sub ''//Required by the Windows Form Designer Private components As System.ComponentModel.IContainer ''//NOTE: The following procedure is required by the Windows Form Designer ''//It can be modified using the Windows Form Designer. ''//Do not modify it using the code editor.  _ Private Sub InitializeComponent() Me.components = New System.ComponentModel.Container Me.btnCancel = New System.Windows.Forms.Button Me.lblPlaceholder = New System.Windows.Forms.Label Me.pb = New System.Windows.Forms.ProgressBar Me.SuspendLayout() ''// ''//btnCancel ''// Me.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Top Me.btnCancel.Location = New System.Drawing.Point(73, 33) Me.btnCancel.Name = "btnCancel" Me.btnCancel.Size = New System.Drawing.Size(91, 21) Me.btnCancel.TabIndex = 0 Me.btnCancel.Text = "Cancel" Me.btnCancel.UseVisualStyleBackColor = True ''// ''// ''//lblPlaceholder ''// Me.lblPlaceholder.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) Me.lblPlaceholder.BackColor = System.Drawing.Color.Transparent Me.lblPlaceholder.Font = New System.Drawing.Font("Arial Narrow", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte)) Me.lblPlaceholder.Location = New System.Drawing.Point(12, 3) Me.lblPlaceholder.Name = "lblPlaceholder" Me.lblPlaceholder.Size = New System.Drawing.Size(221, 29) Me.lblPlaceholder.TabIndex = 1 Me.lblPlaceholder.Text = "Placeholder label for text drawing" Me.lblPlaceholder.Visible = False ''// ''//pb ''// Me.pb.Anchor = CType(((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left) _ Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) Me.pb.Location = New System.Drawing.Point(6, 60) Me.pb.Name = "pb" Me.pb.Size = New System.Drawing.Size(225, 10) Me.pb.Style = System.Windows.Forms.ProgressBarStyle.Continuous Me.pb.TabIndex = 2 ''// ''//ucProgress ''// Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.BackColor = System.Drawing.Color.LightSteelBlue Me.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle Me.Controls.Add(Me.pb) Me.Controls.Add(Me.lblPlaceholder) Me.Controls.Add(Me.btnCancel) Me.Name = "ucProgress" Me.Size = New System.Drawing.Size(236, 77) Me.ResumeLayout(False) End Sub Friend WithEvents btnCancel As System.Windows.Forms.Button Friend WithEvents lblPlaceholder As System.Windows.Forms.Label Public WithEvents pb As System.Windows.Forms.ProgressBar #End Region Dim _mymessage As String Public Event WorkerPart() Public Cancel As Boolean Public EnabledStates As New Dictionary(Of Control, Boolean) Dim oldfocus As Control Dim OldMinBox As Boolean Public Sub StartProgress() Cancel = False Me.Parent = Me.ParentForm oldfocus = Me.ParentForm.ActiveControl Parent_SizeChanged(Nothing, Nothing) AddHandler Me.ParentForm.SizeChanged, AddressOf Parent_SizeChanged Me.Visible = True Me.Enabled = True Me.btnCancel.Focus() EnabledStates.Clear() For Each ctl As Control In Me.Parent.Controls If ctl IsNot Me Then EnabledStates.Add(ctl, ctl.Enabled) ctl.Enabled = False End If Next Me.BringToFront() Me.pb.Value = 0 OldMinBox = Me.ParentForm.MinimizeBox Me.ParentForm.MinimizeBox = True End Sub Public Sub EndProgress() RemoveHandler Me.ParentForm.SizeChanged, AddressOf Parent_SizeChanged For Each ctl As Control In Me.Parent.Controls If ctl IsNot Me And EnabledStates.ContainsKey(ctl) Then ctl.Enabled = EnabledStates(ctl) End If Next If oldfocus IsNot Nothing Then oldfocus.Focus() End If Me.ParentForm.MinimizeBox = OldMinBox Me.Visible = False End Sub Public Property Message() As String Get Return _mymessage End Get Set(ByVal value As String) _mymessage = value Dim g As Graphics = Me.CreateGraphics() DrawString(g) g.Dispose() ''//lblMessage.Text = value Application.DoEvents() End Set End Property Private Sub DrawString(ByVal g As Graphics) ''//g.TextRenderingHint = Drawing.Text.TextRenderingHint.SingleBitPerPixel Dim rct As New Rectangle(Me.lblPlaceholder.Left, Me.lblPlaceholder.Top, _ Me.lblPlaceholder.Width, Me.lblPlaceholder.Height) g.SetClip(rct) Dim b As New SolidBrush(Me.BackColor) If Me.BackgroundImage Is Nothing Then g.FillRectangle(b, rct) Else g.DrawImage(Me.BackgroundImage, 0, 0) End If ''// With lblPlaceholder g.DrawString(_mymessage, .Font, Brushes.DarkBlue, .Left, _ .Top + CInt(IIf(InStr(_mymessage, vbCrLf) <> 0, 0, .Height \ 4))) End With End Sub Private Sub frmProgress_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint DrawString(e.Graphics) End Sub Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click Cancel = True End Sub Private Sub Parent_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Me.Left = (Me.Parent.Width - Me.Width) \ 2 Me.Top = (Me.Parent.Height - Me.Height) \ 2 End Sub End Class 

祝好运!

您可以通过将Enabled属性设置为False然后在完成该过程后将其更改回True来禁用窗体上的所有控件。

此外,您可以使用隐藏标签,在禁用表单之前显示“正在加载”,并在重新启用表单时隐藏。

最后,我建议你将这个过程分成两部分。 一部分是在没有修改可以在工作线程上运行的控件的情况下完成工作的部分,而是在工作线程完成后更改gui的部分,这部分工作在gui线程上。 这样您就不会阻止整个应用程序,使Gui的更改更容易。

我推荐的解决方案是在初始化组件之前将窗体不透明度设置为接近不可见的0.01。 然后创建一个具有相同大小和位置的表单,并在此表单上放置进度条或选取框。在初始化主窗体后,将其不透明度设置为full并处理选取框窗体。