繁体   English   中英

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

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

我正在寻找一种有效的方法来通知用户某个表单当前正在加载(或更新)它的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
    /// <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
}

您可以通过继承SWFPanel并覆盖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',隐藏它。
  • 清晰的背景图像,重新显示面板。

一个更好的方法:

进步类 - 消费者

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

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

进步类 - 定义

这是班级。 “设计师”代码是内嵌粘贴的,您可以将其留在那里。 该类在运行时禁用控件,因此您只能取消。 它在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.
    <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

祝好运!

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM