I have a textbox with some text in it ("hello"). If I change the text to anything else and get the text from the textbox (through the code), I'll get "hello" although I changed it.
In another case, when I change a checkbox's check state, the checkbox won't visually tick (or untick).
Anyone has an idea what's going on and how to sync them?
I've opened a new project,
thats the only function on my form :
public void Switch()
{
Checkbox1.Checked = !Checkbox1.Checked;
}
and I call it from the program.cs :
static MainForm MyForm;
MyForm = new MainForm();
MyForm.Switch();
I assume that you are simply instantiating MainForm in Main and then calling Switch.
Which means that you are not sending the request on the UI Thread.
So, you can Invoke your code to run on the UI thread:
public void Switch()
{
// true if off of the UI thread, and thus must be Invoked
if (this.InvokeRequired)
{
// off UI thread; put it onto it
this.Invoke(Switch);
}
else
{
// on UI thread
Checkbox1.Checked = ! Checkbox1.Checked;
}
}
You should only interact with the UI from the UI Thread. You should not litter your code with Invokes unless you expect to call it off of the UI Thread, or you provide a means for others to do so (some public API).
Related
I have a main UI that doing some time-consuming work. When it is executing, I would like to open a second form with a progress bar (marquee style) to indicate "working on it".
I have seen people putting the time-consuming task in the BackgroundWorker, however, I would like to run in the main UI thread.
The time-consuming task will be executed in MainForm. I would like to reuse the progress bar for various process, so I am writing a second form ProgressBarForm with BackgroundWorker in it, that would start the _mainWork at the same time as showing progress bar, and will stop and close the ProgressBarForm when _mainWork is done.
Because forms are modals, I am thinking of showing ProgressBarForm in the BackgroundWorker in order not to block MainForm.
Please note that I am not running mainForm in BackgroundWorker. My backgroundWorker just show the form and perhaps report a timer.
public partial class ProgressBarFom : UControl
{
public delegate void MainWork();
private MainWork _mainWork;
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//progressBar.Hide();
this.OnClose(sender, e);
//
backgroundWorker.Dispose();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//show this ProgressBarForm
this.ShowDialog();
//stop backgroundWorker
//calling this.Close() in RunWorkerComplete
if (backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
return;
}
}
public void CallProgressBar(object sender, EventArgs e)
{
//progressBar.Show();
backgroundWorker.RunWorkerAsync();
_mainWork();
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
}
}
In MainForm, I am passing mainwork and call ExecWithProgressBar
private void ExecWithProgressBar()
{
ProgressBarFom .MainWork mainWork = new ProgressBarFom .MainWork(ProgressBarMainWork);
ProgressBarFom prBar = new ProgressBarFom (mainWork);
prBar.CallProgressBar(null, null);
}
Some problems I encoutered
Inside DoWork, the same modal issue occurs. ShowDialog() will block the thread and therefore I never get to check CancellationPending to close ProgressBarForm.
ProgressBarForm starts later then the mainWork. I thought when I called CallProgressBar, the backgroundWorker should start well before my mainWork.
Is worker.Dispose() necessary in RunWorkerComplete?
Would it be a better choice to run mainWork in Worker? And why? I decided to let the main thread run this to not disturb the normal flow, what in Main thread will remain in Main thread, Progress bar is like an addon. If we bring it to the worker, would we need another thread to for progress bar itself?
Unless you do some very ugly hacks (like running more than one message loop inside your application) you cannot display a dialog if the thread running the main window is busy. All dialogs use the same thread to do the display update stuff in WinForms. In fact, they even must be running on the same thread.
There's one (sometimes acceptable) hack using Application.DoEvents(), but I wouldn't use it either, because it gets you into a lot of problems as well.
So the simple answer is: This doesn't work. Use a background worker to do lengthy processing.
In my .NET application I am showing a form with a "Please wait" message if some task takes a while to tell the user that the program still works. This form has to run in it's own thread since the main thread is the one doing work and therefore busy. I read that one can use Form.Owner for this task, but I don't think that works if the forms run on different threads.
The problem is that the wait form can be hidden behind the main form in which case the user couldn't see it and can't bring it to the front, since it has no task bar button (an it musn't have one).
My question is if it is possible to let the wait form stay above the main form without making it an AlwaysOnTop form (which would stay above ALL windows)?
Your main thread should not be doing work. It should be handling UI and nothing else.
The right way to do this is to perform any and all time-consuming work in an asynchronous manner (e.g. in a separate thread), and keeping all of your user interface in the main thread. Then you can simply show the "Please wait" message form by calling the Form.ShowDialog() method. This will force focus to that dialog and keep it on top of its parent form (don't forget to pass the parent form reference to the ShowDialog() method).
Without a code example, I can't say exactly how this would look in your specific scenario. But the general idea looks something like this:
private void button1_Click(object sender, EventArgs e)
{
using (Form form = MyWaitMessageForm())
{
form.Shown += async (sender1, e1) =>
{
await Task.Run(() => MyLongRunningWork());
form.Close();
}
form.ShowDialog(this);
}
}
You can use the Form.TopMost property for this.
You can also use the following code:
protected void SetZOrder(IntPtr bottom, IntPtr top) {
const int flags = 0x0002 | 0x0001;
NativeMethods.SetWindowPos(bottom, top, 0, 0, 0, 0, flags);
}
bottom - the pointer of the main form, top - the pointer of the wait form. To get a pointer, use Form.Handle property. And call the SetZOrder via the BeginInvoke method of the parent form.
You could use the Form.ShowDialog(IWin32Window)
Form1 testDialog = new Form1();
testDialog.ShowDialog(this)
I have a quite complex form that presents an option to run a script (our own type). Whilst it runs, I don't want to lock the UI completely, so I would like to start it in a thread. So far so good, but to prevent the user messing with things I need to selectively disable parts of the UI. I could recursively set Enabled = false and then Enabled = true when the thread ends. But this ignores the state of the control at the time of running (ie controls which were disabled for various reasons would be incorrectly re-enabled). Short of building a tree of booleans, is there some other way to block input (such as the GlassPane type in Java)?
Don't use DoEvents, it's evil.
Use a panel and add all the controls you want to disable in it. When the panel will be disabled, all inner controls will appear to be disabled but the value of their Enabled property won't be actually modified.
Here's a working example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Disables UI elements using the panel
this.SetPanelEnabledProperty(false);
// Starts the background work
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(this.Worker));
}
private void Worker(object state)
{
// Simulates some work
System.Threading.Thread.Sleep(2000);
// Now the work is done, enable the panel
this.SetPanelEnabledProperty(true);
}
private void SetPanelEnabledProperty(bool isEnabled)
{
// InvokeRequired is used to manage the case the UI is modified
// from another thread that the UI thread
if (this.panel1.InvokeRequired)
{
this.panel1.Invoke(new MethodInvoker(() => this.SetPanelEnabledProperty(isEnabled)));
}
else
{
this.panel1.Enabled = isEnabled;
}
}
}
Could you use a panel populated with the controls you want disabling while the script runs, then re-enable the panel when the script has ended.
Alternatively you could start a Process for the script.
You can solve this either using the Application.DoEvents() method, or you have to write a delegate which invokes the corosponding control.
I think the Application.DoEvents() would be the simplest way.
You should call Application.DoEvents() in the loop on your thread.
For the delegate version, you find here some information: http://msdn.microsoft.com/de-de/library/zyzhdc6b.aspx
I am attempting to update the progress bar on a main form with the work being done in a different class. For example:
private void transfer_Click(object sender, EventArgs e)
{
Guid aspnetUserId = Guid.Parse(System.Configuration.ConfigurationSettings.AppSettings["ASPNetUserID"]);
WC1toWC2Transfer transfer = new WC1toWC2Transfer(aspnetUserId);
backgroundWorker1.RunWorkerAsync(transfer);
}
And then in the background method actually call the method:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
WC1toWC2Transfer transfer = e.Argument as WC1toWC2Transfer;
transfer.WC1ToWC2EmployerTransfer(log, wc1ConnStr, wc2ConnStr, progressBar1);
}
In the WC1ToWC2EmployerTransfer method, I'm setting the progress bar maximum and updating the value everytime something is transferred to the database in this method, but when I do this nothing happens. There's code inside the method that runs a stored procedure in a database, but as soon as it hits that portion of the code, it stops debugging and I have to run the process again.
Am I doing something wrong here? Do I need to rewrite what I have so the actual methods are in the main form and not in a different class? I'm a junior developer (just started a few months ago) so forgive me if I'm doing something blatantly wrong or if I didn't explain this well enough.
Any help would be greatly appreciated.
You cant alter the UI unless you are on the main thread, which you BackgroundWorker will not be.
What you need to do is create an event handler in the main form to handle the backgroundworker's ProgressChanged event.
eg
// this method should be in your main form.
private void backgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update your progress bar here.
}
In your background worker, you call the ReportProgress method which will fire the ProgressChanged event.
There is a nice example here.
I think the reason is that you get an IllegalCrossThreadException, because you're attempting to access the control from a different thread than it was created. The BackgroundWorker provides a ReportProgress method and a ProgressChanged event that is typically used for such updating and which will be executed on the UI thread. When accessing the progressbar from another thread than the UI thread, do it like this:
if(progressBar1.InvokeRequired) {
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Maximum = someValue; }));
}
As a sidenote: It's not very good design to pass the progressbar to your worker class. The form could close, it could get disposed and the worker would not know anything about it, eventually failing with an ObjectDisposedException that probably isn't caught. Additionally, you're making the worker dependent on System.Windows.Forms when it probably doesn't need to. Rather let your worker report progress through an event and pass that on to your progressbar from the class that created the worker.
I've one MainForm window and from that user can press 3 buttons. Each of the button starts new Form in which user can do anything he likes (like time consuming database calls etc). So i decided to put each of the forms in it's own threads:
private Thread subThreadForRaportyKlienta;
private Thread subThreadForGeneratorPrzelewow;
private Thread subThreadForRaporty;
private void pokazOplatyGlobalne() {
ZarzadzajOplatamiGlobalneDzp varGui = new ZarzadzajOplatamiGlobalneDzp();
varGui.ShowDialog();
}
private void pokazRaportyKlienta() {
RaportyDzpKlient varGui = new RaportyDzpKlient();
varGui.ShowDialog();
}
private void pokazRaportyWewnetrzne() {
RaportyDzp varGui = new RaportyDzp();
varGui.ShowDialog();
}
private void pokazGeneratorPrzelewow() {
ZarzadzajPrzelewamiDzp varGui = new ZarzadzajPrzelewamiDzp();
varGui.ShowDialog();
}
private void toolStripMenuGeneratorPrzelewow_Click(object sender, EventArgs e) {
if (subThreadForGeneratorPrzelewow == null || subThreadForGeneratorPrzelewow.IsAlive == false) {
subThreadForGeneratorPrzelewow = new Thread(pokazGeneratorPrzelewow);
subThreadForGeneratorPrzelewow.Start();
} else {
}
}
private void toolStripMenuGeneratorRaportow_Click(object sender, EventArgs e) {
if (subThreadForRaporty == null || subThreadForRaporty.IsAlive == false) {
subThreadForRaporty = new Thread(pokazRaportyWewnetrzne);
subThreadForRaporty.Start();
} else {
}
}
private void toolStripMenuGeneratorRaportowDlaKlienta_Click(object sender, EventArgs e)
{
if (subThreadForRaportyKlienta == null || subThreadForRaportyKlienta.IsAlive == false) {
subThreadForRaportyKlienta = new Thread(pokazRaportyKlienta);
subThreadForRaportyKlienta.Start();
} else {
}
}
I've got couple of questions and i hope someone could explain them:
When i use Show() instead of ShowDialog() the windows just blink for a second and never shows. What's the actual difference between those two and why it happens?
When i use ShowDialog everything seems normal but i noticed not everything gets filled properly in one of the gui's (one listView stays blank even thou there are 3 simple add items in Form_Load(). I noticed this only in one GUI even thou on first sight everything works fine in two other gui's and i can execute multiple tasks inside those Forms updating those forms in background too (from inside the forms methods). Why would this one be diffrent?
What would be proper way of doing this? Tasks performed in each of those Forms can be time consuming and i would like to give user possibility to jump between those 4 windows without problem so he can execute what he likes.
The difference is with Modal and Modeless Windows Forms.
Modeless forms let you shift the
focus between the form and another
form without having to close the
initial form
Show() method is used for this purpose
Show() shows the new form, then returns. If this is all the thread is doing than the thread will exit, and that will destroy the form.
ShowDialog() shows the form, and then begins running a message pump, until the form is hidden or destroyed, ShowDialog() doesn't return, so your thread keeps running.
If You mean for these forms to each behave like a separate application window. then you could also use Application.Run() after form.Show() to run a message pump for the form on that thread. The drawback to doing it this way, is that when any one of your forms is closed, it might end up taking down the whole process because of the way WM_QUIT is handled.
But other than the way you would deal with closing down your application, Form.ShowDialog() is very much like Form.Show() followed by Application.Run(). The conditions that cause the message pump to exit are a bit different between these to, so you would choose one or the other mostly based on how you want to your application to handle closing one of your forms.
You should put the time-consuming tasks in their own BackgroundWorker threads. Keep all of the forms in the main thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
1: Show() is not blocking, that is it shows the window and then returns. Afterwards, the varGui variable goes out of scope and is finalized by the garbage collector => disappears.
2: You'd need to show the updating code to get a definite answer, but as you can only update the contents of a form when running in the thread that created the form. Doing it otherwise is unreliable, so is the usual culprit when form updates don't work. In a method of a form class, call:
if (InvokeRequired) {
.Invoke(..); // call back this same method on the right thread
} else {
// dowork
}
to manage the UI.
3: The proper way is what Jake wrote: use one thread for all GUI stuff and BackgroundWorker threads for, well, background work.