Change WinForms using Threads in C# - c#

I'm trying to change Forms using threads. Initially I used Hide() and Show() to hide the previous Form from the next one, the problem is that when I close the application there is always something running in the backgorund. My idea is this: From Form1 clicking a button goes to Form2 which in turn can choose whether to return to Form1 or go to Form3 which in turn can choose whether to return to Form2 or Form1
This is a piece of code from the Form1 button that if clicked must take me to Form2. The point is that it does not close Form1 and in fact it always remains in the background and when I click the button to close Form2 (using Close ()) the program does not close and remains at Form1 and if I close that then the program closes definitively
The second form is called OptionGeneral
Thread StartThread = null;
public void btnOptions_Click(object sender, EventArgs e)
{
StartThread = new Thread(SwitchForm);
StartThread.Start();
//Hide();
OptionGeneral option = new OptionGeneral();
option.Show();
}
public void SwitchForm()
{
StartThread.Abort();
}

Is this a new application or one you are trying to redesign that already runs forms in separate threads...
In general programming in Windows either C++, WinForms or WPF you do the following.
Make the interactions you do in the GUI event based. All forms dialogs and GUI elements live in the main thread so data passing is extremely simple and fast.
When real work needs to be done cast the work into a threadpool or task library and when the work is complete marshal the data back to the main ui thred to execute display logic.
In WPF the Dispatcher is your key to pushing data back to the UI.
It has been a LONG while since I did webforms.
If your application does not obey these rules you will suffer complicated penatlies.
If it is brand new then reboot your development with the strategies I had in mind.
If your application was designed against these rules and you are maintaining it then analyze what kind of work effort it would take to port all the features to a stable architecture and discuss with the people in charge.

When you are in the form1() form1 is the main window of the system, which is the boot window, you cannot close it and go to form2(), but you can make the main window form2() and you call form1() before the Initialize Component form2()
in Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form2());
}
in form2()
public Form2()
{
new Form1().ShowDialog();
InitializeComponent();
}
now in form1() you can close window
public Form1()
{
InitializeComponent();
}
private void btn_Close_Click(object sender, EventArgs e)
{
this.Close();
}

Do not work with UI elements directly from other thread otherwise you'll get application freeze. Use some dispatching to do it like discussed in this topic How do I update the GUI from another thread?
Another bad thing - don't use thread.Abort() call otherwise your application will be unpredictable and buggy see https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.abort?view=netframework-4.8
The Thread.Abort method should be used with caution. Particularly when you call it to abort a thread other than the current thread, you do not know what code has executed or failed to execute when the ThreadAbortException is thrown. You also cannot be certain of the state of your application or any application and user state that it's responsible for preserving. For example, calling Thread.Abort may prevent the execution of static constructors or the release of unmanaged resources.

To me it seems a case of just hiding and showing the forms as per the comments, and closing Form1 if the other forms close.. Forget the showing/hiding via threading bit; there lies a world of pain (use TPL for long running operations/stay away from multithreading if you can):
class Form1{
public Form2 Form2 { get; set; }
Form1(){
//wire forms up to each other
var form3 = new Form3() { Form1 = this };
Form2 = new Form2() { Form1 = this, Form3 = form3 };
form3.Form2 = Form2;
}
void GoToForm2ButtonClick(...){
this.Hide();
Form2.Show();
}
}
class Form2{
public Form1 Form1 { get; set; }
public Form3 Form3 { get; set; }
//event handlers
void GoToForm1ButtonClick(...){
this.Hide();
Form1.Show();
}
void GoToForm3ButtonClick(...){
this.Hide();
Form3.Show();
}
void FormClosing(...){
Form1.Close();
}
}
//event handlers
class Form3{
public Form1 Form1 { get; set; }
public Form2 Form2 { get; set; }
void GoToForm1ButtonClick(...){
this.Hide();
Form1.Show();
}
void GoToForm2ButtonClick(...){
this.Hide();
Form2.Show();
}
void FormClosing(...){
Form1.Close();
}
}
And cue usual commentary about making class names meaningful, rather than just a type name followed by a number

Related

How to make form2 update when value changed in form1

