Windows Forms: Windows label doesn't render - c#

Similar to
MessageBox.Show("Test", "Test")
I have made a ProgressWindow, which is shown before a long-running action, and hidden thereafter:
ProgressWindow.Show("Test","Test")
Thread.Sleep(20000);
ProgressWindow.Hide();
using the following code:
class ProgressWindow : Form
{
private Label label1;
private static ProgressWindow window;
internal static void Show(string Message, string Caption)
{
window = new ProgressWindow(Message, Caption);
window.Show();
}
internal new static void Hide()
{
(ProgressWindow.window as Control).Hide();
}
private ProgressWindow(string Message, string Caption)
{
InitializeComponent(Message, Caption);
}
private void InitializeComponent(string Message, string Caption)
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.Location = new System.Drawing.Point(50, 40);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(300, 120);
this.label1.TabIndex = 0;
this.label1.Text = Message;
//
// ProgressWindow
//
this.ClientSize = new System.Drawing.Size(400, 200);
this.ShowIcon = false;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.ControlBox = false;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.StartPosition = FormStartPosition.CenterScreen;
this.Controls.Add(this.label1);
this.Name = "ProgressWindow";
this.Text = Caption;
this.TopMost = true;
this.ResumeLayout(false);
}
}
The problem now is that my progress window is shown, but in the position of the label, there is a white box only, and no text. Furthermore, if I try to click the window, the title changes from "Test" to "Test (not responding...)".
Why is that, and how would I change that?
I suspected an issue with thread blocking (but why? shouldn't the label be rendered?) and tried
internal static void Show(string Message, string Caption)
{
window = new ProgressWindow(Message, Caption);
new Thread(t => {
window.Show();
}).Start();
}
but this doesn't show the ProgressWindow form at all.

Yes, the problem is due to thread blocking. When Thread.Sleep() is called, the current thread is doing nothing, which means that no Windows messages are processed. This prevents your progress dialog from completely displaying its UI.
I'm not exactly sure why calling the Show() method on a background thread doesn't work, but I believe that WinForms requires the UI thread to be a Single Threaded Apartment, and by default threads are Multi-Threaded Apartment.
To implement this properly, I would suggested using the BackgroundWorker class. It automatically creates a background thread to perform the long running work on, and will fire an even once the work completes. You can use it something like this:
ProgressWindow.Show("Test","Test");
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
// perform your long running task here, this is a background thread
Thread.Sleep(2000);
};
worker.RunWorkerCompleted += (sender, args) => {
// update the UI here, this is running on the UI thread
ProgressWindow.Hide();
}
worker.RunWorkerAsync();
Note that RunWorkerAsync() will return right away, so your UI will need to handle the fact that the user can interact with the UI before the background task has finished.

Related

How to show a window on top of all windows of the same application

I have a SplashScreen that should be shown in front of all other windows in the application.
Since it is a SplashScreen, this cannot be modal dialog. Instead, this should be shown by mean of other thread.
I create the splash screen this way:
SplashScreenForm = new SplashScreen(mainForm);
// SplashScreenForm.TopMost = true;
And to show it, I am using this call, called from a different thread:
Application.Run(SplashScreenForm);
If I uncomment SplashScreenForm.TopMost = true, the splash is shown on top of other windows, even on top of windows belonging to different applications.
If you want to know how the thread is created:
public void ShowSplashScreen()
{
SplashScreenThread = new Thread(new ThreadStart(ShowForm));
SplashScreenThread.IsBackground = true;
SplashScreenThread.Name = "SplashScreenThread";
SplashScreenThread.Start();
}
private static void ShowForm()
{
Application.Run(SplashScreenForm);
}
How can I do it?
Something like:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splashThread = new Thread(new ThreadStart(
delegate
{
splashForm = new SplashForm();
Application.Run(splashForm);
}
));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
// Load main form and do lengthy operations
MainForm mainForm = new MainForm();
mainForm.Load += new EventHandler(mainForm_Load);
Application.Run(mainForm);
}
Then later after time-consuming operations are ended:
static void mainForm_Load(object sender, EventArgs e)
{
if (splashForm == null)
return;
splashForm.Invoke(new Action(splashForm.Close));
splashForm.Dispose();
splashForm = null;
}
This will start your splash screen before your main form and only dismiss it when the lengthy operations are completed in mainForm_Load.
You can try calling function SetForegroundWindow(HWND) from WinAPI :
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
// somewhere in the code :
SetForegroundWindow(SplashScreenForm.Handle);

