[英]How to display a “loading” overlay on windows forms while the form is loading its controls (or updating them)?
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. 我正在寻找一种有效的方法来通知用户某个表单当前正在加载(或更新)它的UI,这将需要几秒钟。
This may occurs at initial load or at update. 这可能发生在初始加载或更新时。 Since it's very intensive and modifying ui controls, this has to be done on the ui thread, and therefore blocking the user. 由于它非常密集并且修改ui控件,因此必须在ui线程上完成,因此阻止用户。
Changing the cursor is not enought, I want to get a similar effect than on ajax pages, with the whole area that is overlayed by a semi transparent panel with an animated gear on the center. 没有改变光标,我希望获得与ajax页面相似的效果,整个区域由半透明面板覆盖,中间有动画齿轮。
Have you done something like that already? 你有没有做过类似的事情? Or do you know interesting websites I should consult? 或者你知道我应该咨询的有趣网站吗?
Thanks a lot 非常感谢
Take a look at this post with a great answer that mimics the Ajax style on WinForms 看看这篇文章的一个很好的答案,模仿WinForms上的Ajax风格
Javascript Like Modal Window for WinForms. Javascript像WinForms的模态窗口。
Javascript Like Modal Window for WinForms Javascript像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
}
You can create a transparent panel by subclassing SWFPanel and overriding the CreateParams property: 您可以通过继承SWFPanel并覆盖CreateParams属性来创建透明面板:
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: 覆盖OnPaint以添加半透明叠加层:
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. 将此面板设置在表单上的Dock.Fill上,而不是其他控件上。 Hide it when loading ends. 加载结束时隐藏它。
Note that Winforms does not allow child controls to actually be transparent. 请注意,Winforms不允许子控件实际上是透明的。 As others have posted a separate transparent window is possible - but messy to manage. 正如其他人发布了一个单独的透明窗口是可能的 - 但管理起来很麻烦。
I will give you a Usercontrol I wrote and have used in many different programs that does exactly what you want. 我会给你一个我写过的Usercontrol,并且已经在许多不同的程序中使用,它们完全符合你的要求。 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
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. 它在GUI线程上运行。 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! 祝好运!
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. 您可以通过将Enabled属性设置为False然后在完成该过程后将其更改回True来禁用窗体上的所有控件。
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. 一部分是在没有修改可以在工作线程上运行的控件的情况下完成工作的部分,而是在工作线程完成后更改gui的部分,这部分工作在gui线程上。 This way you won't block the entire application, making changes to the Gui easier to do. 这样您就不会阻止整个应用程序,使Gui的更改更容易。
My recommended solution is to set the forms opacity to near invisible say 0.01 before initializing the components. 我推荐的解决方案是在初始化组件之前将窗体不透明度设置为接近不可见的0.01。 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. 然后创建一个具有相同大小和位置的表单,并在此表单上放置进度条或选取框。在初始化主窗体后,将其不透明度设置为完全并处理选取框窗体。
Use a ProgressBar with marquee or blocks style. 使用带有选框或块样式的ProgressBar。
http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbar.aspx http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbar.aspx
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.