Access parent windows form in backgroundworker - c#

I have an extended method for messagebox.Show(), which uses IWin32Window of the parent winform to center the MessageBox on it.
However, I can't seem to understand how can we possibly use this same extension method to pop-up MessageBox on the main/parent winform from within BackgroundWorker.
The extension method is something like this:
MessageBoxExTn.Show(IWin32Window parentForm, string MsgText, string Caption, MessageBoxButtons);
I mean, I want to call this method from BackgroundWorker's DoWork event with threadsafe access to the parent/main winform.

You could use event in this case and fire event when you want to show the message box. Now in your parent/main form handle this event and show the message box. This way you can achieve this.

You need to leverage the BackgroundWorker event ProgressChanged. This will do the thread switching for you. So imagine something like this:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
MessageBoxExTn.Show(parentForm,
e.UserState as string,
someCaption,
MessageBoxButtons.OK);
}
and that's called like this:
backgroundWorker1.ReportProgress(1, "The message you want displayed.");

You can add another method on your Main Form and do something like this
public void ShowMessage(string cap,string message)
{
if (this.InvokeRequired)
{
Action<string, string> action = ShowMessage;
this.Invoke(action, new object[] {cap, message});
return;
}
MessageBoxExTn.Show(IWin32Window parentForm, string MsgText, string Caption, MessageBoxButtons);
}
please take a look at Control.InvokeRequired to understand more thread-safe in windows form
call your code
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ShowMessage();
}

Related

Show MessageBox immediately in Windows Forms?

Is there any way to have a messagebox immediately pop up when a form opens? I just want to display a short message about how to use the form when it opens. I tried
private void myForm_Load(object sender, EventArgs e)
{
DialogResult dialogOpen = MessageBox.Show("Use the navigation menu to get started.", "Welcome!", MessageBoxButtons.OK);
}
but it doesn't work.
Showing a MessageBox during Form_Load works just fine for me. I literally copy/pasted the code from your original post, and it worked. I'm on .NET Framework 4.5 on Windows 8.1.
Are you sure your Load event handler is getting called? Perhaps the it's not hooked up to the Load event properly.
I don't see why it wouldn't work in Form_Load. Definitely try doing as others have pointed out by putting it beneath form initialization.
Though, given that you're just showing a message box, I don't think there is any reason to store the result, so a simple MessageBox.Show(message); Should do the trick.
As #s.m. said, from a UX point of view, having a notification thrown in your face as soon as the app starts would be very obnoxious, at least if you have it EVERY time. Personally, I would create a boolean Settings variable, set it to true the first time the message is displayed, and only display it when the setting is false, i.e. the first time the message is displayed.
private boolean splashShown = Properties.Settings.Default.splashShown;
private void Form_Load(object sender, EventArgs e)
{
if (!splashShown)
{
MessageBox.Show("message");
myForm.Properties.Settings.Default.splashShown = true;
myForm.Properties.Settings.Default.Save();
}
}
And set up the splashShown Setting in your form properties.
If the problem is that your Form_Load() method isn't actually attached to your Form.Load() event, you can double click the form window in the designer and it will automatically created the Form_Load() base method for you and attach it to the Form.Load() event
Is there a reason to use the Load method of the form? If not you could to it in the constructor of form. If you want it to show up immediately after your form loads, you should do it in the constructor after the form is initialized. It should look something like this:
public partial class myForm : Form
{
public myForm()
{
InitializeComponent();
DialogResult dialogOpen = MessageBox.Show("Use the navigation menu to get started.", "Welcome!", MessageBoxButtons.OK);
}
}
The constructor (public myForm()) and the InitializeComponent(); should be automatically added to the form by Visual Studio after creating it.
Form_Load event occurs before the form is really visible.
I use:
static private bool splashShown = false;
private void Form1_Activated(object sender, System.EventArgs e)
{
if (!splashShown)
{
MessageBox.Show("message");
splashShown = true;
}
}
I have used this and it works fine. App start brings up messagebox first before all else.
InitializeComponent();
MessageBox.Show("put your message here");

Detect when a form has been closed c#