Form with ProgressBar is Frozen while reading file (C# WinForms)

In my C# WinForms app, I have the MainForm in which I am using a BackgroundWorker to read a large Text file.
I am also using a second Form to display a Marquee ProgressBar to inform user that they must wait until file has been completely read.
The problem I am having is the Form with the progressbar (SimpleProgressBar) is frozen until the file is read. Where the BW should be on a separate thread to not let this happen.
I left the code that reads the file as it may be relevant to my problem. However all the code actually works its just that the Form to display the ProgressBar is frozen.
SimpleProgressBar.cs (Form)
//Simple Progress Bar set to Marquee
public partial class SimpleProgressBar : Form
{
public SimpleProgressBar()
{
InitializeComponent();
//ProgressBar is setup in the designer.
/*
System.Windows.Forms.ProgressBar progressBar1;
this.progressBar1.Location = new System.Drawing.Point(16, 65);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(350, 23);
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressBar1.TabIndex = 1;
*/
}
}
MainForm.cs (Form)
//Class variable
private SimpleProgressBar wait = new SimpleProgressBar();
private void generatePreview()
{
//Setup BW Thread
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//Start procesing file
worker.RunWorkerAsync();
//Show the dialog
wait.ShowDialog(); //perhaps .Show() would be better ?
}
//Once completed put the text into the textbox
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = ((StringBuilder)e.Result).ToString();
}
//Report progress here
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker)sender;
int bufferSize = 1024;
var sb = new StringBuilder();
var buffer = new Char[bufferSize];
var length = 0L;
var totalRead = 0L;
var count = bufferSize;
using (var sr = new StreamReader("c:\200mb_text_file.txt"))
{
if (bw.CancellationPending)
{
}
else
{
length = sr.BaseStream.Length;
while (count > 0)
{
count = sr.Read(buffer, 0, bufferSize);
sb.Append(buffer, 0, count);
totalRead += count;
}
}
}
e.Result = sb;
}
UPDATE
So essentially I wanted to create a generic 2nd Form to use a a progress indicator that can be used for multiple purposes.
However it looks like that the BW MUST be on the same thread that hold the UI elements that need updating.
ShowDialog shows a modal dialog that you'll have to explicitly close. When you call ShowDialog, the program will not continue to execute beyond that point. Your background worker's completion event will have to close the dialog.
In addition, you're reading 1,024 bytes at a time and then calling the progress event. Every call to the progress event requires marshaling to the UI thread. It takes approximately zero time to read 1,024 bytes, which means that the progress event is being called continually, which in turn means that the UI thread is nearly 100% occupied with your progress update.
If you really need to report progress as the thing is loading, then use a larger buffer. You'll get better read performance anyway with a 64 Kilobyte buffer. That is:
int bufferSize = 65536;
But your files must be huge. You should be able to read at least 50 megabytes per second unless you have a really slow disk or you're reading over a slow network.
Progress Bar needs to be updated in another Thread.Updating the progress bar from the UI thread will cause freezing issues. Just put the code to update the progres bar in the Backgroudnworker's DOWork Method rather than reporting it.
void worker_DoWork(object sender, DoWorkEventArgs e)
{
stuffdone()
progressBar1.PerformStep();
}
Before using this you will need to set Form.CheckForIllegalCrossThreadCalls = false; so that BW can access the UI

