Progress bar form not fully loading - c#

I'm writing a program that can take quite a bit to load(5-30 seconds). I'm wanting to have a form that starts with a marquee style progress bar, so that the user knows there's something being done.
public FrmMain()
{
var loading = new FrmLoading();
loading.Show();
InitializeComponent();
// other start up code here
loading.Close()
}
Now, loading pops up just fine. The problem is that the label and the progress bar show as nothing, and it almost looks like they are crashing. I've looked into the BackgroundWorker, but, honestly, I don't fully comprehend how to make that work for me. I tried to follow the tutorials and examples at:
BackgroundWorker Basics in C#
C# BackgroundWorker
ProgressBar
BackgroundWorker Class
But my head just isn't grasping it. Is there any other way to get the form to load properly? Thanks for any and all help.
EDIT
My BackgroundWorker attempt included me creating a function which performed all the actions that were done in my FrmMain. So, I had:
public FrmMain()
{
Loading = new FrmLoading();
Loading.Show();
backgroundWorker1.RunWorkerAsync();
}
private void FormLoading()
{
// Initialize and loading elements of the form
InitializeComponent();
}
private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
FormLoading();
}
private void BackgroundWorker1RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Loading.Close();
}
But I kept getting NullReferenceException on the backgroundWorker1.RunWorkerAsync();

An little-known or often-forgotten framework library is Microsoft.VisualBasic. You can use it with C# apps. Just add a Reference ... and this class. It handles all background worker threading and the rest.
public class MyApplication : WindowsFormsApplicationBase
{
private static MyApplication _application;
public static void Run(Form form)
{
_application = new MyApplication{ MainForm = form };
_application.Run(Environment.GetCommandLineArgs());
}
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new MySplashScreenForm();
}
}
Finally, change the app launch under Program.Main() from:
Application.Run(new Form1());
to
MyApplication.Run(new Form1());
If you aren't familiar, this class also offers more features like Single-Instance Handling that are definitely worth a look.

Perhaps this will help:
Add a:
Shown += new EventHandler(Form1_Shown);
after your InitializeComponent();.
And add:
void Form1_Shown(object sender, EventArgs e)
{
Refresh();
}
Also, if you didn't subscribe to the BackgroundWorker1DoWork event handler in the Designer, you'll have to add:
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);

What I ended up doing may not be elegant, but it accomplished what I needed.
public FrmMain()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
var loading = new FrmLoading();
loading.ShowDialog();
}
private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
DbQuery();
}
private void BackgroundWorker1RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Loading.Close();
}
This brings up my loading form so that it has focus and fully loads. Since I called the DoWork before I called the ShowDialog(), the BackgroundWorker continues to work. Once DbQuery() is done, it calls the BackgroundWorker1RunWorkerCompleted(), which closes my FrmLoading and continues on with my program. Thanks to everyone who posted.

Related

EventWaitHandle blocking the entire form

I've been looking around for quite some time now, but without any solution..
What I want to achieve, is use an EventWaitHandle class in order to pause one thread.
So, I create two buttons on a form. The first one should send a message, then pause, and the second one should unpause the thread of the first button, which then sends another message. Like that:
using System;
using System.Windows.Forms;
using System.Threading;
namespace Application
{
public partial class Form1 : Form
{
EventWaitHandle wh = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Thread blocked!");
wh.WaitOne();
MessageBox.Show("Thread unblocked!");
}
private void button2_Click(object sender, EventArgs e)
{
wh.Set();
}
}
}
But as soon as the thread gets blocked with the wh.WaitOne(), I can't do anything on the entire form, including pushing the second button or at least closing it..
What did I do wrong? Because I can't seem to find any difference between examples I could find, and my code.
You have only 1 thread. The UI thread.
When you block it, you block the entire UI.
You'll have to create a second thread.
Try this:
private void button1_Click(object sender, EventArgs e)
{
new Thread() {
void run() {
MessageBox.Show("Thread blocked!");
wh.WaitOne();
MessageBox.Show("Thread unblocked!");
}
}.start();
}

C# Opening a Form, then closing it from another method

