Panel changing the ownership? - c#

In my C# application there is a main form with a panel main_panel. Whenever the user selects something in the menu, lets say "A", the main panel switches to the form A's panel (look at the code), A_panel.
Since A_panel covers every controls in form A, I can summon all of the controls of form A into main form.
if ((string)MainMenu.SelectedItem == "A")
{
FormA A = new FormA();
new_panel = A.Controls["A_panel"] as Panel;
}
this.main_panel.Controls.Clear();
this.main_panel.Controls.Add(new_panel);
My question is when user selects menu A again, I don't want to recreate FormA again by new FormA(). I did because when I add control to the main_panel, then the A_panel's ownership changes to main form so that it was possible to add the control to main_panel again.
So, how to change the ownership of A_panel to Form A again?
And how to solve my problem generally?

Any reason you can't use a UserControl and keep a reference to it on your form? That way you can just show/hide it when the user wants to change views. It would be much easier to extend and interact with. Here's a walk-through on MSDN to get you started.

It sounds like a UserControl would suit your needs better. Have one instance on FormA and another on your MainForm.
If, for some reason, you can't use user controls, you could at least extract the code outside the autogenerated designer file (which I assume is why you're using FormA at all).
As in:
FormA
public FormA()
{
var mainPanel = CreateMainPanel();
this.Controls.Add(mainPanel)
}
public Panel CreateMainPanel()
{
//...build up your control tree. Cut the code from the designer if necesssary
}
MainForm
FormA _A = new FormA();
public void HandleMainMenuClick(object sender, EventArgs e)
{
if ((string)MainMenu.SelectedItem == "A")
{
new_panel = _A.CreateMainPanel();
}
this.main_panel.Controls.Clear();
this.main_panel.Controls.Add(new_panel);
}

Related

New panel not opening on Windows Form after triggering click event