Threaded ticker control on top of Main thread control in wpf

I have requirement in which i need to show smooth scrolling text with some GIF Images and jpeg or mediaelement on a ticker. However, since this involves lot of CPU cycles for the main UI thread, i planned to create the ticker control on another thread with a dispatcher and then host this ticker on the form. However, i am getting a cross-thread exception that thread cannot access the control as another thread owns it.
I have done similar thing in Delphi, wherein i have set the ticker parent with SetWindowParent();
my code is as below
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TickerControlContainer loclContainer = new TickerControlContainer(this);
}
}
public class TickerControlContainer
{
private MainWindow f_Window;
private void CreateControl()
{
TickerControl loclControl = new TickerControl();
loclControl.InitializeComponent();
f_Window.Dispatcher.Invoke((MethodInvoker)delegate { AddControl(loclControl); });
}
private void AddControl(TickerControl piclControl)
{
f_Window.Content = piclControl;
**// exception occurs**
}
public TickerControlContainer(MainWindow piclWindow)
{
f_Window = piclWindow;
ManualResetEvent loclResetEvent = new ManualResetEvent(false);
Dispatcher loclDispatcher = null;
Thread th1 = new Thread(new ThreadStart(() =>
{
loclDispatcher = Dispatcher.CurrentDispatcher;
loclResetEvent.Set();
Dispatcher.Run();
}));
th1.SetApartmentState(ApartmentState.STA);
th1.Start();
loclResetEvent.WaitOne();
loclDispatcher.BeginInvoke((MethodInvoker)delegate { CreateControl(); });
}
}
Do i need to put a contentcontrol or something on my form, instead of setting as the content of the form.
This is just a sample that i am trying to do. Please help.
There's only one UI thread in WPF/.NET (though I think different windows can run on separate threads), so I don't really think there's an easy way to do what you're trying to do here.
Is it the animation that's taking up a lot of CPU, or are you doing a lot of processing in addition to the animation? If so, I would offload the calculations to a background thread and then invoke it to the UI thread when complete.
I was able to host control created on another thread on my main window, but before creating the control, the Window has to be shown atleast once.
using...
namespace WpfMultiDispatcherUpdates
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnCreateControl_Click(object sender, RoutedEventArgs e)
{
TickerControlContainer loclContainer = new TickerControlContainer(this);
}
}
public class TickerControlContainer
{
private MainWindow f_Window;
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
private static extern IntPtr SetParent(IntPtr hwndChild, IntPtr hwndParent);
private void CreateControl(HwndSource piclSource)
{
TickerControl loclControl = new TickerControl();
loclControl.InitializeComponent();
Window loclHostWindow = new Window();
loclHostWindow.WindowStyle = WindowStyle.None;
loclHostWindow.WindowState = WindowState.Normal;
loclHostWindow.Left = 0;
loclHostWindow.Top = 0;
loclHostWindow.ShowInTaskbar = false;
loclHostWindow.Content = loclControl;
loclHostWindow.ShowActivated = true;
loclControl.Height = 200;
loclControl.Width = (double)f_Window.Dispatcher.Invoke(new Func<double>(() => { return f_Window.Width; }));
piclSource.SizeToContent = SizeToContent.WidthAndHeight;
loclHostWindow.SizeToContent = SizeToContent.WidthAndHeight;
loclHostWindow.Show();
SetParent(new WindowInteropHelper(loclHostWindow).Handle, piclSource.Handle);
}
private void AddControl(TickerControl piclControl)
{
f_Window.Content = new ContentControl() { Content = piclControl };
}
public TickerControlContainer(MainWindow piclWindow)
{
f_Window = piclWindow;
ManualResetEvent loclResetEvent = new ManualResetEvent(false);
Dispatcher loclDispatcher = null;
Thread th1 = new Thread(new ThreadStart(() =>
{
loclDispatcher = Dispatcher.CurrentDispatcher;
loclResetEvent.Set();
try
{
Dispatcher.Run();
}
catch (Exception E)
{
System.Windows.MessageBox.Show(E.Message);
}
}));
th1.SetApartmentState(ApartmentState.STA);
th1.Start();
loclResetEvent.WaitOne();
try
{
HwndSourceParameters loclSourceParams = new HwndSourceParameters();
loclSourceParams.WindowStyle = 0x10000000 | 0x40000000;
loclSourceParams.SetSize((int)piclWindow.Width, 200);
loclSourceParams.SetPosition(0, 20);
loclSourceParams.UsesPerPixelOpacity = true;
loclSourceParams.ParentWindow = new WindowInteropHelper(piclWindow).Handle;
HwndSource loclSource = new HwndSource(loclSourceParams);
loclSource.CompositionTarget.BackgroundColor = Colors.Transparent;
loclDispatcher.BeginInvoke((MethodInvoker)delegate { CreateControl(loclSource); });
}
catch (Exception E)
{
System.Windows.MessageBox.Show(E.Message);
}
}
}
}
However, i need to add the Resize events and resize my control when the MainWindow height and width changes. The values are hardcoded for testing purposes. Now, the drawing on the child control does not affect my Main Window, but the complexity of controlling my child control is more..
The host should not have any other child controls in the area where we host this threaded control.

