C# Forms Show/Hide - c#

In VB.NET, you can freely Hide/Show/ShowDialog (for a fresh form). But in C#, you always have to create new form instance to do this. Even your previosly hidden form can't show back, you have to create new instance again. Resulting in your previously hidden form will run as background process until you kill it.
My question is how can I create a function that will unhide a form that doesn't need a new instance when I close the current form using the default exit in form's title bar.
Edit: I solved it on my own. It's just confusing. And what difference between c# and vb.net is that I never have to use form_closed in vb.net. Why do I always see it in c#.

You can always display an hidden form in C#. There are many ways to this.
For example you could check the Application.OpenForms collection that keeps track of all forms owned by your application and still not closed. (An hidden form is not closed). This approach means that you need to identify you form between the ones included in the collection OpenForms
MyFormClass f = Application.OpenForms.OfType<MyFormClass>().FirstOrDefault();
if ( f != null) f.Show();
Another approach, more lengthy, is to keep track yourself of the form to hide/show with a variable inside your application. This requires extra care in handling correctly the various situations in which you global variable becomes invalid (what if the user closes effectively the form instead of hiding it?)
This is an example that you can test easily with LinqPAD
// Your form instance to hide/show
Form hidingForm = null;
void Main()
{
Form f = new Form();
Button b1 = new Button();
Button b2 = new Button();
b1.Click += onClickB1;
b2.Click += onClickB2;
b1.Location = new System.Drawing.Point(0,0);
b2.Location = new System.Drawing.Point(0, 30);
b1.Text = "Hide";
b2.Text = "Show";
f.Controls.AddRange(new Control[] {b1, b2});
f.ShowDialog();
}
void onClickB1(object sender, EventArgs e)
{
// Hide the global instance if it exists and it is visible
if(hidingForm != null && hidingForm.Visible)
hidingForm.Hide();
}
void onClickB2(object sender, EventArgs e)
{
// Create and show the global instance if it doesn't exist
if (hidingForm == null)
{
hidingForm = new Form();
hidingForm.Show();
// Get informed here if the user closes the form
hidingForm.FormClosed += onClosed;
}
else
{
// Show the form if it is not visible
if(!hidingForm.Visible)
hidingForm.Show();
}
}
void onClosed(object sender, FormClosedEventArgs e)
{
// Uh-oh.... the user has closed the form, don't get fooled...
hidingForm = null;
}
Well, OpenForms seems a lot better.

Related

Update Controls in Form 1 from Form 2 that was Created by Form 1

I checked and found this article that is referred to MANY times in this type of question, and it is NOT the answer I want...
I have a form, Form_Main frmMainPage, it creates Form_Status frmStatus.
When it does so, I disable the controls on frmMainPage so the user can't mess with things while things are processing.
When things are done processing they can close the frmStatus window to go back to frmMainPage to continue working. I cannot re-enable the controls from frmStatus. I tried to use the frmMainPage_Enter, but it goes crazy when it first loads, so that isn't really an option.
I have the following in Form_Main.cs
public void EnableForm() {
this.gridData.Enabled = true;
this.txtLocation.Enabled = true;
this.txtSupplier.Enabled = true;
this.txtItem.Enabled = true;
this.FillTable("", "", "");
}
When I use this (per article above):
private void btnClose_Click(object sender, EventArgs e) {
Form_Main f2 = new Form_Main();
f2.Show();
f2.EnableForm();
this.Close();
}
It creates a second Form_Main, which is not what I want. I want to know how to change the controls in the existing form.
Edit: No, it is not this that was also suggested. Most of the answers deal with changing controls on Form 2 from Form 1 when Form 2 is created by Form 1. In my case I need to do the opposite and change controls on Form 1 FROM Form 2, which was created by Form 1. Kind of a circular thing.
I can think of a couple of ways to do this. First (and most common) is to show the second form modally (which means that the first form's code pauses while the second form's code is running):
private void button1_Click(object sender, EventArgs e)
{
var statusForm = new frmStatus();
// ShowDialog will prevent frmMainPage from being accessible.
// This form's code will pause at the next line until the second form is closed
statusForm.ShowDialog();
}
There are occasions where you want to have both forms accessible at the same time. If this is the case, another method would be to add an event handler for the second form's FormClosed event, where you can re-enable the controls on the first form. This will allow both forms to be accessed at the same time:
private void button1_Click(object sender, EventArgs e)
{
var statusForm = new frmStatus();
// Add an event handler for the second form's FormClosed event, and
// put code in that event handler to re-enable controls on this form
statusForm.FormClosed += statusForm_FormClosed;
// Disable our controls on this form and show the second form
DisableForm();
statusForm.Show();
}
private void statusForm_FormClosed(object sender, FormClosedEventArgs e)
{
// When the second form closes, re-enable controls on this form
EnableForm();
}
private void DisableForm()
{
this.gridData.Enabled = false;
this.txtLocation.Enabled = false;
this.txtSupplier.Enabled = false;
this.txtItem.Enabled = false;
}
public void EnableForm()
{
this.gridData.Enabled = true;
this.txtLocation.Enabled = true;
this.txtSupplier.Enabled = true;
this.txtItem.Enabled = true;
this.FillTable("", "", "");
}
you dont need to do this disable enable.
you just need to show your new from with ShowDialog(); like this:
frmStatus.ShowDialog();
instead of just:
frmStatus.Show();

