Navigate from a child form to its sibling form - c#

In a parent form A, there's the following code to call a child form "B":
Window frmChildB;
frmChildB = new FormB();
frmChildB.ShowDialog();
In the same form: the following code to call a child form "C":
Window frmChildC;
frmChildC = new FormC();
frmChildC.ShowDialog();
Now I want to make a button in form B, so that if I click that button, it automatically navigate to form C.
Using a reference object of form C in form B like in the answer of this question should be avoided if possible. The reason is that there are more than ten forms like B, C... and each of them must be able to navigate to another. Having 10 form-referenced objects inside a form isn't good.
I think there must be some way to achieve the effect. Does anyone know about this?

If I understand your question correctly, you would like to have a single instance of each of the forms and just navigate back and forth between them.
If this is what you want, you can implement a static FormManager class that creates instances of the forms and shows them as needed. You can even use an enum to further reduce the complexity.
Here is an example of this class (it will need some additional work, but should give you a good idea):
public class FormManager
{
private static FormB m_FormB;
public static FormB formB
{
get
{
if (m_FormB == null)
{
m_FormB = new FormB();
}
return m_FormB;
}
}
private static FormC m_FormC;
puClic static FormC formC
{
get
{
if (m_FormC == null)
{
m_FormC = new FormC();
}
return m_FormC;
}
}
public enum FormId
{
FormB,
FormC
}
public static Form ShowForm(FormId whichForm)
{
Form oForm;
switch (whichForm)
{
case FormId.FormB:
oForm = FormManager.formB;
break;
case FormId.FormC:
oForm = FormManager.formC;
break;
default:
oForm = null;
break;
}
if (oForm != null)
{
oForm.ShowDialog();
}
return oForm;
}
}
This can be called from the child forms as:
FormManager.ShowForm(FormManager.FormId.FormB);

Try creating an Event in frmChildB and Subscribe to it in the Parent. You can then do what you want without having an reference to frmChildC in frmChildB.
Look at this MSDN link;
This is very rough but should give you an idea.
creating the event in the child forms
public delegate void SwapEventHandler(object sender, EventArgs e);
public partial class Form2 : Form
{
public event SwapEventHandler Swap;
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Swap(sender, e);
}
}
Consumming it in the Parent Form
private void button1_Click(object sender, EventArgs e)
{
frmChildB = new Form2();
frmChildB.Name = "frmChildB";
frmChildB.Swap += new SwapEventHandler(frmChildB_Swap);
frmChildB.ShowDialog();
}
private void frmChildB_Swap(object sender, EventArgs e)
{
frmChildB.Swap -= new SwapEventHandler(frmChildB_Swap);
frmChildB.Close();
frmChildB.Dispose();
frmChildB = null;
frmChildC = new Form2();
frmChildC.Name = "frmChildC";
frmChildC.Swap += new SwapEventHandler(frmChildC_Swap);
frmChildC.ShowDialog();
}
void frmChildC_Swap(object sender, EventArgs e)
{
frmChildC.Swap -= new SwapEventHandler(frmChildC_Swap);
frmChildC.Close();
frmChildC.Dispose();
frmChildC = null;
frmChildB = new Form2();
frmChildB.Name = "frmChildC";
frmChildB.Swap += new SwapEventHandler(frmChildB_Swap);
frmChildB.ShowDialog();
}

At a primitive level it appears that you would benefit more from using the standard 'Wizard' pattern than having separate forms for each question. The exception being that instead of just having the typical next and back buttons, you should have buttons to jump to any of the questions. Here is a good tutorial that will walk you through the normal steps of creating a wizard.

Related

How to save and open the content in the one of subforms separately?

