I have 2 controls that inherit UserControl
one is a container and the other is a collection of basic text boxes lables etc. hereby labeled as ContainerControl and ComboControl.
ContainerControl contains a List<ComboControl> and a foreach loop that adds them to a FlowLayoutPanel. ComboControl has a button that I would like to be used to clear itself from its parent's List.
I am not sure what the best way of doing this would be. this.parent and cast to ContainerControl or would Dispose() work? I'm fairly sure I could pass a ref to the List, but that sounds needlessly messy...
public partial class ContainerControl : UserControl
{
List<ComboControl> ComboControls = new List<ComboControl>();
...
//procederaly generate and fill ComboControls here
...
foreach (ComboControl value in ComboControls)
{
this.flowLayoutTable.Controls.Add(value);
}
...
}
public partial class ComboControl : UserControl
{
private void BtnDel_Click(object sender, EventArgs e)
{
//what goes here
}
...
}
Along the lines of what Zohar Peled said, something like this to remove a control to avoid leaking resources.
private void cleanup(Control c)
{
foreach(Control child in c.Controls)
cleanup(child);
if (c.Parent != null)
{
c.Parent.Controls.Remove(c);
c.Dispose();
}
}
For scenario like this i would use a custom event to send a delete request to parent control :
your ComboControl with Custom Event :
//Create Custom Event
public delegate void DeleteControlDelegate(object sender);
public partial class ComboControl : UserControl
{
//Custom Event to send Delete request
public event DeleteControlDelegate DeleteControlDelegate;
public ComboControl()
{
InitializeComponent();
}
//Invoke Custom Event
private void OnDeleteControl(object sender)
{
DeleteControlDelegate?.Invoke(sender);
}
private void BtnDel_Click(object sender, EventArgs e)
{
//On ButtonClick send Delete request
OnDeleteControl(this);
}
}
and in your ContainerControl subscribe to event of each ComboControl :
List<ComboControl> _comboControls = new List<ComboControl>();
public ContainerControl()
{
InitializeComponent();
}
private void ContainerControl_Load(object sender, EventArgs e)
{
_comboControls.Add(new ComboControl());
_comboControls.Add(new ComboControl());
_comboControls.Add(new ComboControl());
foreach (ComboControl value in _comboControls)
{
flowLayoutPanel.Controls.Add(value);
//Subscribe to Custom Event here
value.DeleteControlDelegate += Value_DeleteControlDelegate;
}
}
private void Value_DeleteControlDelegate(object sender)
{
//When Raised Delete ComboControl
flowLayoutPanel.Controls.Remove((Control) sender);
}
}
Related
I have a parent form that loads different user controls but when I am trying to access a method on the parent form from a button on the user controller which is not working but if the same method is accessed from the own parent form its all good I excluded the rest of code and here is the code not working
method on Parent Form:
using IT_HUB_MANAGEMENT_SOLUTION.SERVICES;
namespace IT_HUB_MANAGEMENT_SOLUTION.APP_FORMS
{
public partial class FIRST_RUN_FRM : Form
{
public FIRST_RUN_FRM()
{
InitializeComponent();
}
public void NEXT_STEP_CMD()
{
MAIN_PANEL.Controls.Clear();
FIRST_RUN_OBJECTS.CRYSTAL_REPORTS_CONTROL cRYSTAL_REPORTS_CONTROL = new();
cRYSTAL_REPORTS_CONTROL.Dock = DockStyle.Fill;
MAIN_PANEL.Controls.Add(cRYSTAL_REPORTS_CONTROL);
}
}
here is the code on user controls:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public DOTNET_INSTALLER_CONTROL()
{
InitializeComponent();
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
FIRST_RUN_FRM fff = new();
fff.NEXT_STEP_CMD();
}
}
First of all, this is not a normal behavior (at least for me) that a UserControl calls a parent form method directly. Usually user controls are not aware of the parent that is using them, and for me, it's a really smelly code.
In your code:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public DOTNET_INSTALLER_CONTROL()
{
InitializeComponent();
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
FIRST_RUN_FRM fff = new();
fff.NEXT_STEP_CMD();
}
}
You're simply creating a new instance of the FIRST_RUN_FORM (Also try to follow the naming convention) and then calling it's function. This won't throw a compiler error since it's a valid .Net syntax. You're creating a new class and calling a public function inside it. However, it won't show anything on your initial form because it's not the same instance.
There's two ways to fix this code, and I don't like the first way I'll show you.
You can add the parent form instance to the user control and pass it on when creating a new instance of the UC. Something like this:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
private FIRST_RUN_FRM _parent = null;
public DOTNET_INSTALLER_CONTROL(FIRST_RUN_FRM parent)
{
InitializeComponent();
_parent = parent;
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
if (_parent == null)
{
return;
}
_parent.NEXT_STEP_CMD(); //This might throw an error not related to this question
}
}
I wouldn't recommend this though.
What I'd go for is using events, this way you don't need the UserControl to know who their parent is or what should be done to the parent when a button inside the UC is clicked.
Event:
In the user control:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public event EventHandler NextButtonClicked;
public DOTNET_INSTALLER_CONTROL(FIRST_RUN_FRM parent)
{
InitializeComponent();
_parent = parent;
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
OnNextButtonClicked();
}
protected virtual void OnNextButtonClicked()
{
EventHandler handler = NextButtonClicked;
handler?.Invoke(this, null);
}
}
And in the parent form you'll subscribe to this event and react when it's fired.
I have a user control(VCtrlDetails in the code below) that hosts a data grid(detailsGrid) which is private.
Now, i have this control loaded in another user control(UcResult_Details) and i want to handle grid selection changed event in this another user control.
public partial class VCtrlDetails : UserControl
{
public event EventHandler<bool> EnableEditTemplateButton;
private void InitializeComponent()
{
private System.Windows.Forms.DataGrid detailsGrid;
this.detailsGrid.SelectionChanged += new
System.EventHandler(this.detailsGrid_SelectionChanged);
}
private void detailsGrid_SelectionChanged(object sender, EventArgs e)
{
EnableEditButton?.Invoke(this, IsApproved());
}
public bool IsApproved()
{ }
}
public partial class UcResult_Details : UserControl
{
private readonly VCtrlDetails vCtrlDetails;
UcResult_Details()
{
//Need to subscribe to vCtrlDetails' grid selection changed event here in this ctor
}
}
I'm not that well versed with event handlers, so stuck with the solution as the grid object is private in the user control 'VCtrlDetails', so cannot directly do something like:
vCtrlDetails.detailsGrid.SelectionChanged += DetailsGrid_SelectionChanged
You need to bubble the event up and out of the VCtrlDetails class. You could do so by creating an event within the VCtrlDetails class and allowing your UcResult_Details class to subscribe to it.
public partial class VCtrlDetails : UserControl
{
public event EventHandler<bool> EnableEditTemplateButton;
public event EventHandler<EventArgs> DetailsGridSelectionChanged;
private void InitializeComponent()
{
private System.Windows.Forms.DataGrid detailsGrid;
this.detailsGrid.SelectionChanged += new
System.EventHandler(this.detailsGrid_SelectionChanged);
}
private void detailsGrid_SelectionChanged(object sender, EventArgs e)
{
EnableEditButton?.Invoke(this, IsApproved());
//Raise your custom event
DetailsGridSelectionChanged?.Invoke(this, e);
}
public bool IsApproved()
{
}
}
public partial class UcResult_Details : UserControl
{
private readonly VCtrlDetails vCtrlDetails;
UcResult_Details()
{
//Need to subscribe to vCtrlDetails' grid selection changed event here in this ctor
this.vCtrlDetails.DetailsGridSelectionChanged += new
EventHandler(this.vCtrlDetailsSelectionChanged);
}
private void vCtrlDetailsSelectionChanged(object sender, EventArgs e)
{
//Do whatever
}
}
I have a program that has a parent form which then creates a child form. Upon clicking the updateButton within the child form, I want the searchButton within the parent form to fire.
However I get an error for protection reasons. I have tried setting everything Public just to see, still wont work for me.
Error 1 'SalesSystem.SystemForm.searchButton' is inaccessible due to
its protection level SalesSystem\UpdateForm.cs 111 20 SalesSystem
This is what I have so far.
Parent Code
namespace SalesSystem
{
public partial class SystemForm : Form
{
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
}
Child Code
namespace SalesSystem
{
public partial class UpdateForm : Form
{
public UpdateForm(string selectedPerson, string dbdirec, string dbfname)
{
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
SystemForm parent = (SystemForm)this.Owner;
parent.searchButton.PerformClick();
this.Close();
}
}
}
Your searchButton button control is set to private by default in WinForm. You've said you set everything to public but I assume you mean you've set everything in the code you've posted to public. There are a few ways to fix this. The direct fix would be to simply go to Visual Studio designer, select the button, and set its Modifier property to internal or public.
However, it seems you're closing your form straight after so I'd just have my parent form subscribe to the FormClosing event of the form.
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.FormClosing += (s, o) =>
{
//your code for what the parent class should do
};
upForm.ShowDialog(this);
If you're not closing the form then you can create your own event handler that your parent form subscribes to.
You have 2 options:
create a public void search() method in your parent form. Then, instead of accessing the the button on the parent form and invoking its click event, you run the search code directly. The new method is not tied to a GUI element and accessing it from a different form is no problem.
The better solution is to create a delegate. A delegate is an execution target that will be assigned at run time. The parent form still has a public void search() method. And when it creates the child form, it will pass the name of that function as parameter. The child form has no knowledge about the parent form (as opposed to the first option where the child MUST know that there is a method called search()). When it is time to inform whoever created the child form, the delegate is called. This is a small example:
public partial class SystemForm : Form
{
public delegate void dSearch();
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
search();
}
private void search()
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname, search);
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
}
And the child form:
public partial class UpdateForm : Form
{
private SystemForm.dSearch _target;
public UpdateForm(string selectedPerson, string dbdirec, string dbfname, SystemForm.dSearch target)
{
_target = target;
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
_target();
this.Close();
}
}
You should use the "Model View Controller" or "Model View Presenter" pattern to approach this kind of thing.
Each form should only be concerned with displaying its contents to the user. When it comes to responding to UI events such as button clicks, each form (i.e. each "View") should simply raise an event which informs the controller/presenter that something has happened.
The controller/presenter should then respond appropriately. Then the logic that wires together different forms (such as the parent and child forms in your example) is encapsulated in the Controller class. Such logic does not really belong in either of the forms.
I wrote an example that demonstrates a simple design to do this sort of thing in another answer some time ago. Rather than copy/paste it all here, I'll just give you a link to it:
How to make Form1 label.text change when checkbox on form2 is checked?
You'll have to scroll down to see my answer. It's broadly similar to what you're doing; hopefully it will make sense to you! Follow the instructions to make a test application and run it to see what happens.
I'm tired and might be missing something but that is correct behaviour.
Your child form does not directly inherit from your parent form.
Your parent form has a protected level, so only it and classes that extend it can access the method.
2 solutions:
Change your child form to:
public partial class UpdateForm : SystemForm
Change method to public
public void searchButton_Click(object sender, EventArgs e)
You could expose a Search Event from your UpdateForm and subscribe to this event in the SystemForm
namespace SalesSystem
{
public partial class SystemForm : Form
{
public SystemForm()
{
InitializeComponent();
}
protected void searchButton_Click(object sender, EventArgs e)
{
//search code
}
private void updateButton_Click(object sender, EventArgs e)
{
try
{
UpdateForm upForm = new UpdateForm(resultBox.SelectedItems[0].Text, dbdirec, dbfname);
upForm.OnSearch += Search;
upForm.ShowDialog(this);
}
catch (Exception)
{
//
}
}
private void Search(string searchParameter)
{
....
}
}
namespace SalesSystem
{
public delegate void SearchEventHandler(string searchParameter);
public partial class UpdateForm : Form
{
public event SearchEventHandler OnSearch;
public UpdateForm(string selectedPerson, string dbdirec, string dbfname)
{
InitializeComponent();
}
private void updateButton_Click(object sender, EventArgs e)
{
//do stuff
OnSearch("SearchThis");
this.Close();
}
}
}
I have a UserControl, that contains a panel, the panel contains a picture box.
When I MouseMove over the Picture Box, I want to update a label on the MainForm.
I have a get/set method on the main form, but how do I use it?? thanks
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public String MouseCords
{
get { return this.MouseCordsDisplayLabel.Text; }
set { this.MouseCordsDisplayLabel.Text = value; }
}
}
public partial class ScoreUserControl : UserControl
{
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
// MainForm.MouseCords("Hello"); //What goes here?
}
}
Actually it's possible to do in your case like:
((MainForm)this.ParentForm).MouseCords = "Some Value Here";
But the right way is with events like Felice Pollano mentinoed:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
this.myCustomControlInstanse.PicureBoxMouseMove += new EventHandler<StringEventArgs>(myCustomControlInstanse_PicureBoxMouseMove);
}
private void myCustomControlInstanse_PicureBoxMouseMove(object sender, StringEventArgs e)
{
this.MouseCordsDisplayLabel = e.Value // here is your value
}
}
public class StringEventArgs : EventArgs
{
public string Value { get; set; }
}
public partial class ScoreUserControl : UserControl
{
public event EventHandler<StringEventArgs> PicureBoxMouseMove;
public void OnPicureBoxMouseMove(String value)
{
if (this.PicureBoxMouseMove != null)
this.PicureBoxMouseMove(this, new StringEventArgs { Value = value });
}
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
this.OnPicureBoxMouseMove("Some Text Here");
}
}
Ideally, you should raise an event for the same.
Create a delegate
public delegate void Update();
in the user control
public class MyUserControl : UserControl
{
public event Update OnUpdate;
}
On the main form register a handler for the user controls event.
public class Main
{
public Main()
{
myUserControl.OnUpdate += new Update(this.UpdateHandler);
}
void UpdateHandler()
{
//you can set the delegate with sm arguments
//set a property here
}
}
On user control,
To raise an event on button click
do this
OnUpdate();
This might give you an idea...
public partial class ScoreUserControl : UserControl
{
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
// MainForm.MouseCords("Hello"); //What goes here?
MainForm parent = this.ParentForm as MainForm;
if (parent != null) parent.MouseCordsDisplayLabel.Text = "Hello";
}
}
You have several options:
Create an event on the user control and have to form listen to it (I think this is the recommended way by most C# programmers).
Pass a reference to the main form to the User Control (in the constructor). This way, the user control knows about its MainForm.
Cast this.ParentForm to the MainForm class, then you have the reference.
Options 2 and 3 are somewhat more comfortable and lazy, but the cost is that the user control has to know about the specific class MainForm. The first option has the advantage that you could reuse the user control in another project, because it does not know about the MainForm class.
You should publish an event from the user control and subscribe to it from the main form.
At least this is the pattern suggested for winform. In any case the idea is to make the control "observable" from the agents who need to see the coords, instead of using it as a driver to update the interested agents.
I'm trying to subscribe to the the save button event of a user control that is launched in a separate radwindow from the calling parent. but I am getting object not initialized error, I know why but what am I missing?
Update: I found my error but it appears that if (this.SaveEvent!= null) in the ControlBase is always null
Parent Control Code:
public partial class myControl : ControlBase
{
private myChildControl __myChildControl;
private void myControl_PreRender(object sender, EventArgs e)
{
// error occurs here
//this.__myChildControl.SaveEvent += new myChildControl.SaveEventHandler(__myChildControl_SaveEvent);
// found my error
this.SaveEvent += new myChildControl.SaveEventHandler(__myChildControl_SaveEvent);
}
private void __myChildControl _SaveEvent(object sender, CustomEventArgs e)
{
this.Label1.Text = e.CustomEventArg1.ToString();
this.Label2.Text = e.CustomEventArg2.ToString();
}
}
Child Control Launched in RadWindow:
public partial class myChildControl : ControlBase
{
protected void btnSave_OnClick(object sender, EventArgs e)
{
CustomEventArgs _cea = new CustomEventArgs {CustomEventArg1 = 123, CustomEventArg2 = 12};
callBaseMethod(_cea);
}
}
ControlBase Code:
public class ControlBase : UserControl
{
public event CustomEventHandler SaveEvent;
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
internal void callBaseMethod(CustomEventArgs cea)
{
if (this.SaveEvent!= null)
{
this.SaveEvent(this, cea);
}
}
}
CustomEventArgs class:
public class CustomEventArgs : EventArgs
{
public int CustomEventArgs1 { get; set; }
public int CustomEventArgs2 { get; set; }
}
This isn't possible in codebehind: the RadWindow presents a separate aspx/ascx page altogether that is linked to the main page through javascript alone.
What you need to do is handle the RadWindow OnClientClose event in javascript, then fire something in the parent page that performs the appropriate tasks.