Error when calling Form.Show() a second time

I have a main form with a button.
OnClick of that button does the following...
Form AllBook = new Form();
//Does some other processing and SQLReader querying.
AllBook.Show();
Afterwards I close the form. When I try to show it again I receive this error.
System.ObjectDisposedException: 'Cannot access a disposed object.
Object name: 'Form'.'
// objects
Form AllBook = new Form();
ComboBox booksList = new ComboBox();
ComboBox chapters = new ComboBox();
Panel topPannel = new Panel();
Panel txtPannel = new Panel();
TextBox mainText = new TextBox();
private void button1_Click(object sender, EventArgs e)
{
// add objects to form
AllBook.Controls.Add(topPannel);
topPannel.Controls.Add(booksList);
// get combobox items from another Method
int chapterCount = countChapters(43);
for (int i = 1; i <= chapterCount; i++) {
chapters.Items.Add(i);
}
topPannel.Controls.Add(chapters);
AllBook.Controls.Add(txtPannel);
txtPannel.Controls.Add(mainText);
AllBook.Show();
}
// count books chapters
public int countChapters(int bookNum) {
int chapter = 0;
switch (bookNum) {
case 1:
chapter = 50;
break;
case 2:
chapter = 40;
break;
case 3:
chapter = 27;
break;
case 4:
chapter = 36;
break;
.....
}
#H.G. Sandhagen and #LarsTech are correct.
Closing should dispose of the form. If you want to show it again you need to...
Form AllBook = new Form();
AllBook.Show();
...every time.
Edit: Adding further clarification.
Closing the form in a way calls Disopose() as well.
Source:
When a form is closed, all resources created within the object are
closed and the form is disposed. You can prevent the closing of a form
at run time by handling the Closing event and setting the Cancel
property of the CancelEventArgs passed as a parameter to your event
handler. If the form you are closing is the startup form of your
application, your application ends.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.close?view=netframework-4.7.2
By default, closing a form will dispose it. But you can change this behavior by overriding the Closing event.
So you should either:
Create a new instance of your form each time you show it, and allow WinForms to dispose it when the user closes it; or
Override the Closing event, so that closing causes the form to get hidden instead of disposed, at which point you'll be able to show the same form multiple times:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Hide();
e.Cancel = true;
}
Note that if you choose the second option, it will be up to you to call Dispose() on the form when you're done with it.
When the form is closing it automatically gets disposed, So you can refresh it before showing it again by using:
Allbook.Refresh();

C# Using One Panel As a Placeholder For Multiple Forms