In C# I have two forms "Form1" and "Form2". Form1 creates images that are stored in a folder. Form2 displays the number of images in that folder.
Say I have made 2 images with Form1 and then I open Form2. Form2 now says there are 2 images. Now while keeping both forms open I want to be able to add a new image and Form2 updates. At the moment if I use Form1 to add more images while Form2 is open Form2 continues to display the number of images that were in the folder when Form2 opened.
I have found solutions that involve Form2 closing and reopening but I don't want this. It's jarring for the user having windows opening and closing every time they press a button. I just want Form2 to update live as I make changes with Form1.
Any help would be greatly appreciated!! Thanks!
You can do this with public method in Form2
In Form1 you need to save Form2 object in property
FORM 1
public partial class Form1 : Form
{
public Form2 MyProperty { get; set; }
public Form1()
{
InitializeComponent();
}
// for opening form 2
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.Show();
MyProperty = f2;
}
// adding new image
private void button_ADD_Click(object sender, EventArgs e)
{
MyProperty.updateCounter();
}
}
When you add new image, then you can call the metoh from Form2 to update counter.
In FORM 2 you need crate PUBLIC method to update counter
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
// default value
label1.Text = "0";
}
// update counter
public void updateCounter()
{
label1.Text = (int.Parse(label1.Text) + 1).ToString();
}
}
You may want to take a look at events and delegates. Basically, whenever you do something to Form1 that can affect other forms, you want to trigger an event. In Form2, you have a method which you call whenever that event triggers. This way you can have multiple forms open, and as long as you set them to "listen" to a given event, they can all react.
You can read more about them here, in the Microsoft Documentation.

Control.InvokeRequired - Subject to a race condition

I found a suggested solution to the cross-thread exception issue in another posting and have used it my code below, but I am finding it erratic at best. My tests indicate that it's correct operation is at the mercy of a race condition so I was hoping someone might be able to point out the obvious or provide a more robust solution.
I have a program with two simple forms, where the sole purpose of Form1 is to open Form2. Form2 only contains a RichTextBox and has code that starts a thread that accesses that RichTextBox in an arbitrary way. When execution reaches the InvokeRequired property it is usually (but not always) false, so it goes directly to the RichTextBox access where a cross-thread exception is generated. However, when I call Thread.Sleep(...) just before testing the InvokeRequired property it appears to work properly. However, I hate to use Sleep for this purpose simply because it seems like a kluge that may not always work. Is there a reliable, yet reasonable, way to do cross-thread communication? Thanks.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var form2 = new Form2();
form2.ShowDialog();
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
Thread myThread = new Thread(new ThreadStart(myStartingMethod));
myThread.Start();
}
void myStartingMethod()
{
Test("Hello world!\n");
}
private delegate void myCallback(string text);
private void Test(string text)
{
// If I put Thread.Sleep(...something...) here it works better.
if (myRichTextBox.InvokeRequired)
{
myCallback d = new myCallback(Test);
Invoke(d, new Object[] { text });
}
else
{
// Cross-thread exception usually occurs, but not always.
int x = myRichTextBox.TextLength;
}
}
}

Check if a Winforms BackgroundWorker is active upon opening a form

I currently have a Main form that contains a SplitPanel object. In the left panel, I have several buttons that when clicked, will open a specific form in the right panel.
One of these forms, Reports, has a BackgroundWorker (bgLaborForecast) that does a lot of database table maintenance and building of data tables in the database for reporting purposes. While this BackgroundWorker is running, I can navigate to any other form simply by clicking the appropriate button in the left panel of the Main form. When the BackGroundWorker is done, it displays a MessageBox, no matter where I am in the application.
What I am trying to accomplish is to check if the BackgroundWorker is running when I load the Reports form. I tried
if (bgLaborForecast.IsBusy) {
MessageBox.Show("Busy...");
}
But when I enter the form, the .IsBusy status is always false.
How can I check for the process being active when I load the form?
You need to do interface
public interface IState
{
bool BgWorkerIsWorking { get; set; }
}
then implement it in Form1:
public partial class Form1 : Form, IState
...
private bool _working;
public bool BgWorkerIsWorking
{
get { return _working; }
set { _working = value; }
}
In MainForm declare variable to keep form
Form firstForm;
And in button this
if (first == null)// If form wasn't created
{
first = new Form2();// Create form and show
first.Show();
}
else// If form was created
{
var check = first as IState;// Use form as our interface
if (check.BgWorkerIsWorking)// And check state
{
// Here you do something.
}
}
And at last in Form1 in BgWorker events you do this
private void Bg_DoWork(object sender, DoWorkEventArgs e)
{
_working = true;
// Here is your code
}
private void Bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_working = false;
}
Ofcourse your worker initialization must be this
using (BackgroundWorker bg = new BackgroundWorker())
{
bg.DoWork += Bg_DoWork;
bg.RunWorkerCompleted += Bg_RunWorkerCompleted;
}
And just in case, worker start work after
bg.RunWorkerAsync();
P.S.
This steps must be repeated for each form you create. declare secondForm, thirdForm and so on.
P.P.S.
You can design more complicated algorithm, store created forms in array, add extra fields in interface and so on. This example show basic principle :)