I have a Windows Forms App.
I created a custom UserControl which extends Panel.
Inside one of my Forms, there is a button which should open that panel when clicked.
However, after clicking, I still don't see the panel displayed on the form.
Form code
public partial class IngredientMenu : Form
{
public IngredientMenu()
{
InitializeComponent();
}
private void btnOpenRegisterBasePanel_Click(object sender, EventArgs e)
{
BaseIngredientPanel baseIngredientPanel = new BaseIngredientPanel();
baseIngredientPanel.Location = new Point(257, 63);
baseIngredientPanel.Show();
baseIngredientPanel.BringToFront();
Console.WriteLine("panel should open");
Console.WriteLine(baseIngredientPanel.Visible);
Console.WriteLine(baseIngredientPanel.Location);
}
}
Panel code
public partial class BaseIngredientPanel : UserControl
{
public BaseIngredientPanel()
{
InitializeComponent();
}
private void btnRegisterBaseIngredient_Click(object sender, EventArgs e)
{
IngredientContext ingredientContext = new IngredientContext();
if (ingredientContext.RegisterIngredient(txtName, txtUnit, lblBaseIngredientNameValidation, lblBaseIngredientUnitValidation))
{
MessageBox.Show("Ingredient Registered."
, "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
txtName.Text = "";
txtUnit.Text = "";
}
else
{
MessageBox.Show("There are errors in the form fields."
, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BaseIngredientPanel_Load(object sender, EventArgs e)
{
Console.WriteLine("I AM OPEN");
}
}
Additionally, the "I AM OPEN" message never appears, even after clicking, so it seems like in general it's not even loading the control.
How can I make the panel open after clicking a button? I'd rather not manually drag a panel into the designer since I need to have like 5 panels and designer is just gonna try to box them like a russian doll.
All UI controls need to have a parent to whom they belong via the Controls.Add() method.
The parent can be a Form or other controls (not all will accept children). e.g. Your panel can be the parent of textboxes, comboboxes, labels, etc.
private void btnOpenRegisterBasePanel_Click(object sender, EventArgs e)
{
BaseIngredientPanel baseIngredientPanel = new BaseIngredientPanel();
baseIngredientPanel.Location = new Point(257, 63);
//Add user panel to form.
this.Controls.Add(baseIngredientPanel);
//You will probably not need these two rows any more. Try it out! But make sure your usercontrol has Visible = true.
baseIngredientPanel.Show();
baseIngredientPanel.BringToFront();
}
Edit:
to answer your questions in the comment below
If you need to make many UI changes at the same time then it is also best to use this.SuspendLayout() and this.ResumeLayout() to temporarily suspend the UI logic. This will help with performance in such cases. See https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.suspendlayout?view=windowsdesktop-5.0 for more details
If you will need to remove the controls at some stage after adding them then you have two options.
Create a separate List<BaseIngredientPanel> to (also) store your
controls when adding them to the form. This use this list to find
and remove them via this.Controls.Remove() method
Give your BaseIngredientPanel a unique Name when creating it, and
use this to find and remove the control via the
this.Controls.Remove() method. All Controls already have a Name property so you can use this.
If you want to show a Form, the class should be derived from Form. Your BaseIngredientPanel is not a Form, it is a UserControl.
UserControls are similar to other Controls like Buttons, ComboBoxes, Menus, DataGridViews, etc. Usually you use Visual Studio Designer to show them on a Form. In rare occassions you do this programmatically.
I created a custom UserControl which extends Panel.
What do you really want:
I want to create a special kind of Panel, one that behaves like a standard Panel, with only small differences. Users of my class may use almost all Properties of my special Panel: Derive from class Panel
I want to create a special kind of Panel, one that behaves like a standard Panel. I want to control which methods users of my special Panel can call: Create a class derived from UserControl and put a Panel on it.
I want to create a Form, on which I want to put a Panel. This Panel has some behaviour which I only use on this Form.
If you want to create a special kind of Panel, one that is to be reused on several Forms, use derivation. Whether you derive from Panel or UserControl depends on how fool proof you want your class to be, and how many Panel methods users of your class may call.
If your special Panel is only to be used on one Form, don't bother to create a special Panel class. Create a Form that contains the Panel.
Use standard Panel on special Form
If you decided that you don't have to reuse the behaviour of this Panel, you can just put it on a new Form. Use visual studio designer to Create the form and put a Panel on it. Subscribe to the Panel events that you want to react on.
If you want that the form also has a Button, also use visual studio designer to add the Button and subscribe to event Button_Clicked.
To show the PanelForm, you have to decide whether the PanelForm is model or modeless.
Modal: the operator can only use this Form until it is closed. He can't use other forms of this application. This is by far the most used kind of Form: on menu selection, or button click: show the PanelForm, use the PanelForm, close the PanelForm, after which the parent form can read the results of the PanelForm and act accordingly. Modal Dialog boxes are shown using Form.ShowDialog.
Modeless: while the PanelForm is shown, the operator can switch back to the parent form and interact with it. This is not used very often. You can use it to show some extra information about the status of the parent window or application, for instance the position of the mouse, or the selected Shape that you will draw. A modeless Dialog box is shown using Form.Show(). Keep in mind, that before you close the parent form, you will have to close the modeless dialog box
Show Modal
A good example is the OpenFileDialog form
In your parent form:
using (var form = new OpenFileDialog())
{
// set properties of the OpenFileDialog before showing
form.InitialDirectory = this.GetInitialDirectory();
form.DefaultExt = ".txt";
...
// Show the Form as a modal box and wait until it is Closed
DialogResult dialogResult = form.ShowDialog(this);
// the operator has closed the form. Interpret the result:
if (dialogResult == DialogResult.OK)
{
string fileName = form.FileName();
this.OpenFile(fileName);
}
}
Modeless: operator can switch back to this form
private MyPanel PanelForm {get; set;} = null;
private void ShowMyPanel()
{
if (this.PanelForm != null) return; // panel already shown
this.PanelForm = new MyPanel();
// set properties:
this.PanelForm.DisplayedItems = ...
// show the PanelForm
this.PanelForm.Show();
}
The operator can switch back to this form, and probably close this form. In that case you will have to Close the PanelForm:
private void CloseMyPanel()
{
if (this.MyPanel != null)
{
this.MyPanel.Close();
this.MyPanel.Dispose();
this.MyPanel = null;
}
}
private void OnFormClosing(object sender, ...)
{
// if MyPanel is shown, is it allowed to Close this form AND myPanel
// or should you warn the operator to close MyPanel first?
if (this.MyPanel != null)
{
this.CloseMyPanel(); // close immediately
// or:
MessageBox.Show(..., "Please close Panelbox first");
}
}
Of course you should also react if the operator closes the panelbox. Before showing the panel box:
this.MyPane.Closed += OnMyPanelClosed;
private void OnMyPanelClosed(object sender, ...)
{
if (object.ReferenceEquals(sender, this.MyPanel)
{
// my panel is closed
this.MyPanel = null;
You can't dispose the Panel, because it is still being used. The Panel show Dispose itself when Closed.
Special Panel Class
If you have a Special Panel Class (derived from either Panel, or UserControl), then after compiling, the panel control is visible in the toolbox of visual studio designer.
You should use use visual studio designer to create a Form and put the panel on the Form:
class MyPanelForm : Form
{
public MyPanelForm()
{
InitializeComponent();
If you have used visual studio designer to add the special Panel, then you will find it in InitializeComponent. You can also add it yourself in the constructor. In that case, don't forget to add it to this.components, so your panel will be disposed when MyPanelForm is disposed.
To show MyPanelForm, use either ShowDialog or Show, as described in the previous section.

How can I add a UserControl to a Panel on the main form?

I am working on project winForm application. Am trying to add a UserControl to a Panel on the main Form.
Here is what I did, I created a public static method on the Main Form so I can call it from anywhere without instantiating the main form.
public static void showUC(UserControl uc)
{
pnlContainer.Controls.Clear();
GC.Collect();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
I am getting a compilation error "An object reference is required for a non-static field, method or property".
If I create the method as non-static the compilation error will be gone but I won't be able to call it without creating object of the main form.
What I want to achieve is to be able to open any UserControl inside the Panel 'pnlContainer' on my Main Form 'frmMain'.
When the main form loads I want to calling the method without instantiating the main form again. For example
public static void showUC(UserControl uc)
{
pnlContainer.Controls.Clear();
GC.Collect();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
I want to be calling this method like this
frmMain.showUC(new ucClientList);
From another class.
Let me explain exactly what I want to achieve.
I have a Form Control named 'frmMain' which contain a panel control named 'pnlContainer' and I want to be loading UserControl into it, I have UserControl1 and UserControl2.
Now I run the program and the frmMain loads, I wants to add UserControl1 to the panel pnlContainer and I have a button inside the UserControl1 which is supposed to load UserControl2 into the same panel on the Main Form when I click on it. How do I implement this?
If you really want to do so you can write your code like this:
public static void showUC(Control uc, Control pnlContainer)
{
pnlContainer.Controls.Clear();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
and call it like this:
showUC(new someControlType(), someControlContainer);
No need to call the GC; do leave it to the system to clean up matters!
And a static function needs to know all objects it is supposed to work with, so you need to pass in the container as well.
Finally new needs paranthesis with (optionally) parameters..
I made the signature of the new control Control so you can pass in any control; and for even more flexibility the container is also Control. Note that this still allows you to pass in the Form! Of course you may want to go back to more restricted types..
Update
If you want to switch between two UserControls that both sit in the same container you can use this function:
public static void showUC(Control uc)
{
foreach (Control ctl in uc.Parent.Controls) ctl.Visible = ctl != uc;
}
It can access the other one via the Parent of the calling one. In the UC write:
private void button1_Click(object sender, EventArgs e)
{
Form1.showUC(this);
}
You could modify to actually create the UserControls instead of Hiding/Showing them; but you would lose their state each time..
If that is what you want, you may consider changing the name to something like switchUcFrom or showOtherUC; after all it doesn't 'show this' but rather 'hide this and show the other UC'

How to change a control property (FlatStyle) in base form?

This might be kind of beginner question but I searched and didn't find any clear answer!
The main question is: How to inherit properties of a control (specially FlatStyle) from a base form which doesn't have that control in C#?
Details: I have Form1 inherited from baseForm. baseForm has a Panel and a Label control but no Button. In Form1 I added a button named Button1. How can I change the style of that Button through the baseFrom?
I don't want to create a custom control or redesign the button using rectangles or similar ways, but only change that property for all buttons in my application.
UPDATE: I want all of the buttons to be affected, whether they already exist or just added. Not matter in which -if any- container they are.
In baseForm, you could hook the ControlAdded event on the Panel where the Button is to be added, and style appropiately via code. This will work for every form inherited from baseForm.
For example (in baseForm)
public partial class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
// "myPanel" is the panel where the button will be added in inherited forms
myPanel.ControlAdded += myPanel_ControlAdded;
}
private void myPanel_ControlAdded(object sender, ControlEventArgs e)
{
var button = e.Control as Button;
if (button != null)
{
button.FlatStyle = FlatStyle.Flat;
button.ForeColor = Color.Red;
}
}
}
Just made a really quick test... it works even in design mode:
As an alternative, if you are going to use heavily styled buttons everywhere in your application, you may consider creating a custom control inheriting from Button, and assign the properties there, like:
public class FlatButton : System.Windows.Forms.Button
{
public FlatButton()
{
FlatStyle = FlatStyle.Flat;
}
}
After building, you will find it in the Toolbox (under "[Your Project's] components" tab), or you can cram it on your own control library (in a different solution) and add it permanently to the Toolbox in Visual Studio.
You would need to make use of Reflection
You can use a LINQ query to do this. This will query everything on the form that is type Button
var c = from controls in this.Controls.OfType<Button>()
select controls;
foreach(var control in c)
control.FlatStyle = FlatStyle.Flat;

Having UserControl signing up for event in MainForm

I am currently working on a Winforms solution where I have a Main Form containing a ToolStrip and a Panel. The solution is meant as an administrative tool.
The panel is filled with a user control, normally containing an input form or a ListView of some sort.
Depending on which button is clicked the user control in the Panel is replaced with another user control.
So far so good. Now the trickier part is that the user can be administrator of one or more departments, and if the user is admin of more than one departments, some of the user controls will be displaying a ComboBox where the admin can choose which department to admin. However some of the user controls are independent of the department and therefore does not contain the ComboBox.
Now the thing is that instead of adding the ComboBox to the user control that are department-specific I have been thinking about adding it to the Main Form and have the user controls, when instantiated, look at what department is picked and populate the data accordingly.
But my problem is: When a user then is picking another department in the ComboBox I will have to tell the child (user control) that the value has changed.
My first thought was to have the user control - when instantiated - sign up for the change-event. But that I can not seem to figure out how to get working (it is easier going the other way around).
Another approach that I have been thinking about is to call a method in the user control from the Main Form whenever the value has changed. However there will be no guarantee that such method exists as not all user controls need this functionality.
Is there a preferred/golden solution to this? Something I am missing or is this just bad practice/design?
Thanks in advance!
When childcontrols needs to be notified, i would implement an Interface. You should avoid crosscalling methods/event handling because events could lead to "ghost controls" and unreadable code.
I would do something like this: (pseudo/not tested)
public interface IDepartmentChanged
{
void DepartmentChanged(int departmentId);
}
public class UserControl1 : UserControl, IDepartmentChanged
{
public void DepartmentChanged(int departmentId)
{
// refresh data
}
}
public class Form1 : Form
{
// Add all UserControls to a List => _controls
private List<UserControl> _controls = new List<UserControl>();
public Form1()
{
InitializeComponent();
_controls.Add(userControl11);
}
private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
{
int selectedDepartmentId = ((MyData)comboBox1.SelectedValue).Id;
foreach (UserControl control in _controls)
if (control is IDepartmentChanged)
((IDepartmentChanged)control).DepartmentChanged(selectedDepartmentId);
// or even shorter:
foreach (IDepartmentChanged departmentChanged in _controls.OfType<IDepartmentChanged>())
departmentChanged.DepartmentChanged(selectedDepartmentId);
}
On this method the childs functionality is totally separated from the parent.(OOP)
i created a form and a userenter code hereControl (called UserControl1) and added it to the form. i've added a comboBox to the form and then did:
public Form1()
{
InitializeComponent();
comboBox1.SelectedValueChanged += userControl11.comboBox1_SelectedValueChanged;
}
and then when the comboBox1 the user control knows about it and can re initiate

C# call MDI child from child form

I have these forms:
MainScreen - MDI container
DataBaseOutput - child
NewAnime - child
DataBaseOutput has a tab control that holds datagrids, each for different tables. I use an access database.
In those tabs, there is a menustrip, where the functions "New", "Edit", "Delete" etc. will be called from. Now when I'm on the first tab's menustrip and click on "New" I want to open the form "NewAnime", inside the MDI container. This however, is not working as I planned. At first I tried to just call it from the childform (DataBaseOutput). This resulted in opening a new form instead of a child. when I made it a child it was not showing up.
Then I triend lots of things, but I still haven't figured it out.
This is the current code for calling the form. It calls the form with a method in the main form:
private void NewAnime_Click(object sender, EventArgs e)
{
MainScreen main = new MainScreen();
main.mShowForm(2);
this.Close();
}
Method in the main form:
// Forms for MDI Parent
DataBaseOutput OutputForm = new DataBaseOutput();
NewAnime AddAnime = new NewAnime();
// How i made them childs (this is at the InitializeComponent(); part)
OutputForm.MdiParent = this;
AddAnime.MdiParent = this;
public void mShowForm(int formnumber)
{
switch (formnumber)
{
case 1: OutputForm.Show(); break;
case 2: AddAnime.Show(); break;
}
}
Does anyone have a clue of what i'm doing wrong and maybe has a better idea? This might be a bit too much working around, but as I said, it's my first time using MDI forms and i'm just trying to get it to work.
Have you set the MainForm to be an MDIContainer? To do this set its IsMdiContainer property to true; Also check it has File and Window top-level menu items and New and Close menu items. (The tutorial suggests this, I know it should have a Window menu item at least).
Have a look at this tutorial for more guidance: Creating MDI Child Forms (MSDN)
EDIT: Looking at it more closely, it seems you are creating a new instance of MainForm, and trying to show the form as a child of that instance, as opposed to showing it in the existing MainForm. I assume you already have an instance of MainForm open at this point? And assuming the OutputForm and AddAnime forms are children of MainForm you could call the existing instance's method like this:
private void NewAnime_Click(object sender, EventArgs e)
{
this.ParentForm.mShowForm(2);
this.Close();
}
but ideally you should have an event on DataBaseOutput that MainForm listens to, and shows the new Form when the event is raised. See here for more info (it talks about user controls and not child forms, but the principle is the same):
Calling parent form functions from a user control

Categories

Resources