I have a WinForm that I create that shows a prompt with a button. This is a custom WinForm view, as a message box dialog was not sufficient.
I have a background worker started and running. I also want to exit the while(aBackgroundWorker.IsBusy) loop if the button on myForm was clicked.
//MyProgram.cs
using(CustomForm myForm = new CustomForm())
{
myForm.Show(theFormOwner);
myForm.Refresh();
while(aBackgroundWorker.IsBusy)
{
Thread.Sleep(1);
Application.DoEvents();
}
}
Right now, in the CustomForm the Button_clicked event, I have
//CustomForm.cs
private void theButton_Click(object sender, EventArgs e)
{
this.Close();
}
Do I need to add more code to the CustomForm class, or the location where I declare and initialize the form in order to be able to detect a closure?
To detect when the form is actually closed, you need to hook the FormClosed event:
this.FormClosed += new FormClosedEventHandler(Form1_FormClosed);
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Do something
}
Alternatively:
using(CustomForm myForm = new CustomForm())
{
myForm.FormClosed += new FormClosedEventHandler(MyForm_FormClosed);
...
}
void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Do something
}
You might be going overkill. To show a form like a dialog window and wait for it to exit before returning control back to the calling form, just use:
mySubForm.ShowDialog();
This will "block" the main form until the child is closed.
Make sure your background worker supports cancellation and as others have pointed out use the form closed event handler. This code should point you in the right direction:
using(CustomForm myForm = new CustomForm())
{
myForm.FormClosed += new FormClosedEventHandler(ChildFormClosed);
myForm.Show(theFormOwner);
myForm.Refresh();
while(aBackgroundWorker.IsBusy)
{
Thread.Sleep(1);
Application.DoEvents();
}
}
void ChildFormClosed(object sender, FormClosedEventArgs e)
{
aBackgroundWorker.CancelAsync();
}
Handle the FormClosing event of the form to be notified when the form is closing, so you can perform any cleanup.
You should be able to hook into the FormClosing and FormClosed events.
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosing.aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosed.aspx
Closing is before it's closed.
Closed is after it's closed.
A couple things...
First, it appears that loop is there in order to prevent execution form proceeding while the dialog is open. If that is the case, change you .Show(parent) to .ShowDialog(parent). That will also take care of the rest of your question.
Note that this.Hide(); is not the same as this.Close(); in the actual dialog your overriding the closed event

cancel process by clicking a button

There is a process in the project I wrote. It takes time, and I want to use a progress bar. I want to allow the user to cancel the process and the ProgressBar by clicking a button. I do not want the user to be able to click any other controls on that form when my process is running. If I use a thread, then the user can click other controls on the form.
Perhaps one solution is to use another form, and set the ProgressBar and cancel button on the second form. But how can I set the value of the ProgressBar according my process, which is taking part on the first form.
What's the solution?
Thanks in advance.
This is best done with a dialog, it automatically makes the rest of your UI inaccessible. Add a new form to your project and drop a ProgressBar and a Button on it. And add a public method so you can update the progress bar from the event handler in your main form:
public partial class ProgressDialog : Form {
public ProgressDialog() {
InitializeComponent();
}
public void ShowProgress(int progress) {
progressBar1.Value = progress;
}
private void CancelProcess_Click(object sender, EventArgs e) {
this.DialogResult = DialogResult.Cancel;
}
}
You'll need to display the dialog when you start the worker:
ProgressDialog dlg;
private void RunProcess_Click(object sender, EventArgs e) {
backgroundWorker1.RunWorkerAsync();
using (dlg = new ProgressDialog()) {
dlg.ShowDialog(this);
}
dlg = null;
if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
}
Note how it calls CancelAsync() to stop the worker so closing the dialog is enough to make it stop. You'll need to update the progress bar:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
dlg.ShowProgress(e.ProgressPercentage);
}
And you need to automatically close the dialog when the worker completes and the user hasn't close the dialog herself:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (dlg != null) dlg.Close();
}
Use a BackgroundWorker, this provides an API for reporting progress and cancellation.
Use BackgroundWorker for that. Add it to your first form and on BackgroundWorker progress changed event change progress bars value. Look at example shown in documentation.

Code placement in windows.forms