I am pretty new to C# and having a little issue with something. I believe threading may be the answer, but thats just a Buzz-Word I have picked up when looking for solutions.
namespace Test
{
public partial class Form1 : Form
{
private Form2 form2;
public Form1()
{
InitializeComponent();
form2 = new Form2();
}
private void runCheck(object source, System.Timers.ElapsedEventArgs e)
{
form2.ShowDialog();
form2.TopMost = true;
}
private void runCheckFalse()
{
form2.Hide();
}
}
This is only a quick code snippet of my stripped out application, however when trying this I get an error: Cross-thread operation not valid: Control 'Form2' accessed from a thread other than the thread it was created on.
Also as a side note I am using form2.TopMost = true; to attempt to open the window on top of everything else, but this often ends up at the back behind Visual Studio etc
You need to use Invoke in order to work with the form from a different thread.
Here is a nice article explaining how to work with Windows Forms controls from multiple threads: How to: Make Thread-Safe Calls to Windows Forms Controls
Try this:
namespace Test
{
public partial class Form1 : Form
{
private Form2 form2;
public Form1()
{
InitializeComponent();
form2 = new Form2();
}
private void runCheck(object source, System.Timers.ElapsedEventArgs e)
{
if (form2.InvokeRequired)
{
form2.Invoke(new EventHandler(delegate { form2.ShowDialog(); form2.TopMost = true; }));
}
else
{
form2.ShowDialog();
form2.TopMost = true;
}
}
private void runCheckFalse()
{
if(form2.InvokeRequired)
{
form2.Invoke(new EventHandler(delegate { form2.Hide(); }));
}
else
{
form2.Hide();
}
}
}
}
You can modify your runCheckFalse method in the following way - this is a fairly standard pattern for Windows Forms:
private void runCheckFalse()
{
if(InvokeRequired)
{
BeginInvoke(new MethodInvoker(runCheckFalse));
return;
}
form2.Hide();
}
Effectively, what this does is check to see if we are running on the GUI thread (" if InvokeRequired"). If we aren't, we call ourselves on the GUI thread and immediately return. If we are running on the GUI thread, then we don't need to do anything and just continue with the method as normal.
If you need to use parameters:
private void runCheckFalse(bool someParameter)
{
if(InvokeRequired)
{
BeginInvoke(new MethodInvoker(() => { runCheckFalse(someParameter);}));
return;
}
form2.Hide();
}
WinForm controls can only be updated from the UI thread. Take a look at this blog post, it gives a number of approaches to making sure that the update occurs on the UI thread. It is a long post, but worth the read. The quick and dirty approach is the first one if you don't have time to read it.
Erick

Perform action after form is shown.

I am developing a windows mobile application, and I want to do something after a form (a loading screen) is displayed to the user.
normally, I have a Form.Shown event, but with the .net compact framework v3.5, I can't find this event.
Does anyone know of an equivalent to the Shown event or a simple workaround I could use? I don't want to do my own multi-threading if I can help it.
The only thing I can think of would be a bit of a hack, when your form is shown, if it has a default control then it will receive the focus. The Focus event will fire during the initial load before the form is shown, but will be fired the second time when it is visible. put a boolean in the Activate event that is set with the first Activates, then test in the default controls Got Focus event.
Other option would be to use a Timer. Set the Interval to something like 10, start it at the end of your Form Load event and then run your startup specific code.
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
//Do something
}
An example per Hans's Comment:
public partial class Form1 : Form
{
public delegate void DoWorkDelegate();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
BeginInvoke(new DoWorkDelegate(DoWorkMethod));
}
public void DoWorkMethod()
{
//Do something
}
}

how can i make a threadsafe progress bar in C#? what am i doing wrong?

