There is a code represents Console Application where I've done new Thread for the form and displaying a CustomForm on our new thread, I've also tried some kind of data transfer but I haven't successed.
Program.cs code ...
class Program {
public static CustomForm _customForm {
get {
return customForm;
}
set {
customForm = value;
customForm.Show();
}
}
private static CustomForm customForm;
/// <summary>
/// Static method which constains all the magic for the console!
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
// Declaring Thread for the FormThread.
Thread formThread = new Thread(new ThreadStart(FormThread));
// Fires out the work of the thread.
formThread.Start();
Console.ReadKey();
// And console is still running?
// Thread formThread is still running too, thats the reason bruh!
}
/// <summary>
/// Static method which constains all the magic for the form!
/// </summary>
static void FormThread() {
customForm.lbl.Text = "Yolo, it wurks!";
Application.Run(new CustomForm());
}
}
CustomForm.cs code ...
public partial class CustomForm : Form {
public string lblText {
get {
return lbl.Text;
}
set {
lbl.Text = value;
}
}
/// <summary>
/// Just initializer, something what we'll never understand.
/// </summary>
public CustomForm() {
InitializeComponent();
}
/// <summary>
/// When the form is loaded.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnLoad(object sender, EventArgs e) {
Program._customForm = this;
}
}
The only thing I want to do is call the lbl's text property and set some value in a program.cs, not in the customform.cs
Sometimes form wont to show or the lbl in the form isn't changed.
customForm.lbl.Text = "Yolo, it wurks!"; executes before you are creating CustomForm.
Probably, you need to create your form in the main and pass it into Application.Run(CustomForm);
static void Main(string[] args) {
// Declaring Thread for the FormThread.
Thread formThread = new Thread(new ThreadStart(FormThread));
// Fires out the work of the thread.
customForm = new CustomForm();
formThread.Start();
Console.ReadKey();
// And console is still running?
// Thread formThread is still running too, thats the reason bruh!
}
Also, you can't change a control property from other threads. In order to change property from other thread use Invoke method.
public partial class CustomForm : Form {
public string lblText
{
get
{
return lbl.Text;
}
set
{
if (lbl.InvokeRequired)
lbl.Invoke((MethodInvoker) (() => lbl.Text = value));
else
lbl.Text = value;
}
}
}
Related
I want to display an animated loading form while executing some code in the main form. The animated form is used only to show the user that an operation is executing and I want to close it once the operation finishes. The code that I'm using is:
public partial class Form_main_admin : Form
{
private Thread loadingThread;
private string loadingText;
public Form_main_admin()
{
InitializeComponent();
}
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
switch (main_tabControl.SelectedIndex)
{
case 0:
// ...
break;
case 1:
showLoadingForm("Loading");
// Load a datagridview (load data, adjust column widths) in Form_main_admin
closeLoadingForm();
break;
}
}
private void showLoadingForm(string text)
{
loadingText = text;
loadingThread = new Thread(new ThreadStart(openLoadingForm));
loadingThread.Start();
}
private void openLoadingForm()
{
try
{
Form_loading loadingForm = new Form_loading(loadingText);
loadingForm.ShowDialog();
}
catch
{
Thread.ResetAbort();
}
}
private void closeLoadingForm()
{
try
{
loadingThread.Abort();
}
catch
{
Thread.ResetAbort();
}
}
}
The problem is that I get a "Thread was being aborted" exception when I quickly change between tabs (see image in link below).
http://postimg.org/image/bvre2bmi5/
I do not want the user to see this exception if he chages tabs too fast. After reading other posts on this forum I realized that my implementation is not recommended. Could someone please show me how to properly implement this functionality?
If you need an animated progress form, try to use BackgroundWorker class to perform loading in an additional thread:
public partial class MainForm : Form
{
/// <summary>
/// Some progress form
/// </summary>
WaitForm waitForm = new WaitForm();
/// <summary>
/// https://msdn.microsoft.com/library/cc221403(v=vs.95).aspx
/// </summary>
BackgroundWorker worker = new BackgroundWorker();
public MainForm()
{
InitializeComponent();
worker.DoWork += (sender, args) => PerformReading();
worker.RunWorkerCompleted += (sender, args) => ReadingCompleted();
}
/// <summary>
/// This method will be executed in an additional thread
/// </summary>
void PerformReading()
{
//some long operation here
Thread.Sleep(5000);
}
/// <summary>
/// This method will be executed in a main thread after BackgroundWorker has finished
/// </summary>
void ReadingCompleted()
{
waitForm.Close();
}
private void button1_Click(object sender, EventArgs e)
{
//Run reading in an additional thread
worker.RunWorkerAsync();
//Show progress form in a main thread
waitForm.ShowDialog();
}
}
In my project i have a Form, like a Task Dialog, that displays on its own thread... This way when the main thread locks up, the Task Dialog's ProgressBar and status can still be updated.
The issue im having is that the ProgressBar is not updating. The status text updates, but the ProgressBar doesn't move until right before the Form closes. The Form is being opened in the main thread. Here is my code:
public partial class TaskForm : Form
{
/// <summary>
/// Gets or Sets whether the task is cancelable
/// </summary>
protected bool Cancelable = true;
/// <summary>
/// Returns whether the Task form is already open and running
/// </summary>
/// <returns></returns>
public static bool IsOpen
{
get { return (Instance != null && !Instance.IsDisposed); }
}
/// <summary>
/// The task dialog's instance
/// </summary>
private static TaskForm Instance;
/// <summary>
/// Private constructor... Use the Show() method rather
/// </summary>
private TaskForm()
{
InitializeComponent();
}
/// <summary>
/// Open and displays the task form.
/// </summary>
/// <param name="Parent">The calling form, so the task form can be centered</param>
public static void Show(Form Parent, string WindowTitle, string InstructionText, string SubMessage, bool Cancelable, ProgressBarStyle Style)
{
// Make sure we dont have an already active form
if (Instance != null && !Instance.IsDisposed)
throw new Exception("Task Form is already being displayed!");
// Create new instance
Instance = new TaskForm();
Instance.Text = WindowTitle;
Instance.labelInstructionText.Text = InstructionText;
Instance.labelContent.Text = SubMessage;
Instance.Cancelable = Cancelable;
Instance.progressBar.Style = Style;
// Hide Cancel
if (!Cancelable)
{
Instance.panelButton.Hide();
Instance.Padding = new Padding(0, 0, 0, 15);
Instance.BackColor = Color.White;
}
// Set window position to center parent
double H = Parent.Location.Y + (Parent.Height / 2) - (Instance.Height / 2);
double W = Parent.Location.X + (Parent.Width / 2) - (Instance.Width / 2);
Instance.Location = new Point((int)Math.Round(W, 0), (int)Math.Round(H, 0));
// Run form in a new thread
Thread thread = new Thread(new ThreadStart(ShowForm));
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Thread.Sleep(100); // Wait for Run to work
}
/// <summary>
/// Closes the Task dialog
/// </summary>
public static void CloseForm()
{
if (Instance == null || Instance.IsDisposed)
throw new Exception("Invalid Operation. Please use the Show method before calling any operational methods");
Instance.Invoke((Action)delegate()
{
Instance.Close();
});
}
/// <summary>
/// Runs the form in a new application, to prevent thread lock
/// </summary>
protected static void ShowForm()
{
Application.Run(Instance);
}
/// <summary>
/// Updates the instruction text on the task dialog
/// </summary>
/// <param name="Message"></param>
public static void UpdateInstructionText(string Message)
{
if (Instance == null || Instance.IsDisposed)
throw new Exception("Invalid Operation. Please use the Show method before calling any operational methods");
Instance.Invoke((Action)delegate()
{
Instance.labelInstructionText.Text = Message;
});
}
/// <summary>
/// Updates the detail text above the progress bar
/// </summary>
/// <param name="Message"></param>
public static void UpdateStatus(string Message)
{
if (Instance == null || Instance.IsDisposed)
throw new Exception("Invalid Operation. Please use the Show method before calling any operational methods");
Instance.Invoke((Action)delegate()
{
Instance.labelContent.Text = Message;
});
}
/// <summary>
/// Updates the progress bar's value
/// </summary>
/// <param name="Percent"></param>
public static void UpdateProgress(int Percent)
{
if (Instance == null || Instance.IsDisposed)
throw new Exception("Invalid Operation. Please use the Show method before calling any operational methods");
Instance.Invoke((Action)delegate()
{
Instance.progressBar.Step = Percent;
Instance.progressBar.PerformStep();
Instance.progressBar.Refresh();
});
}
#region Non Static
private void CancelBtn_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
#endregion
}
It doesnt make sense that text updates when i call the UpdateStatus and UpdateInstructionText methods, but the ProgressBar does not update when calling UpdateProgress. Any help will be appreciated.
by looking at your code, you are passing the percentage in UpdateProgress() method in the progressBar.Step property, which the step indicate the amount to increase when PerformStep() is called.
since the UpdateProgress() method is used in percentage, i would put
Instance.progressBar.Step = Percent;
into static Show() method
...
Instance.progressBar.Style = Style;
Instance.progressBar.Step = Percent;
...
and you can see the it updating the progress bar precentage.
I'm using below example
TaskForm.Show(this, "Task is executing...", "Step 1 - Preparing...",
"Runninig", true, ProgressBarStyle.Continuous);
for (int i = 1; i <= 100; i++)
{
TaskForm.UpdateInstructionText("Step 1 - Executing..." + i );
TaskForm.UpdateStatus("Running " + i);
TaskForm.UpdateProgress(i);
Thread.Sleep(1000);
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
My apologies I was not accurate in my question and effort. I am developing Console application which has different components. Now I have decoupled them and want them to interact them using asynchronous Publisher/Subscriber way; similar to WPF. So in this case I will have one Master thread which will always be there and depending on request it will invoke event e.g. DataRequested which would be fired on background thread. Once Background thread completes process it will fire event again e.g. DataCompleted which should come back to the calling thread i.e. Master thread. I hope I am clear in my explanation.
So far I have coded below; where I have EventBroker.
public class EventBroker
{
public static event EventHandler SubscriptionAdded;
public static event EventHandler SubscriptionRemoved;
private static volatile EventBroker instance;
private static object syncRoot = new Object();
private static Dictionary<string, List<Delegate>> subscriptions;
/// <summary>
/// Initializes a new instance of the <see cref="T:EventBroker"/> class.
/// </summary>
private EventBroker()
{
}
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>The instance.</value>
public static EventBroker Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new EventBroker();
subscriptions = new Dictionary<string, List<Delegate>>();
}
}
}
return instance;
}
}
/// <summary>
/// Gets or sets the internal subscriptions dictionary.
/// </summary>
/// <value>The subscriptions.</value>
private static Dictionary<string, List<Delegate>> Subscriptions
{
get { return EventBroker.subscriptions; }
set
{
lock (syncRoot)
{
EventBroker.subscriptions = value;
}
}
}
/// <summary>
/// Raises the subscription added event.
/// </summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private static void OnSubscriptionAdded(EventArgs e)
{
if (SubscriptionAdded != null)
SubscriptionAdded(instance, e);
}
/// <summary>
/// Raises the subscription removed event.
/// </summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private static void OnSubscriptionRemoved(EventArgs e)
{
if (SubscriptionRemoved != null)
SubscriptionRemoved(instance, e);
}
/// <summary>
/// Subscribe method to the specified event.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="method">The method Delegate to be invoked when Event fires.</param>
public static void Subscribe(string id, Delegate method)
{
//Check if there is a existing event
List<Delegate> delegates = null;
if (Subscriptions == null)
Subscriptions = new Dictionary<string, List<Delegate>>();
if (Subscriptions.ContainsKey(id))
{
delegates = subscriptions[id];
}
else
{
delegates = new List<Delegate>();
Subscriptions.Add(id, delegates);
}
delegates.Add(method);
OnSubscriptionAdded(new EventArgs());
}
/// <summary>
/// Unsubscribe method from event notifications
/// </summary>
/// <param name="id">The id.</param>
/// <param name="method">The method.</param>
public static void Unsubscribe(string id, Delegate method)
{
if (Subscriptions.ContainsKey(id))
{
if (Subscriptions[id].Contains(method))
{
Subscriptions[id].Remove(method);
OnSubscriptionRemoved(new EventArgs());
}
if (Subscriptions[id].Count == 0)
Subscriptions.Remove(id);
}
}
/// <summary>
/// Fire the specified event by and pass parameters.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="args">The args.</param>
public static void Execute(string id, object sender, EventArgs e)
{
if (Subscriptions.ContainsKey(id))
{
for (int i = 0; i < Subscriptions[id].Count; i++)
{
Delegate x = Subscriptions[id][i];
DynamicInvoke(id, x, sender, e);
if (!Subscriptions.ContainsKey(id))
break;
}
}
}
/// <summary>
/// Checks to see if target of invocation is still a valid
/// (non-disposed objects). Then it dinamicly invokes Delegate.
/// </summary>
/// <param name="id">Event ID</param>
/// <param name="x">Delegate to invoke</param>
/// <param name="args">Object array of arguments</param>
private static void DynamicInvoke(string id, Delegate x, object sender, EventArgs e)
{
if (x.Method != null)
{
if (x.Target is Control)
{
Control ctl = (Control)x.Target;
if (ctl.IsDisposed)
{
Unsubscribe(id, x);
return;
}
}
if (x.Target == null)
{
Unsubscribe(id, x);
return;
}
x.DynamicInvoke(sender, e); ***//this becomes blocking call untill EventHandle is completed and hangs Master Thread***
}
}
}
I use this EventBroker to keep track of my Subscribers and once Publisher comes I invoke certain delegate. But it gets invoked only on Master thread and it gets hanged. I want to invoke EventHandler on separate thread.
public class MasterClass
{
public MasterClass()
{
EventBroker.Subscribe("Topic2", new EventHandler<EventArgs<string>>(CallBackfromWorker));
}
public void InvokeTest()
{
EventArgs<string> EventArgs = new EventArgs<string>("Test");
EventBroker.Execute("Topic1", null, EventArgs); //I want both of this to be asynchronous.
}
public void CallBackfromWorker(object sender, EventArgs<string> e)
{
Debug.Pring("Get Called Asynchronously from Worker thread through Event");
}
}
**//Worker Class**
public class WorkerClass
{
public WorkerClass()
{
EventBroker.Subscribe("Topic1", new EventHandler<EventArgs<string>>(HandleRapRequest1));
}
public void HandleRapRequest1(string RAPRequest)
//public void HandleRapRequest1(object sender, EventArgs<string> e)
{
Logger.LogToDisplay("WorkerClass Request" + RAPRequest);
Logger.LogToDisplay("AsyncClient : " + System.Threading.Thread.CurrentThread.IsBackground);
Logger.LogToDisplay("AsyncClient : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
Logger.LogToDisplay("Going to Sleep");
System.Threading.Thread.Sleep(10000); ***//Hangs my Master Thread***
EventBroker.Execute("Topic2", null, EventArgs); //I want both of this to be asynchronous.
}
}
So bottom line is I am looking for Asynchronous Eventbased Publisher/Subscriber in Console application...similar to CAB event sin SCSF and in WPF...
Thanks
This is simple enough to do:
public class Foo
{
public event Action MyEvent;
public void FireEvent()
{
Action myevent = MyEvent;
if (myevent != null)
{
Task.Factory.StartNew(() => myevent())
.ContinueWith(t =>
{
//TODO code to run in UI thread after event runs goes here
}, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
}
}
If you're using C# 5.0 you can use await which simplifies this code:
public class Foo
{
public event Action MyEvent;
public async Task FireEvent()
{
Action myevent = MyEvent;
if (MyEvent != null)
{
await Task.Run(() => myevent());
//TODO code to run in UI thread after event runs goes here
}
}
}
If you don't need the code running in the UI thread to start after the event handlers are all completed, and it can instead keep going on the UI thread at the same time, you can also simplify the code to just:
public class Foo
{
public event Action MyEvent;
public void FireEvent()
{
Action myevent = MyEvent;
if (MyEvent != null)
{
Task.Factory.StartNew(() => myevent());
//TODO code to run in UI thread while event handlers run goes here
}
}
}
I am having a kinda annoying problem mostly due to my low skill level/experience in C# multithreading.
Here is the background. In my framework, I have a static class named WaitFormHelper, which has two static methods (well... actually more but we don't care here), Start() and Close()
The Start() method initializes and starts a thread which will acquire a lock on the locker object and create a WaitForm (which is a small loading control with a custom message and a progress bar)
In my current project, I have a method which starts a WaitForm, performs calculations, then closes the WaitForm. Nothing fancy at all.
The method looks like the following, I simplified it as much as possible:
public void PerformCalculations()
{
try
{
WaitFormHelper.Start("Title", "message", false);
if (this.CalculationsParameters.IsInvalid)
{
return;
}
// Perform all those lengthy calculations here
}
// catch whatever exception I may have to catch, we don't care here
finally
{
WaitFormHelper.Close();
}
}
Here are the Start() and Close() methods with related methods & attributes, simplified as well:
private static Thread instanceCaller;
private static WaitForm instance;
private static AutoResetEvent waitFormStarted = new AutoResetEvent(false);
private static object locker = new object();
/// <summary>
/// Initializes WaitForm to start a single task
/// </summary>
/// <param name="header">WaitForm header</param>
/// <param name="message">Message displayed</param>
/// <param name="showProgressBar">True if we want a progress bar, else false</param>
public static void Start(string header, string message, bool showProgressBar)
{
InitializeCallerThread(showProgressBar, header, message);
instanceCaller.Start();
}
/// <summary>
/// Initializes caller thread for executing a single command
/// </summary>
/// <param name="showProgressBar"></param>
/// <param name="header"></param>
/// <param name="message"></param>
private static void InitializeCallerThread(bool showProgressBar, string header, string message)
{
waitFormStarted.Reset();
instanceCaller = new Thread(() =>
{
lock (locker)
{
instance = new WaitForm()
{
Header = header,
Message = message,
IsProgressBarVisible = showProgressBar
};
waitFormStarted.Set();
}
instance.ShowDialog();
});
instanceCaller.Name = "WaitForm thread";
instanceCaller.SetApartmentState(ApartmentState.STA);
instanceCaller.IsBackground = true;
}
/// <summary>
/// Closes current form
/// </summary>
public static void Close()
{
lock (locker)
{
if (instance != null && !instance.IsClosed)
{
waitFormStarted.WaitOne();
instance.FinalizeWork();
instance.Dispatcher.Invoke(
new Action(() =>
{
instance.Close();
}));
}
}
}
Now let's get to the problem
This usually works fine, except in this case:
If this.CalculationsParameters.IsInvalid is true (ie. as you probably already understood, I can't perform calculations for some reason, such as "user entering crap in my forms"), the execution directly closes my WaitForm.
However in this case, the main thread will reach the Close method and acquire a lock on the locker object before the thread fired by the Start() method.
What happens is that: Close acquires lock, tries to close the form but instance is still null because the Thread created in InitializeCallerThread is still waiting for the lock to actually create it. Close releases lock, InitializeCallerThread acquires it and... shows a WaitForm which will not close.
Now I know that I can simply fix this problem by testing if the calculation parameters are invalid before actually starting the WaitForm, but well, problem is this WaitForm is supposed to be used by all applications of our framework (which includes 40+ different apps used and maintained in 4 countries), so ideally I'd rather prefer seeing my WaitForm working at all cases.
Do you have any idea on how could I synchronize this to make sure the starter thread is definitely called and executed first?
As you can see I already use an AutoResetEvent for this matter, but if I listen to it before the test if (instance != null) I will end up stuck in my case.
Hope this is clear enough! Thank you!
You need some mechanism to make a "queue control", coordinating steps according to the order you want them occurring.
Recently I have needed to implement something like this to force a specific order in the execution of several threads in a unit test.
Here is my suggestion:
QueueSynchronizer:
/// <summary>
/// Synchronizes steps between threads.
/// </summary>
public class QueueSynchronizer
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="minWait">Minimum waiting time until the next try.</param>
/// <param name="maxWait">Maximum waiting time until the next try.</param>
public QueueSynchronizer(Int32 minWait, Int32 maxWait)
{
}
private Mutex mx = new Mutex();
/// <summary>
/// Minimum waiting time until the next try.
/// </summary>
private Int32 minWait = 5;
/// <summary>
/// Maximum waiting time until the next try.
/// </summary>
private Int32 maxWait = 500;
int currentStep = 1;
/// <summary>
/// Key: order in the queue; Value: Time to wait.
/// </summary>
private Dictionary<int, int> waitingTimeForNextMap = new Dictionary<int, int>();
/// <summary>
/// Synchronizes by the order in the queue. It starts from 1. If is not
/// its turn, the thread waits for a moment, after that, it tries again,
/// and so on until its turn.
/// </summary>
/// <param name="orderInTheQueue">Order in the queue. It starts from 1.</param>
/// <returns>The <see cref="Mutex"/>The mutex that must be released at the end of turn.
/// </returns>
public Mutex Sincronize(int orderInTheQueue)
{
do
{
//while it is not the turn, the thread will stay in this loop and sleeping for 100, 200, ... 1000 ms
if (orderInTheQueue != this.currentStep)
{
//The next in queue will be waiting here (other threads).
mx.WaitOne();
mx.ReleaseMutex();
//Prevents 100% processing while the current step does not happen
if (!waitingTimeForNextMap.ContainsKey(orderInTheQueue))
{
waitingTimeForNextMap[orderInTheQueue] = this.minWait;
}
Thread.Sleep(waitingTimeForNextMap[orderInTheQueue]);
waitingTimeForNextMap[orderInTheQueue] = Math.Min(waitingTimeForNextMap[orderInTheQueue] * 2, this.maxWait);
}
} while (orderInTheQueue != this.currentStep);
mx.WaitOne();
currentStep++;
return mx;
}
}
Start() and Close() with QueueSynchronizer:
//synchronizer
private static QueueSynchronizer queueSynchronizer;
private static Thread instanceCaller;
private static WaitForm instance;
private static AutoResetEvent waitFormStarted = new AutoResetEvent(false);
private static object locker = new object();
/// <summary>
/// Initializes WaitForm to start a single task
/// </summary>
/// <param name="header">WaitForm header</param>
/// <param name="message">Message displayed</param>
/// <param name="showProgressBar">True if we want a progress bar, else false</param>
public static void Start(string header, string message, bool showProgressBar)
{
queueSynchronizer = new QueueSynchronizer();
InitializeCallerThread(showProgressBar, header, message);
instanceCaller.Start();
}
/// <summary>
/// Initializes caller thread for executing a single command
/// </summary>
/// <param name="showProgressBar"></param>
/// <param name="header"></param>
/// <param name="message"></param>
private static void InitializeCallerThread(bool showProgressBar, string header, string message)
{
waitFormStarted.Reset();
instanceCaller = new Thread(() =>
{
lock (locker)
{
//Queuing to run on first.
Mutex mx = queueSynchronizer.Sincronize(1);
try
{
instance = new WaitForm()
{
Header = header,
Message = message,
IsProgressBarVisible = showProgressBar
};
}
finally
{
//I think is here that ends the first step!?
mx.ReleaseMutex();
}
waitFormStarted.Set();
}
instance.ShowDialog();
});
instanceCaller.Name = "WaitForm thread";
instanceCaller.SetApartmentState(ApartmentState.STA);
instanceCaller.IsBackground = true;
}
/// <summary>
/// Closes current form
/// </summary>
public static void Close()
{
//Queuing to run on second.
Mutex mx = queueSynchronizer.Sincronize(2);
try
{
lock (locker)
{
if (instance != null && !instance.IsClosed)
{
waitFormStarted.WaitOne();
instance.FinalizeWork();
instance.Dispatcher.Invoke(
new Action(() =>
{
instance.Close();
}));
}
}
}
finally
{
mx.ReleaseMutex();
}
}
You need to Join on the thread that you created. By joining on a thread you'll block at that point until the thread has finished executing.
public static void Close()
{
lock (locker)
{
instanceCaller.Join();
if (instance != null && !instance.IsClosed)
{
waitFormStarted.WaitOne();
instance.FinalizeWork();
instance.Dispatcher.Invoke(
new Action(() =>
{
instance.Close();
}));
}
}
}
I know this question has been asked several times an I spent all day trying to understand other answers, but since I am very new to C# and WPF nothing helped me so far. I will try to explain my exact problem as much as I can so it will directly help me.
In my MainWindow.xaml I have a progress bar and some button starting a new thread and a long calculation:
<ProgressBar Height="....... Name="progressBar1"/>
<Button Content="Button" Name="button1" Click="button1_Click" />
Now within my MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ParameterizedThreadStart(MyLongCalculation));
ParameterClass myParameters = new ParameterClass();
thread.Start(myParameters);
}
public void MyLongCalculations(object myvalues)
{
ParameterClass values = (ParameterClass)myvalues;
//some calculations
}
}
public class ParameterClass
{
//public variables...
}
Now somehow I have to include somethign in my method MyLongCalculations that will keep updating progressBar1. However, I just can't manage to get it working.
I know all this is very simple, but unfortunately it is the level I am at the moment on with C# so I hope an answer not too complicated and as detailed as possible would be great.
Background worker is well suited for this.
try this:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Process data
ProcessDataAsync(new ParameterClass { Value = 20 });
}
/// <summary>
/// Processes data asynchronously
/// </summary>
/// <param name="myClass"></param>
private void ProcessDataAsync(ParameterClass myClass)
{
// Background worker
var myWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
};
// Do Work
myWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
// Set result
e.Result = MyLongCalculations(myClass);
// Update progress (50 is just an example percent value out of 100)
myWorker.ReportProgress(50);
};
// Progress Changed
myWorker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
};
// Work has been completed
myWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
// Work completed, you are back in the UI thread.
TextBox1.Text = (int) e.Result;
};
// Run Worker
myWorker.RunWorkerAsync();
}
/// <summary>
/// Performs calculations
/// </summary>
/// <param name="myvalues"></param>
/// <returns></returns>
public int MyLongCalculations(ParameterClass myvalues)
{
//some calculations
return (myvalues.Value*2);
}
}
/// <summary>
/// Custom class
/// </summary>
public class ParameterClass
{
public int Value { get; set; }
}
You can use Dispatcher.BeginInvoke() to push UI changes on the UI thread rather than worker thread. Most important thing - you need to access Dispatcher which is associated with UI thread not a worker thread you are creating manually. So I would suggest cache Dispatcher.Current and then use in a worker thread, you can do this via ParametersClass or just declaring a dispatchr field on a class level.
public partial class MainWindow
{
private Dispatcher uiDispatcher;
public MainWindow()
{
InitializeComponents();
// cache and then use in worker thread method
this.uiDispatcher = uiDispatcher;
}
public void MyLongCalculations(object myvalues)
{
ParameterObject values = (ParameterObject)myvalues;
this.uiDispatcher.BeginInvoke(/*a calculations delegate*/);
}
}
Also if you need to pass a UI dispatcher in some service/class (like ParametersClass) I would suggest take a look at this nice SO post which show how you can abstract it by an interfaces with ability to push UI changes synchronously/asynchronously so it would be up to a caller (basically use Invoke() or BeginInvoke() to queue a delegate in the UI messages pipeline).