Is this code [theoretically] thread-unsafe?

I'm experiencing a strange deadlock in the code that I've written.
The idea is to implement an asynchronous operation whose Stop is synchronous -- the caller has to wait until it completes. I've simplified the part where real work is done to a simple property increment (++Value, see below); in reality though, a heavy COM component is invoked which is very sensitive to threads.
The deadlock I'm experiencing is in the Stop() method where I explicitly wait for a manual-reset event that identifies a completed operation.
Any ideas what I could have done wrong? The code should be self-contained and compilable on its own.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using ThreadingTimer = System.Threading.Timer;
namespace CS_ManualResetEvent
{
class AsyncOperation
{
ThreadingTimer myTimer; //!< Receives periodic ticks on a ThreadPool thread and dispatches background worker.
ManualResetEvent myBgWorkerShouldIterate; //!< Fired when background worker must run a subsequent iteration of its processing loop.
ManualResetEvent myBgWorkerCompleted; //!< Fired before the background worker routine exits.
BackgroundWorker myBg; //!< Executes a background tasks
int myIsRunning; //!< Nonzero if operation is active; otherwise, zero.
public AsyncOperation()
{
var aTimerCallbac = new TimerCallback(Handler_Timer_Tick);
myTimer = new ThreadingTimer(aTimerCallbac, null, Timeout.Infinite, 100);
myBg = new BackgroundWorker();
myBg.DoWork += new DoWorkEventHandler(Handler_BgWorker_DoWork);
myBgWorkerShouldIterate = new ManualResetEvent(false);
myBgWorkerCompleted = new ManualResetEvent(false);
}
public int Value { get; set; }
/// <summary>Begins an asynchronous operation.</summary>
public void Start()
{
Interlocked.Exchange(ref myIsRunning, 1);
myTimer.Change(0, 100);
myBg.RunWorkerAsync(null);
}
/// <summary>Stops the worker thread and waits until it finishes.</summary>
public void Stop()
{
Interlocked.Exchange(ref myIsRunning, 0);
myTimer.Change(-1, Timeout.Infinite);
// fire the event once more so that the background worker can finish
myBgWorkerShouldIterate.Set();
// Wait until the operation completes; DEADLOCK occurs HERE!!!
myBgWorkerCompleted.WaitOne();
// Restore the state of events so that we could possibly re-run an existing component.
myBgWorkerCompleted.Reset();
myBgWorkerShouldIterate.Reset();
}
void Handler_BgWorker_DoWork(object sender, EventArgs theArgs)
{
while (true)
{
myBgWorkerShouldIterate.WaitOne();
if (myIsRunning == 0)
{
//Thread.Sleep(5000); //What if it takes some noticeable time to finish?
myBgWorkerCompleted.Set();
break;
}
// pretend we're doing some valuable work
++Value;
// The event will be set back in Handler_Timer_Tick or when the background worker should finish
myBgWorkerShouldIterate.Reset();
}
// exit
}
/// <summary>Processes tick events from a timer on a dedicated (thread pool) thread.</summary>
void Handler_Timer_Tick(object state)
{
// Let the asynchronous operation run its course.
myBgWorkerShouldIterate.Set();
}
}
public partial class Form1 : Form
{
private AsyncOperation myRec;
private Button btnStart;
private Button btnStop;
public Form1()
{
InitializeComponent();
}
private void Handler_StartButton_Click(object sender, EventArgs e)
{
myRec = new AsyncOperation();
myRec.Start();
btnStart.Enabled = false;
btnStop.Enabled = true;
}
private void Handler_StopButton_Click(object sender, EventArgs e)
{
myRec.Stop();
// Display the result of the asynchronous operation.
MessageBox.Show (myRec.Value.ToString() );
btnStart.Enabled = true;
btnStop.Enabled = false;
}
private void InitializeComponent()
{
btnStart = new Button();
btnStop = new Button();
SuspendLayout();
// btnStart
btnStart.Location = new System.Drawing.Point(35, 16);
btnStart.Size = new System.Drawing.Size(97, 63);
btnStart.Text = "Start";
btnStart.Click += new System.EventHandler(Handler_StartButton_Click);
// btnStop
btnStop.Enabled = false;
btnStop.Location = new System.Drawing.Point(138, 16);
btnStop.Size = new System.Drawing.Size(103, 63);
btnStop.Text = "Stop";
btnStop.Click += new System.EventHandler(Handler_StopButton_Click);
// Form1
ClientSize = new System.Drawing.Size(284, 94);
Controls.Add(this.btnStop);
Controls.Add(this.btnStart);
Text = "Form1";
ResumeLayout(false);
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
It seems like all you're trying to do is have an asynchronous task that starts with the press of a button and stops when another button is pressed. Given that, you seem to be over-complicating the task. Consider using something designed for cancelling an asynchronous operation, such as a CancellationToken. The async task simply needs to check the cancellation token's status in the while loop (as opposed to while(true)) and the stop method becomes as simple as calling cancel on the CancellationTokenSource.
private CancellationTokenSource cancellationSource;
private Task asyncOperationCompleted;
private void button1_Click(object sender, EventArgs e)
{
//cancel previously running operation before starting a new one
if (cancellationSource != null)
{
cancellationSource.Cancel();
}
else //take out else if you want to restart here when `start` is pressed twice.
{
cancellationSource = new CancellationTokenSource();
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
asyncOperationCompleted = tcs.Task;
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (_, args) => DoWork(bgw, cancellationSource);
bgw.ProgressChanged += (_, args) => label1.Text = args.ProgressPercentage.ToString();
bgw.WorkerReportsProgress = true;
bgw.RunWorkerCompleted += (_, args) => tcs.SetResult(true);
bgw.RunWorkerAsync();
}
}
private void DoWork(BackgroundWorker bgw, CancellationTokenSource cancellationSource)
{
int i = 0;
while (!cancellationSource.IsCancellationRequested)
{
Thread.Sleep(1000);//placeholder for real work
bgw.ReportProgress(i++);
}
}
private void StopAndWaitOnBackgroundTask()
{
if (cancellationSource != null)
{
cancellationSource.Cancel();
cancellationSource = null;
asyncOperationCompleted.Wait();
}
}
put this code under ++Value; in Handler_BgWorker_DoWork. Then press the button when you see the output in debug window. Deadlock occurs then.
int i = 0;
while (i++ < 100) {
System.Diagnostics.Debug.Print("Press the button now");
Thread.Sleep(300);
Application.DoEvents();
}

Wait window in a windows application

I basically need to show a wait window to the user. For this i have put two seperate window forms in the application. the first form is the main form with a button. The second one is a empty one with just a label text. On click of the button in Form1 i do the below
Form2 f = new Form2();
f.Show();
Thread.Sleep(2000);
f.Close();
My idea here is to show the wait window to the user for 2 second. But when i do this the Form 2 is not completely loaded because of which the label in it is blank. Please let me know your inputs on this.
That's because you probably do some lengthy operation in the same thread (UI thread). You should execute your code in a new thread (see Thread class) or at least call Application.DoEvents periodically from inside your lengthy operation to update the UI.
Here is a Waiting Box class I use. Here is how you use it:
using WaitingBox;
ShowWaitingBox waiting = new ShowWaitingBox("Title Text","Some Text so the user knows whats going on..");
waiting.Start();
//do something that takes a while
waiting.Stop();
Here is the code for WaitingBox:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WaitingBox
{
public class ShowWaitingBox
{
private class WaitingForm:Form
{
public WaitingForm()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Controls.Add(this.progressBar1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(492, 155);
this.tableLayoutPanel1.TabIndex = 0;
//
// label1
//
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(209, 92);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(73, 13);
this.label1.TabIndex = 3;
this.label1.Text = "Please Wait...";
//
// progressBar1
//
this.progressBar1.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
this.progressBar1.Location = new System.Drawing.Point(22, 37);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(447, 23);
this.progressBar1.TabIndex = 2;
//
// WaitingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(492, 155);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "WaitingForm";
this.Text = "Working in the background";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WaitingForm_FormClosing);
this.Load += new System.EventHandler(this.WaitingForm_Load);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label label1;
private void WaitingForm_Load(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
this.BringToFront();
this.CenterToScreen();
}
private void WaitingForm_FormClosing(object sender, FormClosingEventArgs e)
{
}
internal void SetLabel(string p)
{
label1.Text = p;
}
}
private WaitingForm wf = new WaitingForm();
private string title, text;
private Thread waiting;
public bool IsAlive
{
get
{
return waiting.IsAlive;
}
set { }
}
public ShowWaitingBox(string Title, string Text)
{
this.title = string.IsNullOrEmpty(Title) ? "Working in the Background..": Title;
this.text = string.IsNullOrEmpty(Text) ? "Please wait..." : Text;
waiting = new Thread(new ThreadStart(Show));
}
public ShowWaitingBox()
{
waiting = new Thread(new ThreadStart(Show));
}
private void Show()
{
wf.Show();
wf.Text = this.title;
wf.SetLabel(this.text);
while (true) {
wf.Refresh();
System.Threading.Thread.Sleep(50);
}
}
public void Start()
{
waiting.Start();
}
public void Stop()
{
waiting.Abort();
}
}
}
When yo use Thread.Sleep you will disable the windows message loop and prevent the window from painting itself.
You could force a repaint:
f.Refresh();
Or better yet use a timer with a callback.
Timer t = new Timer();
t.Interval = 2000;
t.Tick += delegate { Close(); t.Stop();};
t.Start();
To prevent users from clicking in the original window you can open the new form as a dialog:
f.ShowDialog();
You're basically blocking the UI thread.
I suggest that instead, you make your Form2 constructor (or possibly Load event handler) start a timer which will fire two seconds later. When the timer fires, close the form. During those two seconds, the UI thread will be free, so everything will display properly and the user will be able to move the window etc.
I think you should just use
f.ShowDialog(this);
Then control is returned when f is closed.
By using the sleep, you are blocking the UI thread from updating for 2 seconds. (The thread is asleep).
You can (an always should for UI threads) use Thread.Current.Join(2000) instead of Thread.Sleep(2000).

Categories

Resources