I apologize if this has been addressed already, but I could not find a case that fit my exact situation. So here goes...
I have a MainForm that contains a toolStrip1 docked to the left that functions as a vertical navigation bar. I have a panel (pnlMain) filling up the remainder of the form. I want to use pnlMain to display different forms which are made up of win form classes. Right now, I can click on the labels/buttons on toolStrip1 to display different forms within pnlMain.
private void tsLblCustomers_Click(object sender, EventArgs e)
{
hidePanels();
CustomerReport cr = new CustomerReport();
cr.TopLevel = false;
cr.AutoScroll = true;
cr.BackColor = Color.White;
pnlMain.Controls.Add(cr);
cr.Show();
}
What I want to do now is display additional forms within pnlMain by clicking on a button on another form rather than a label/button on toolStrip1. Some of my forms are as follows: CustomerReport, AddCustomer, EmployeeReport, AddEmployee. The Report forms are linked to my tool strip buttons. The Add forms are linked to buttons on the Reports forms. I tried several things including the following:
1) On CustomerReport, I tried creating an instance of MainForm, then I'll create an instance of AddCustomer, and then add that instance to the panel on MainForm.
2) I also tried creating a method in MainForm to create the instance of AddCustomer, and then call that method from the Add button on CustomerReport. Even though the code was the same as the toolstrip buttons on MainForm, it did not work.
I tried different variations of hiding forms, showing forms, clearing the panel, setting Visible to true or false, and I can't get it to work right. In some cases, I've managed to hide the CustomerReport, but AddCustomer will not come up. At some point I think I created a NEW instance of MainForm and my code wasn't impacting the original form that is already open. I'm just lost. Should I be using a different design? Originally I set up my application to just hide one form then show the other but I read that that is a 'terrible design'.
This sounds very similar to this thread here: Creating Form Inside the Form
You'd want to look into MDI.
Although it sounds like you're aiming for one cohesive interactive window. Otherwise, if you just want separate windows to popup, you can create properties within that other form and read them after returning a DialogResult. I'm not sure why this would be bad design without knowing more about the context of the program.
//Optionally do a hide(); here.
AddCustomer customer = new AddCustomer();
DialogResult result = customer.ShowDialog();
if(result == DialogResult.OK)
{
var name = customer.Name;
//More properties or whatever here.
}
//The properties would still be accessible here, too.
I ended up keeping the toolstrip nav bar on the left side of the primary window, and I created a panel in the main part of the window. All forms are displayed in the panel. Each time one of the label options in the nav bar is clicked on, the current form is cleared off the panel and the active form is displayed.
private void tsLblCustomers_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
CustomerReport cr = new CustomerReport();
cr.TopLevel = false;
cr.AutoScroll = true;
cr.BackColor = Color.White;
pnlMain.Controls.Add(cr);
cr.Show();
}
private void tsLblEmployees_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
EmployeeReport emp = new EmployeeReport();
emp.TopLevel = false;
emp.AutoScroll = true;
emp.BackColor = Color.White;
pnlMain.Controls.Add(emp);
emp.Show();
}
private void tsLblVendors_Click(object sender, EventArgs e)
{
pnlMain.Controls.Clear();
VendorReport vend = new VendorReport();
vend.TopLevel = false;
vend.AutoScroll = true;
vend.BackColor = Color.White;
pnlMain.Controls.Add(vend);
vend.Show();
}
private void MainForm_Load(object sender, EventArgs e)
{
WelcomeForm welcome = new WelcomeForm();
welcome.TopLevel = false;
welcome.AutoScroll = true;
welcome.BackColor = Color.White;
pnlMain.Controls.Add(welcome);
welcome.Show();
}

Resize all the windows of an application