Hey everyone, can someone let me know what they see wrong with this code ?
it throws "Cross-thread operation not valid" exception, on
_DialogueThread.Start();
but if i remove "owner" from
_progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};
the exception wont be thrown but the progressDialouge will be shown then hidden right away .
now i understand why this the error is thrown if i set the progressDialouge.Owner to a parent form that was created on a different thread. but why dose the form disappears when i dont ? what am i doing wrong ?
thanks
class Sampleer : BackgroundWorker
{
private Progresser _progressDialogue;
private Thread _DialogueThread;
private Form _owner;
private bool _SampleSuccess;
public Sampleer(Form owner)
{
_owner = owner;
_progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};
_progressDialogue.Closed += ProgressDialogueClosed;
WorkerReportsProgress = true;
WorkerSupportsCancellation = true;
DoWork += Sampleer_DoWork;
RunWorkerCompleted += Sampleer_RunWorkerCompleted;
ProgressChanged += Sampleer_ProgressChanged;
}
private void Sampleer_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//UPDATE STATUS CODE IS HERE
}
void ProgressDialogueClosed(object sender, EventArgs e)
{
CancelAsync();
Dispose();
}
void Sampleer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//FINISH PROCESS
}
void Sampleer_DoWork(object sender, DoWorkEventArgs e)
{
_DialogueThread = new Thread(_progressDialogue.Show);
_DialogueThread.Start();
//DO LONG PROCESS HERE
}
}
In your action (button click), i would create the progress dialog, and then fire off the background worker. The background worker then reports back to the dialog in the ProgressChanged event.
public partial class MainWindow : Window {
private void btnDoSomething_Click(object sender0, RoutedEventArgs e0) {
_progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};
_progressDialogue.Closed += ProgressDialogueClosed;
_progressDialogue.Show();
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
DoSomething();
e.Result = result;
};
worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e) {
progressDialogue.Update()
};
worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
progressDialogue.Close()
};
worker.RunWorkerAsync(new CustomArgs() {
SomeValue = txtValue.Text,
});
}
}
There are a few mistakes in your approach. Let me point them out one by one.
You inherit BackgroundWorker. That is fine. But you create another thread inside (_DialogueThread). There is no need. DoWork() method runs in a separate thread.
You create/use/manipulate a UI element in another thread. Now, always remember. A Thread never creates a UI element. Its the other way around. A UI element creates a Thread. Progresser in your case should be creating a new Thread or using BackgroundWorker to do any background work you require.
`
Yes decyclone is right, there are many mistakes in the code and in your approach to solution.
You are inherting BackgroundWorker type but subscribing to its own events?
Instead override the methods that are responsible for raising the event in your class.
ex: Instead of subscribing to DoWork, override OnDoWork method.
I've created a sample application, in which a Form (when performing a background task) shows another Form (BackgroundWorkUINotification) and starts the background task in BackgroundWorker thread. The BackgroundWorkUINotification notifies the main form when the Form's CancelButton is clicked.
The main Form when notified, closes the notifier and cancels the background task.
Code below: BackgroundWorkUINotification Form
public partial class BackgroundWorkUINotification : Form
{
public event EventHandler CancelClicked;
public BackgroundWorkUINotification()
{
InitializeComponent();
// call code to animate progress bar..
// probably in another BackgroundWorker that reports progress..
this.cancelButton.Click += HandleCancelButtonOnClick;
}
protected virtual void OnCancelClicked()
{
if (CancelClicked != null)
this.CancelClicked(this, EventArgs.Empty);
}
private void HandleCancelButtonOnClick(Object sender, EventArgs e)
{
this.OnCancelClicked();
}
}
Main Form
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.backgroundWorker = new BackgroundWorker();
this.backgroundWorker.DoWork += HandleBackgroundWorkerOnDoWork;
this.backgroundWorker.RunWorkerCompleted += HandleBackgroundWorkerOnRunWorkerCompleted;
this.backgroundWorker.WorkerSupportsCancellation = true;
}
private void HandleDataRequest()
{
// show UI notification...
BackgroundWorkUINotification backgroundWorkUINotification = new BackgroundWorkUINotification();
backgroundWorkUINotification.CancelClicked += HandleBackgroundWorkUINotificationOnCancelClicked;
backgroundWorkUINotification.Show(this);
// start the background worker
this.backgroundWorker.RunWorkerAsync();
}
private void HandleBackgroundWorkUINotificationOnCancelClicked(Object sender, EventArgs e)
{
// UI notification Form, Cancelled
// close the form...
BackgroundWorkUINotification backgroundWorkUINotification = (BackgroundWorkUINotification)sender;
backgroundWorkUINotification.Close();
// stop the background worker thread...
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
}
private void HandleBackgroundWorkerOnRunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
{
}
private void HandleBackgroundWorkerOnDoWork(Object sender, DoWorkEventArgs e)
{
// do some work here..
// also check for CancellationPending property on BackgroundWorker
}
}
Some days ago I had the same trouble.
This was helped me: "MSDN. How to: Make Thread-Safe Calls to Windows Forms Controls"
I used first aproach (checking InvokeRequired) because it is easiest way.
Hope it is helpful advise!

show message in new windows

now program show Messagebox and wait user decision.
How make, that program don't wait?
Show Messagebox, ant keep going.
(I do not need a user action. I just need to show text)
Or maybe a better option than to report the information to a new window?
I hope to understand my problem.
quick and easy way: use a BackgroundWorker to host your long running job and use the worker's events to pop up messages in the UI thread.
edit: might want to display the messages in the form of a message log.
public partial class MainWindow : Form
{
#region Constructor
public MainWindow()
{
InitializeComponent();
}
#endregion
#region Events
private void button_Click(object sender, EventArgs e)
{
listBox.Items.Add("Job started!");
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10; i++)
{
// send data from the background thread
backgroundWorker.ReportProgress(0, i);
}
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// communicate with UI thread
listBox.Items.Add(string.Format("Received message: {0}", e.UserState));
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listBox.Items.Add("Job done!");
}
#endregion
}
Create a new Form, put some controls on it and show it to the user:
new PopupForm().Show();
If you just have to show a notification window, then this would be a help that I wrote sometime back; works like Outlook like notification window.

Categories

Resources