I am new to windows.forms programming. I started making an application that has the following flow of events:
click a button that opens a file - extract its contents in some list of strings - visit some websites - parse their content - etc.
So because everything in my app happens after i click a button to open a file, I have put all my code on the click event of the button. However I do know this is bad coding practice, because I realised I ended up having ALL the program flow inside that click event. I know that the event should only contain code related to the button, but where to place the code that follows, if not inside the event? Is there another event that I should use instead of just writing all in the button click?
I hope I've made my question clear. If not then I'll retry to explain my problem. I simply don't know where to write the code that follows the click event. If I put it in:
public Form1()
{
InitializeComponent();
}
..then it executes before the click event which is wrong.
Thank you in advance.
The typical way to do this is to write one or more methods that perform the action, and call those from the click event. For any long-running actions, do them in a background worker thread.
For example:
public void myButton_OnClick(EventArgs e, object sender)
{
VisitWebSites();
}
private void VisitWebSites()
{
var webSiteList = GetWebSitesFromFile();
foreach (var w in webSiteList) {
StartVisitingWebSite(w);
}
}
private IEnumerable<string> GetWebSitesFromFile()
{
// whatever
}
private void StartVisitingWebSite(string url)
{
backgroundWorker1.RunWorkerAsync(url);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string url = (string)e.Argument;
e.Result = VisitWebSite(url);
}
private string VisitWebSite(string url)
{
// This is called in background thread. Do whatever you do to return data.
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error || e.Cancelled)
return;
string result = e.Result.ToString();
// Do whatever you do with the result
}
Look at the BackgroundWorker documentation to see how to perform actions in a background thread and then handle their results.
You can encapsulate all of the work that you want to do into another function in the form's class. Its modification access would be PRIVATE of course (unless you want another class to be able to access the method). From inside of your button_click event handler, you can call this new function. That is simplest way to do this. Otherwise, you can use the example provided in the link above by #Robert S.
You should look into the Model View Presenter pattern. http://msdn.microsoft.com/en-us/magazine/cc188690.aspx

Databinding Textbox to Form.Text (title)

I'm trying to bind a Textbox.Text to Form.Text (which sets the title of the form).
The binding itself works. But, the title isn't updated until I move the entire form.
How can I achieve Form.Text being updated without moving the form? I'd like Form.Text being updated directly when I type something in the Textbox.
Edit; I set the title of the Form in a TextChanged event which is fired by a ToolStripTextbox:
public partial class ProjectForm : Form
{
public ProjectForm()
{
// my code contains all sorts of code here,
// but nothing that has something to do with the text.
}
}
private void projectName_TextChanged_1(object sender, EventArgs e)
{
this.Text = projectName.TextBox.Text;
}
And the Databinding version:
public partial class ProjectForm : Form
{
public ProjectForm()
{
this.projectName.TextBox.DataBindings.Add("Text", this, "Text", true, DataSourceUpdateMode.OnValidation);
}
}
Edit 2: I see I forgot to mention something. Don't know if it adds anything, but my apllication is a MDI application. The part of the title that changes is:
ApplicationName [THIS CHANGES, BUT ONLY AFTER MOVING/RESIZING]
Classic problem, you're not updating the existing form's Text property but a new one that is not visible. Call the Show() method after you change the Text.
Source
You can also try invalidating your form in the TextChanged event so it will force a re-paint.
Edit 1: This StackOverflow question may provide an answer for you since you are a MDI application
Edit 2: It could be that this operation is not thread-safe and therefore the UI thread is blocking. Therefore, you need to invoke another function in order to cause it to update. I had a similar problem with StatusBar Labels a while back. Here is some example code if you do not know how to use delegates:
public delegate void updateFormTextD(string text);
private void formText(string text)
{
this.Text = text;
}
private void updateFormText(string text)
{
Invoke(new updateFormTextD(formText), text);
}
What about using the TextChanged event of the TextBox, like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
this.Text = this.textBox1.Text;
}
}
I had the same problem that almost freaks me out. Eventually I found that my form title update request by "this.text = " was blocked by the method "WndProc(ref Message message)". At the end of WndProc method I added "base.WndProc(ref message)", which passes along all messages to the base also. After that I could update successfully my form title by "this.text = ".
Therefore, I suggest you to look for a method that blocking your form title for being updated.
None of the traditional items worked (invalidate & refresh); nor was I able to readily determine where the message might have been blocked. However, sleeping the thread took care of this.
fForm1->Text = Title;
Thread::Sleep(0); //Allow release for title to update

Categories

Resources