Как отобразить "загрузочный" оверлей на формах Windows, когда форма загружает свои элементы управления (или обновляет их)?
Я ищу эффективный способ уведомления пользователя о том, что данная форма в данный момент загружает (или обновляет) свой интерфейс, и это займет несколько секунд.
Это может произойти при начальной загрузке или при обновлении. Так как он очень интенсивный и модифицирует элементы управления пользовательским интерфейсом, это должно быть сделано в потоке пользовательского интерфейса и, следовательно, блокирует пользователя.
Изменение курсора не достаточно, я хочу получить аналогичный эффект, чем на страницах Ajax, со всей областью, которая перекрыта полупрозрачной панелью с анимированным механизмом в центре.
Вы уже сделали что-то подобное? Или вы знаете интересные сайты, на которых я должен консультироваться?
большое спасибо
6 ответов
Посмотрите на этот пост с отличным ответом, который имитирует стиль Ajax на WinForms
Javascript, как модальное окно для WinForms.
Javascript Like Модальное окно для 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
}
Обратите внимание, что Winforms не позволяет дочерним элементам управления быть фактически прозрачными. Как и другие опубликовали, возможно отдельное прозрачное окно - но грязное управление.
Дешевый способ
- перетащите все элементы управления на одну панель и установите размер окна (удобное изменение)
- При работе: Скрыть эту панель. Используйте метод Panel.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
Удачи!
Вы можете создать прозрачную панель, создав подкласс 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 на вашей форме поверх других элементов управления. Скрыть это, когда загрузка заканчивается.
Вы можете отключить все элементы управления в форме, установив для свойства Enabled значение False, а затем вернув его в значение True после завершения процесса.
Кроме того, у вас может быть скрытая метка с надписью "Загрузка", которая отображается перед отключением формы, и скрывается при ее повторном включении.
Наконец, я хотел бы предложить вам разделить процесс на две части. Одна часть, которая выполняет работу без изменения элементов управления, которую вы можете запустить в рабочем потоке, и часть, которая изменяет графический интерфейс, который работает в потоке графического интерфейса после выполнения рабочего потока. Таким образом, вы не заблокируете все приложение, что облегчает внесение изменений в графический интерфейс.
Мое рекомендуемое решение - установить непрозрачность форм почти невидимой, скажем, 0,01, до инициализации компонентов. Затем создайте форму с тем же размером и положением и поместите в эту форму либо индикатор выполнения, либо область выделения. После инициализации основной формы установите ее непрозрачность на полную и избавьтесь от формы выделения.
Используйте ProgressBar со стилем марки или блоков.
http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbar.aspx