EventWaitHandle blocking the entire form - c#

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();
}

Related

C# - Return result asynchronously

I know I asked this before - but it involved threading or tasks. Now, I want to achieve the same without using threading or tasks, because I'm limited (the code is a Lua script). Now, what I want to achieve is like that:
Imagine a form with 2 buttons. The first button is called "Run", the second one is called "Set". I want Run to show a MessageBox with the text: "The result was " + result. However, I want it to show this MessageBox only once the result is set. So, you click the Run button, it halts the exceution of the method (the thread doesn't get stuck, though, so it's asychronous), and then when you click the Set button it sets the response to true and the code of the Run button continues and it shows the MessageBox.
How can I achieve this without the use of threading and tasks? Please show some examples. Moreover, if you can - show examples with threading or tasks even though my Lua code limits it. So far I've tried using a TaskCompletionSource of boolean but this involves the usage of the await operator which I can't use because it's a part of the C# sytnax (not allowed in Lua).
Best regards.
A very primitive way would be to use the Application.DoEvents() in a loop, it continues serving your form in a very delayed way, but it works to what you specified. Ofcourse if you depend on a more complex calculation inside your run loop, you would need to call application.doevents inside the complex calculation
So, like this:
using System;
using System.Windows.Forms;
namespace TestForm
{
public partial class Form1 : Form
{
FakeAsync test = new FakeAsync();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
test.Start();
}
private void button2_Click(object sender, EventArgs e)
{
test.IsSet = true;
}
}
public class FakeAsync
{
public bool Running { get; protected set; }
public bool IsSet { get; set; }
protected virtual void Loop()
{
Running = true;
while (!IsSet)
{
Application.DoEvents();
}
MessageBox.Show("Hello World!");
Running = false;
}
public void Start()
{
if (!Running)
{
Loop();
}
}
}
}

Progress bar form not fully loading

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.

Get a Control's Property from Another Thread

I want to get a control's property from a BackgroundWorker in my form:
foreach (ListViewItem i in ListView.CheckedItems) { //error: Cross-thread operation not valid: Control 'ListView' accessed from a thread other than the thread it was created on.
//do something with i
}
Can anyone suggest the simplest and easiest method to do this?
Let me take another stab at this...
1.) Drag a ListView onto the Form
2.) Drag a BackgroundWorker onto the Form
3.) Create a method do iterate through the ListViewItem collection
private void LoopThroughListItems()
{
foreach (ListViewItem i in listView1.CheckedItems)
DoSomething();
}
4.) Add code to call LoopThroughListItems() inside the BackgroundWorker's DoWork Event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
LoopThroughListItems();
}
5.) In your Form Load - execute the code on the main thread (it works) then on the backgroundWorkder thread (it fails)
private void Form1_Load(object sender, EventArgs e)
{
// Try it on the UI Thread - It works
LoopThroughListItems();
// Try it on a Background Thread - It fails
backgroundWorker1.RunWorkerAsync();
}
6.) Modify your code to use IsInvokeRequired/Invoke
private void LoopThroughListItems()
{
// InvokeRequired == True when executed by non-UI thread
if (listView1.InvokeRequired)
{
// This will re-call LoopThroughListItems - on the UI Thread
listView1.Invoke(new Action(LoopThroughListItems));
return;
}
foreach (ListViewItem i in listView1.CheckedItems)
DoSomething();
}
7.) Run the app again - now it works on the UI thread and the non-UI thread.
That solve the problem. The checking IsInvokeRequired/Invoking is a common pattern you'll get used to a lot (which is why it's included on all Controls). If you are doing it all over the place, you can do something clever and wrap it all up - as described here: Automating the InvokeRequired code pattern
Try something like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void OnClick(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void OnDoWork(object sender, DoWorkEventArgs e)
{
foreach (ListViewItem i in GetItems(listView1))
{
DoSomething(i);
}
}
private IEnumerable<ListViewItem> GetItems(ListView listView)
{
if (InvokeRequired)
{
var func = new Func<ListView, IEnumerable<ListViewItem>>(GetItems);
return (IEnumerable<ListViewItem>)Invoke(func, new[] { listView });
}
// Create a defensive copy to avoid iterating outsite UI thread
return listView.CheckedItems.OfType<ListViewItem>().ToList();
}
private void DoSomething(ListViewItem item)
{
if (InvokeRequired)
{
var action = new Action<ListViewItem>(DoSomething);
Invoke(action, new[] { item });
return;
}
// Do whatever you want with i
item.Checked = false;
}
}
}
However, your question was really general. Maybe there would be a easier or better solution if you shared more details.

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