I am creating a Windows app that will have multiple forms in it. Initially, I have set WindowState Maximized for all.
My problem here is that when I minimise one form, size of the other forms stay same. So, if I minimize the main screen go to the next screen, the size of the next screen remains unchanged. All I need to do is change size of all the windows at once.
I tried this:
Mainscreen mainscreen = new Mainscreen();
this.WindowsState = mainscreen.WindowsState;
But I am finding a way to do it for all screens.
A very ugly demo for your reference
private List<Form> Windows { get; set; }
public Form1()
{
InitializeComponent();
this.Text = "Main Window";
this.Windows = new List<Form>();
var defaultSize = new Size(200, 100);
for (var i = 0; i < 3; i++)
{
var form = new Form() { Size = defaultSize, Text = "Resize Me" };
var suppressEvent = false;
form.SizeChanged += (sender, e) =>
{
if (suppressEvent)
return;
suppressEvent = true;
defaultSize = (sender as Form).Size;
foreach (var otherForm in this.Windows)
{
if (otherForm != sender as Form)
otherForm.Size = defaultSize;
}
suppressEvent = false;
};
this.Windows.Add(form);
form.Show();
}
}
If the minimization is the concern and all sub forms launch from a previous form the quickest way would be to have the main form be the owner of the sub form.
If you make a simple project with two forms Form1 and Form2 and add a button onto Form1 that launches Form2 it would look like the following.
private void button1_Click(object sender, EventArgs e)
{
Form2 lForm = new Form2();
lForm.Show(this);
}
Passing "this" (which is reference to Form1) into the constructor of show will make sure that when it minimizes the subform (Form2) will also minimize. You can also change the setting of Form2 and set
lForm.ShowInTaskbar = false;
if you wanted to help make it clear that all of the forms are tied together.
If you are instead talking about having all forms maintain the same size regardless of who started the change and what the size ends up being then it gets a bit trickier and the simplest way would be to make a form manager of some sort that listens to the OnSizeChanged event and updates all forms whenever any form updates. Keep in mind with this that you'll also need to have a test in the form to know whether or not it is the one that started the update otherwise you would get in a sort of infinite loop where Form1 updates causing Form2 to update which then sends out another message which tries to make Form1 update etc...

Is it possible to define a class-scope objet from a method?

This may seem totally unreasonable to ask, but I have been designing a multi-panel, real device simulator, that has many different screens and my current approach is to add all the screen objects from the code only and dispose them when I switch to another screen.
I have some fixed objects, that are the real device buttons that are already defined and in place. The thing is, I am separating each panel construction in methods, for example: buildLogin(), buildMainScreen(), etc, and I need to edit some of the screen objects from those methods, like changing the color of an enabled function label to green if enabled or white if disabled.
My question is: would it be possible to declare an object from a method that would be accessible in the whole class, like if it were defined in the variable declaration section? It would be something like the GLOBAL in PHP.
I can't declare it on top of everything like they would always be because when I dispose the objects, I can't "re-create" them, because of parenting, or re-using a disposed object or something...
[EDIT] Sample code:
public partial class frmMain : Form
{
//I could as well do this:
//Button button1 = new Button();
public frmMain()
{
buildLogin();
}
private void buildLogin()
{
Panel panel1 = new Panel();
Controls.Add(panel1);
//But then, there is no way to do this:
// if (button1.IsDisposed == true) //because of the panel, or smthing
Button button1 = new Button();
panel1.Controls.Add(button1);
button1.Click += (s, f) => { panel1.Dispose(); buildMainMenu(); };
}
private void buildMainMenu()
{
Panel panel2 = new Panel();
Controls.Add(panel2);
Button button2 = new Button();
panel2.Controls.Add(button2);
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
//Here, I have no access to the objets I've created programatically.
//button1.Text = "Text changed!";
}
}
If you want to make sure things are always completely dynamic and always done in the code behind, you may want to look at searching for the controls you've created in the Controls collection.
For example, make sure to give button1 an ID value (button1.ID="textUpdatedButton") that will identify it uniquely from other controls you create. Then use FindControl or search on the Controls collection to find the control with the ID you want to locate in your event handler.
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
Control control = this.FindControl("textUpdatedButton");
if(control != null && control is Button){
Button button1 = (Button)control;
button1.Text = "Text changed!";
}
}
Alternatively, to make things look more like a variable, you can use a Property to hide the control finding (as mentioned previously):
private Button Button1 {
get { return (Button)this.FindControl("textUpdatedButton"); }
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
if(this.Button1 != null){
this.Button1.Text = "Text changed!";
}
}
The actual implementation will vary with how you build up your controls, but essentially this approach can let you build everything dynamically in your code behind if you need to do it that way. Just remember to use identifiers to let you find things later.
Define your object at the class level, as Static. This way it will be accessible from all methods of all instances of the class(disposing an instance will not affect it).

Categories