WPF + Windows Forms, open a second Form as child of MainWindow from the first Form

Probably I will not make myself clear in the title, change it if necessary. Now I step to explanation:
I have a program done in WPF (cause graphics). The MainWindow open a first Form with a button, the Form1 do some stuff, changing the UI of the MainWindow and when it is done should open a Form2 that it should also do some things changing the UI of the MainWindow, but I can't open the Form2 through Form1.
Here's some code:
MainWindow.xaml.cs
internal static MainWindow main; //It allows me to put the Form1 in the same context in order to change the UI
private void start_button_Click(object sender, RoutedEventArgs e)
{
Form1 frm = new Form1(this);
frm.Show();
}
Form1.cs
//function that allow me to communicate with the MainWindow
private MainWindow main1 = null;
public Form1(MainWindow callingForm)
{
main1 = callingForm as MainWindow;
InitializeComponent();
}
//Load function
private void Form1_Load(object sender, EventArgs e)
{
//do some stuff changing MainWindow UI
Form2 frm = new Form2(main1); //no compilation error
frm.Show();
}
Now, the Form2 does not open. I also tried to do it calling a void that is in the MainWindow with the Form1 but nothing. What I have to do? NOTE WELL: need to change the UI of the main window also with the Form2 and I need that Form2 is opened only at the end of the Form1 calculations.
public Form1(MainWindow callingForm)
{
main1 = callingForm as MainWindow;
InitializeComponent();
this.Load += Form1_Load;
}
If you are trying to open a window from the MainWindow, I would recommend you to use events to instanciate the window.
It is a bit weird from a structure standpoint to a child view to say to his parent: "Hey, I want YOU to open this form" - yet it is the Form1 that creates and shows it.
I would do something like this:
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void start_button_Click(object sender, RoutedEventArgs e)
{
Form1 frm = new Form1();
frm.MyEvent += Form1EventMethod;
frm.Show();
}
private void Form1EventMethod(object sender, EventArgs e)
{
// You can add here what you need to change in the MainWindow.
Form2 frm = new Form2();
frm.Show();
}
}
Form1:
public partial class Form1 : Window
{
public event EventHandler MyEvent;
public Form1()
{
InitializeComponent();
}
private void show_form_Click(object sender, EventArgs e)
{
if(MyEvent != null)
MyEvent(this, new EventArgs());
}
}
Also this way, you may subscribe more than just the MainWindow if you want to do something when this event is raised. Also, you may pass differents parameters to your subscribers. Your views will be seperated and not dependant on one and another.
I know what you mean, you want to call a window form from a WPF application main window. I've prepared this test Program to help you understand the how to programmatically achieve this.
Consider the following simple program:
1) takes 2 numbers as user input (using TextBoxes).
2) displays the result in a customized window form that pops up as response to a calculate button.
When the user press the 'Calculate' button ( not show in this answer), the program adds the 2 number then displays the result in a customized window form (that acts like a dialog box ).
Assuming this is what you're looking for here's the caculateclick method that responds to the user's click :
In MainWindow.xaml.cs:
private void calculateClick(object sender, RoutedEventArgs e)
{
try
{
int leftHandSide = System.Int32.Parse(lhsOperand.Text); //user input
int rightHandSide = System.Int32.Parse(rhsOperand.Text); // lhsOperand and rhsOperand are just references to TextBoxes that takes user input
string result_txt=addValues(leftHandSide,rightHandSide).ToString(); // add two numbers that returns the result
//addValues method is not defined in my answer, it's just a method I use in this example to add 2 values together
CustomDialog.show(result_txt); // diplays result in a custom window form
}
catch (FormatException exception )
{
exception.ToString();
}
All you need to do is to create a class for the user-defined Form then inside this class define the show static method which main window is going to call from the caculateClick() method seen above ( I will show you the code just keep reading ). Notice the call to the static method 'CustomDialog.show(result_txt);'
CustomDialog is a partial class ( just like the Form1 class you created) displays the result of the addition, in a user-defined form that's going to act like a dialog box. ( I assume you know to design the user interface for the form using the design view in Visual Studio ).
Here's the code for CustomDialog:
public partial class CustomDialog : Form // CustomDialog
{
static CustomDialog MsgBox; //used in show method as a reference
static DialogResult result; //retuned by show method
public CustomDialog()
{
InitializeComponent();
result = DialogResult.OK; //initialise the result according to the function of the button, here I only use the OK button to close the dialog }
public static DialogResult show (string calculationResult) // show static method called by the Main Window
{
MsgBox = new CustomDialog();
MsgBox.result_text.Text = calculationResult; // result_text is a textbox implemented in the form using design view
MsgBox.StartPosition = FormStartPosition.CenterParent; // center dialog in the middle of the parent (WPF application main window)
MsgBox.ShowDialog(); // displays the dialog
return result; // returns the result so the button event is handled
}
We define two fields in the CustomDialog class,
static CustomDialog MsgBox; static DialogResult result; the MsgBox object is used in the static method called 'show ( string calculation )' in order to display the dialog box and return the 'result' variavle which is a DialogResult object. ( DialogResult is returned so we can see the result in the TextBox and handle event clicks ).
Important NOTE:
To avoid any errors it's crucial that you maintain the form using the following methods that does some background work and handle button clicks and label clicks on the dialog ( even if your not going to use them, you can just leave them empty). Also make sure that 'CustomDialog' and the 'MainWindow' for the WPF application is implemented in the SAME namespace
Consider adding the following methods to your CustomDialog class as good practice in case you face some errors :
private void Form1_Load(object sender, EventArgs e) // the name of this method depends on what you name it when you first create the form
{
}
private void button1_Click(object sender, EventArgs e)
{
result = DialogResult.OK; // handle result returned by show() method, here I only used an OK button
MsgBox.Close(); //closes the dialogbox
}
private void label1_Click(object sender, EventArgs e)
{
}
Design Notes: Be careful when you create the form in Design View you must assign the button you dragged into it as an Acceptbutton in the Misc section within the Properties tab in Visual Studio. Also keep in mind that I refer to MessageBox, Dialogbox and Form as the same entity. Actually if you carefully examine my code, the 'MsgBox' static variable is a CustomDialog Object.
Sorry for the long post but hope this helps ! if you need screen shots to show you that it works and if it's helpful, let me know.

making a cross-thread call to hide a form visual c#

Update: Solution arrived since and is now accepted. Due to the clarity of the pseudo codes on this question the solution took only a few seconds. Trying to figure out the solution from the other seemingly "duplicate" questions took a lot of time already. I had the solution in 20 minutes from the moment I asked this question. So it is not a duplicate, probably the clearest of them all to date.
While several similar questions have been asked on this, I was still not able to put together a working code from them. My main application is not on a form, but in a separate class that is a plugin and executed by a host application.
So while it seems a duplicate question it is not. Please read on.
I have my main class doing stuff. Then I have a form that displays information to the user. I need to hide this form when the user switches away from the application (host application loses focus).
I am using very limited APIs so the only methods I have at my disposal events triggered by the host application.
I created a timer that fires every 100ms and checks whether the user had the application in focus. And if not a command is sent to the form to hide itself.
The reason for this approach is because the host application loosing focus is just one of the many scenarios that I need to hide the form and I need to channel all these through the same exact method.
All works from within the rest of the classes (the Hide() method is called from the rest of the application no problem.
But it does not work when the timer calls the Hide() method, because the timer is on a different thread when it fires. So the call becomes a cross-thread call.
The very specific question is that I need an exact sample code how to make this call from the timer event handler to the form's Hide() method thread-safe with Invoke.
Thanks.
This is the timer:
private void Controllel_Opened(object sender, EventArgs e)
{
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 50;
myTimer.Start();
}
public static System.Timers.Timer myTimer = new System.Timers.Timer();
// This method checks different scenarios when the tool tip should be hidden and calls the hiding method
public static void DisplayTimeEvent(object source, System.Timers.ElapsedEventArgs e)
{
FormHandler.Hide();
}
Then the "FormHandler" class:
public static class FormHandler
{
private static Form1 frm1 = new Form1();
public delegate void Form1HideEventHandler();
public static event Form1HideEventHandler Form1Hide;
public static void Hide()
{
if (Form1Hide != null)
{
Form1Hide();
}
}
}
Then the form's code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
FormHandler.Form1Hide += FormHandler_Form1Hide;
}
private void FormHandler_Form1Hide()
{
Hide();
}
}
I would like to get a solution with exact code if possible. Thanks.
private void FormHandler_Form1Hide()
{
if (InvokeRequired)
{
this.Invoke(new Action(() => { FormHandler_Form1Hide(); }));
}
else
{
Hide();
}
}
You need to use System.Windows.Forms.Timer instead. That will invoke back onto the dispatcher thread.

Categories