How to display a “loading” overlay on windows forms while the form is loading its controls (or updating them)?

后端 未结 6 919
生来不讨喜
生来不讨喜 2020-12-08 08:54

I\'m looking for an effective way to notify the user that a given form is currently loading (or updating) it\'s UI and it will take few seconds.

This may occurs at i

相关标签:
6条回答
  • 2020-12-08 09:01

    My recommended solution is to set the forms opacity to near invisible say 0.01 before initializing the components. Then create a form with the same size and position and place either a progress bar, or marquee on this form.. After the initialization of the main form, set it's opacity to full and dispose of the marquee form.

    0 讨论(0)
  • 2020-12-08 09:05

    You can create a transparent panel by subclassing S.W.F.Panel and overriding the CreateParams property:

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

    Override the OnPaint to add a semi transparent overlay:

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

    Set this panel on Dock.Fill on your form over the other controls. Hide it when loading ends.

    0 讨论(0)
  • 2020-12-08 09:07

    Use a ProgressBar with marquee or blocks style.

    http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbar.aspx

    0 讨论(0)
  • 2020-12-08 09:13

    Note that Winforms does not allow child controls to actually be transparent. As others have posted a separate transparent window is possible - but messy to manage.

    Cheap way

    • drag all controls into one panel and make it the size of the window (easy change)
    • When operating: Hide that panel. Use the panels .DrawToBitmap method to set form background image.
    • show progress bar, do work with 'doevents,' hide it.
    • clear background image, re-show panel.

    A Better way:

    Progress Class - Consumer

    I will give you a Usercontrol I wrote and have used in many different programs that does exactly what you want. Here is a trivial consumer example, you can paste into a form's code (yes, it just makes a bunch of new buttons for no reason):

    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
    

    Progress Class - Definition

    And here is the class. The 'designer' code is pasted inline, you can leave it there. The class disables controls when running so all you can do is cancel. It runs on the GUI thread. You can disable the cancel option. In the consumer is an example of dealing with newly added controls so they don't show up enabled, but get enabled when the progress is over.

    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.
        <System.Diagnostics.DebuggerNonUserCode()> _
        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.
        <System.Diagnostics.DebuggerStepThrough()> _
        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
    

    Good luck!

    0 讨论(0)
  • 2020-12-08 09:19

    You can disable all controls on the form by setting the Enabled property to False and then changing it back to True after the process is done.

    In addition you can have a hidden label that says "Loading" that you display before disabling the form and hide when re-enabling it.

    Finally, I would suggest that you split the process in two parts. One part that does the work without modifying controls that you can run on a worker thread and the part that changes the gui that does it work on the gui thread after the worker thread is done. This way you won't block the entire application, making changes to the Gui easier to do.

    0 讨论(0)
  • 2020-12-08 09:20

    Take a look at this post with a great answer that mimics the Ajax style on WinForms

    Javascript Like Modal Window for WinForms.
    Javascript Like Modal Window for WinForms

    Here is a custom Form that'll do what you want... alter to your taste:

    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
        /// <summary>
        /// Gets or Sets the main form that will be used as a background canvas for the loading form.
        /// </summary>
        public Form BackgroundForm { get; set; }
    
        /// <summary>
        /// Gets or Sets the text to displayed as the progress text.
        /// </summary>
        public string Title
        { 
            get
            {
                return label1.Text;
            }
    
            set
            {
                label1.Text = value;
            }
        }
    
        /// <summary>
        /// Gets or Sets the value of the progress bar.
        /// </summary>
        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;
                }
            }
        }
    
        /// <summary>
        /// Gets or Sets a value to indicate if the background form should be faded out.
        /// </summary>
        public bool UseFadedBackground { get; set; }
    
        /// <summary>
        /// Gets or Sets a value to indicate if the splash box is to be displayed.
        /// </summary>
        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
        /// <summary>
        /// Paints the background form as the background of this form, if one is defined.
        /// </summary>
        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;
        }
    
        /// <summary>
        /// Paints a shadow around the panel, if one is defined.
        /// </summary>
        /// <param name="g">The graphics object to paint into</param>
        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
    }
    
    0 讨论(0)
提交回复
热议问题