There are two forms, a MainForm and a GraphicsForm.
In MainForm, there are "New" and "Save", "Open" buttons. When clicking the "New", a GraphicsForm created (When the "New" is clicked multiple times, multiple GraphicsForms are created).
The question is, when created multiple GraphicsForms, and the user only wants to save the content in one of them or open a content file to one of them, How to implement this?
MainForm.cs
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private ToolStripMenuItem _winMenuItem = new ToolStripMenuItem();
private GraphicsForm _graphicsForm;
private int _counter = 1;
private ContentDoc _contentDoc = new ContentDoc();
private void New_Click(objec sender, EventArgs e)
{
_winMenuItem.Name = "Win";
_winMenuItem.Text = "Windows";
int item = MainMenuStrip.Items.IndexOf(_winMenuItem);
if (item == -1)
{
MainMenuStrip.Items.Add(_winMenuItem);
MainMenuStrip.MdiWindowListItem = _winMenuItem;
}
_graphicsForm = new GraphicsForm(_contentDoc);
_graphicsForm.Name = string.Concat("Win_", _counter.ToString());
_graphicsForm.Text = _graphicsForm.Name;
_graphicsForm.MdiParent = this;
_graphicsForm.Show();
_graphicsForm.WindowState = FormWindowState.Maximized;
_counter++;
}
private void Save_Click(object sender, EventArgs e)
{
... // here
}
private void Open_Click(object sender, EventArgs e)
{
... // here
}
}
GraphicsForm.cs
public partial class GraphicsForm : Form
{
//ContentDoc is a class to manage all the graphics drawn by the user in the form.
private ContentDoc _contentDoc = new ContentDoc();
public GraphicsForm(ContentDoc contentDoc)
{
InitializeComponent();
_contentDoc = contentDoc;
}
private Canvas_MouseDown()
{
}
private Canvas_Paint()
{
}
...
The parent form has an ActiveMdiChild property, so you can use the to access the currently-selected GraphicsForm instance:
var activeGraphicsForm = ActiveMdiChild as GraphicsForm;
There are other variations you might use, e.g. pattern matching, depending on the specific details and your preference.
You can then put your saving logic in a public method in GraphicsForm and call it from the parent form. Alternatively, you can put your saving logic in the parent form and expose the data to be saved via one or more public properties in GraphicsForm.

How to find the name of a second instance of a form C#

For example if I have a form called frmOne and I am there and I do some things then click a button event and it goes and takes me to a second form called frmTwo. Then I am in frmTwo and I do some things then I click a button in frmTwo and this button creates a new instance of frmOne. Since now I have two forms of frmOne open what will Visual Studios call the second instance of frmOne. I need to figure this out because I need to access it in code. I have tried using frmOne as the name to reference it in code but it doesnt work on the second instance. Any ideas how I can find this name or what Visual Studios calls it? I am assuming Visual Studios does something like calling the second instance frmOne1 or something like that. Thanks in advance.
From the example above here below frmShoppingCart is my first form. I have two instances of it open. I am trying to close the second instance from the closing event of another form. I can close the first instance with the code below and I can use it to close the second instance if I knew what the name was of the second instance which I am assuming is different from frmShoppingCart. I just am assuming Visual Studios is calling the name of my second instance of frmShoppingCart something else than frmShoppingCart.
private void frmViewer_FormClosing(object sender, FormClosingEventArgs e)
{
//close shopping cart form and refresh and open the shop now form
frmShoppingCart obj = (frmShoppingCart)Application.OpenForms["frmShoppingCart"];
obj.Close();
}
It's not clear why you need to reference a form indirectly by name. You could instead hold a reference directly to the form.
If you still want to reference by name, you can, but you need some other piece of data to disambiguate the form as #Steve said in the comments. To do that you could add another property to the form. Here's a small demo form
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
private void button1_Click(object sender, EventArgs e)
{
var f = new Form1 { Instance = ++Counter };
f.ShowDialog();
}
private void button2_Click(object sender, EventArgs e)
{
var forms = Application.OpenForms;
}
public int Instance { get; set; }
public static int Counter { get; set; }
}
that shows, under the debugger with a breakpoint after
var forms = Application.OpenForms;
that you can see the disambiguation.
Note that searching by name alone will return the first form with that name. To get the correct one, do a search using linq against Application.OpenForms (again, credit #Steve):
var myForm = Application.OpenForms
.FirstOrDefault(x => x.Name == "Form1" && x.Instance == 1);
This is but one way to find the form. You could instead change Name when you create the form. Here's an example using the Counter above.
private void button1_Click(object sender, EventArgs e)
{
var f = new Form1();
f.Name += ++Counter;
f.ShowDialog();
}
This gives us
You can use Singleton to have a list of named Forms opened from the first ...
public partial class Form1 : Form {
public FormsOpened Forms => FormsOpened.Instance;
public Form1() {
this.InitializeComponent();
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
for (int i = 0; i < 10; i++) {
var f = default(Form);
// Form2 is a simple empty form
this.Forms.formsOpened.Add(f = new Form2() { Name = "Pippo" + i });
f.Show();
}
}
protected override void OnClosing(CancelEventArgs e) {
base.OnClosing(e);
foreach (var f in this.Forms.formsOpened) {
f.Close();
}
}
}
public sealed class FormsOpened {
private static FormsOpened instance = null;
public List<Form> formsOpened = new List<Form>();
private int counter = 0;
public static FormsOpened Instance {
get {
if (instance == null) {
instance = new FormsOpened();
}
return instance;
}
}
private FormsOpened() {
this.counter++;
}
}
In this way in every class you want you can access the FormsOpened Forms => FormsOpened.Instance and use it ;)

calling button click function from another class

i currently have one form and a few user control, i have a panel set from the Form1 - mainpanel, so that whenever i click a button, one of the user control will be showing on the mainPanel.
my problem is that one of the control(InfoSetting) has some other new buttons, and when i click it I would like the mainPanel to show the new user control. However, right now all other buttons from Form1 is working fine, but when I click on the InfoSetting button, nothing seems to show up.
here is my code from Form1
public partial class Form1 : MetroFramework.Forms.MetroForm
{
InfoSettings infoSetting;
CashSales cashSales;
PriceSetting priceSetting;
public Form1()
{
InitializeComponent();
infoSetting = new InfoSettings();
cashSales = new CashSales();
priceSetting = new PriceSetting();
}
protected void ButtonClicked(object sender, EventArgs e)
{
buttonHandler(sender);
}
public void buttonHandler(object sender)
{
Button button = sender as Button;
mainPanel.Controls.Clear();
if (button != null)
{
switch (button.Name)
{
case "HomeBtn":
infoSetting.Dock = DockStyle.Fill;
mainPanel.Controls.Add(infoSetting);
break;
case "cashSalesBtn":
cashSales.Dock = DockStyle.Fill;
mainPanel.Controls.Add(cashSales);
break;
case "settingBtn":
infoSetting.Dock = DockStyle.Fill;
mainPanel.Controls.Add(infoSetting);
break;
case "priceSettingBtn":
cashSales.Dock = DockStyle.Fill;
mainPanel.Controls.Add(cashSales);
break;
default:
mainPanel.Controls.Clear();
break;
}
}
}
and here is the code from the InfoSetting user control
public partial class InfoSettings : UserControl
{
public InfoSettings()
{
InitializeComponent();
}
private void priceSettingBtn_Click(object sender, EventArgs e)
{
Form1 form1 = new Form1();
form1.buttonHandler(sender);
}
}
as stuartd said you can add a call to your parent Form:
public partial class InfoSettings : UserControl
{
public InfoSettings()
{
InitializeComponent();
}
private void settingBtn_Click(object sender, EventArgs e)
{
Form1 form1 = (Form1) this.Parent;
form1.buttonHandler(sender);
}
}
It would probably be better to have a common class that you can access in both form and user control and have the code you want to run in a method there. Something like:
public class FormHelper
{
public void CodeIWantToRunIn2Places()
{
// Implementation Here
}
}
You can implement the above as a singleton so you have access in both places. Then in your form classes you could do:
FormHelper.Instance.CodeIWantToRunIn2Places();
In your event handler as well as in your user control.
You can make the Form 1 as "static" and also the method buttonHandler(object sender) as "static" as well.
Then inside the priceSettingBtn_Click, call the below function directly
Form1.buttonHandler(sender);
I would have a global object with a pointer to all the forms in the application.
public Global
{
public Form1 MainForm;
public Dialog1 D1;
...
}
public static Global = new Global();
In as each form is created
Global.MainForm = form1;
then you can go
private void priceSettingBtn_Click(object sender, EventArgs e)
{
Global.MainForm.buttonHandler(sender);
}
As other have said , its probably better to not call the click handler, its better to have this code and the real click handler call a business logic level function

How to write a class method to dispose an existing form in c#

I have two forms in my project, form1 and form2. I have added a new class to my project. It has a method which accepts a form object and it should be able to do following things.
show the accepted form object (frm.ShowDialog())
When I press enterkey on the displayed form (frm) it should close. (without adding code to form2 s keydown event). it should be handled by the newly added class.
I tried to add new keyeventhandler but I don't know how to proceed from here. (Dispose() doesnt work)Please help me to solve this. Thank you.
class Class1
{
public static void SearchResultBox(Form2 frm)
{
frm.KeyDown += new KeyEventHandler(frm_KeyDown);
frm.ShowDialog();
}
static void frm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode==Keys.Enter)
{
}
throw new NotImplementedException();
}
}
You need to keep a reference to the form passed in and then use that reference to close the form
class Class1
{
private static Form2 _frm = null;
public static void SearchResultBox(Form2 frm)
{
_frm = frm;
frm.KeyDown += new KeyEventHandler(frm_KeyDown);
frm.ShowDialog();
}
static void frm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode==Keys.Enter)
{
if(_frm != null)
{
_frm.Close();
_frm.Dispose();
}
}
}
}
create your own Form class and use AcceptButton, like this
public class MyOwnForm : Form
{
private override OnLoad(...)
{
base.OnLoad(..);
AcceptButton = yourOkButtonObject;
}
}
where yourOkButtonObject is the object of the "OK" buton that I suppose present on your form. If not any other button that confirms the form.
Wouldn't frm.close(), and frm.dispose() perform the task you're asking for.
static void frm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode==Keys.Enter)
{
frm.close();
frm.dispose();
}
throw new NotImplementedException();
}

How to check from which button I called form in C#?

I have "formA" and 2 buttons on it (button1 and button2). What I want to do is:
When I click on button1 to call "formB" display text written in label1.
When I click button2 to call the same form ("formB") hide label1 and display label2.
The problem is that I don't know how to check what button is clicked on "formA".
Edit: Thanks very much folks for the quick answer. Problem is solved!
This is where events come in handy:
public class FormA
{
private void button1_Click(object sender, EventArgs e)
{
formB.Button1WasClicked();
}
private void button2_Click(object sender, EventArgs e)
{
formB.Button2WasClicked();
}
}
public class FormB
{
public void Button1WasClicked()
{
label2.Visible = false;
label1.Visible = true;
label1.Text = "Button 1 was clicked!";
}
public void Button2WasClicked()
{
label1.Visible = false;
label2.Visible = true;
label2.Text = "Button 2 was clicked!";
}
}
button1 and button2 have their own separate Click event handlers. This way we can differentiate the two when they are clicked.
If you have the same event handler for both buttons (as mentioned in one of the comments), you can identify them with the sender parameter using:
Object.ReferenceEquals(sender, button1);
or
Object.ReferenceEquals(sender, button2);
Then your code would look like this:
private void button_Click(object sender, EventArgs e)
{
if (Object.ReferenceEquals(sender, button1))
{
formB.Button1WasClicked();
}
else
{
formB.Button2WasClicked();
}
}
FormB can't find out, the buttons are a private implementation details of FormA. They might not even be a button, surely you are going to add a menu or a toolbar to FormA some day.
The workaround becomes much simpler if you stop thinking of "calling a form". You never call a form, you create an instance of it. And then you make it visible by calling its Show() method. Lots of things you can do in between those two steps.
Add a public method to FormB. For lack of a better name:
public void MakeLabel2Visible() {
this.label1.Visible = false;
this.label2.Visible = true;
}
Now it becomes simple. Implement button2's Click event handler like this:
private void button2_Click(object sender, EventArgs e) {
var frm = new FormB();
frm.MakeLabel2Visible();
frm.Show();
}
Adding another constructor to a form that lets you initialize it differently is another very common approach. These are just classes, standard programming techniques are appropriate.
Because you are using winforms you can do all this very easily due to the fact that you have a stateful environment.
Assuming a very basic set up with:
event handlers in the code behind of form a
a reference to an instance of form b in form a (or the button click creating such an instance)
a method to use in form b to pass it data
Your code will be something like this:
public partial class FormA : Form
{
private FormB formB;
public FormA()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (formB == null || formB.IsDisposed)
{
formB = new FormB();
}
formB.UpdateLabel("Button A");
formB.Show();
}
private void button2_Click(object sender, EventArgs e)
{
if (formB == null || formB.IsDisposed)
{
formB = new FormB();
}
formB.UpdateLabel("Button B");
formB.Show();
}
}
public partial class FormB : Form
{
public FormB()
{
InitializeComponent();
}
public void UpdateLabel(string message)
{
label1.Text = message;
}
}
Of course, there are lots of improvements to this - using events and alerts more intelligently and refactoring to remove duplication, but this is a basic example of the sort of things you can do.

Categories

Resources