I have a child UserControl which is dynamically loaded into a parent page.
ChildControl childcontrol = this.LoadControl("") as ChildControl;
I have to call one of the methods in the parent page from this childUserControl. How do we do that?
Thanks
You shouldn't write a control that relies on a method being on the page, so the best thing to do would be to expose and handle an event from the child control. Add the following to your child control:
public event OnSomethingHandler Something;
public delegate void OnSomethingHandler(ChildControl sender);
Then when you want to fire the page method in question, fire off the event:
public void FireParentMethod()
{
if (Something != null)
{
Something(this);
}
}
The all you need to do is handle the event on the page (in markup or in code as follows):
childUserControl.Something+=
new ChildControl.OnSomethingHandler(ChildControl_OnSomething);
And add the handler to the code behind:
protected void ChildControl_OnSomething(ChildControl sender)
{
FirePageMethod();
}
Cast the parent of the control as the specific type of your page and then call the method.
Related
I have a form and two custom UserControl that I made myself. one control has some buttons that each of these button have theire Tag property set to an array of PointF. I have another UserControl that has a ObservableCollection<PointF[]> that I set its event handler to draw the lines if data is being added to it. This works fine If I put the data points on its own class...just make to sure it works.
No my problem is, having this two control in one form, how can I set the click event of buttons in the first control, to add data points to the second control?
This two controls are both in two different projects in my soloution. and the form that these to controls are being showed in, is also in a different project (it is the launching project of soloution)
Add an event to the first control.
public event EventHandler<EventArgs> Control1ButtonClicked;
private void OnClicked()
{
var handler = Control1ButtonClicked;
if (handler != null)
{
handler(this, new EventArgs());
}
}
private void Button1_Click(object sender, EventArgs e)
{
OnClicked();
}
Add a property to the second control
public ObservableCollection<PointF[]> MyPoints{ get; set;};
Then in your main application add a listener
userControl1.Control1ButtonClicked += new EventHandler<EventArgs>(userControl1_Control1ButtonClicked);
void userControl1_Control1ButtonClicked()
{
//Do Something to control 2
userControl2.MyPoints.Add() = //Whatever
}
You could add a public method on the second usercontrol that receive an array of PointF, then inside this method you could add the PointF to your collection.
EDIT: To handle the click event inside the first user control
inside the first usercontrol add the event and the delegate required
public delegate void OnClickPointDataEvent(object sender, PointF[] data);
public event OnClickPointDataEvent ClickPointData;
then form_load event subscribe to the usercontrol1 event
uc1.ClickPointData += new UserControl1.OnClickPointDataEvent(form_subscribe_event);
private void form_subscribe_event(object sender, PointF[] data)
{
uc2.SomePublicMethod(data);
}
and finally, inside the first usercontrol button click call the code that handle the event inside the form
....
if(ClickPointData != null)
ClickPointData(pointf_array);
...
I am working on a UserControl for selecting special files, in this control there is a TreeView which gets populated with nodes when user selects some file. Also user can remove the files from this treeview!
I am using this control in a wizard form. In this wizard form there is a button named buttonNext and this button is disabled by default.
How can I create an event for the treeview in the usercontrol that when it gets populated it notify the next button in wizard form to get enabled and if user removes all files from that treeview it notify the button to get disabled again.
P.S: Selecting files (browser dialog and stuff like that) are all done within this usercontrol, so in my wizard form I have no access to the things that is going on in this component, but only I set the TreeView itself as public so I can read its nodes in my wizard form.
I know how to subscribe to events but never created any event myself :(
Declare events on your CustomControl:
public event EventHandler DataPopulated;
public event EventHandler DataRemoved;
Common practice is creating protected virtual methods (for possible overriding them in descendant classes), named On<EventName> which will verify that event has attached handlers and raise event, passing required arguments:
protected virtual void OnDataPopulated()
{
if (DataPopulated != null)
DataPopulated(this, EventArgs.Empty);
}
NOTE: If you need to pass some data to event handlers, then use generic EventHandler<DataPopulatedEventArgs> delegate as event type, where DataPopulatedEventArgs is a class, inherited from EventArgs.
Then just call this method just after your data was populated:
treeView.Nodes = GetNodes();
OnDataPopulated();
Then just subscribe to this event and enable your next button:
private void CustomControl_DataPopulated(object sender, EventArgs e)
{
buttonNext.Enabled = true;
}
Who is the one populating the TreeView? The one loading the data on it could enable the Next button when it has finished the loading. Am I missing something?
By the way, you create an event like this:
public event EventHandler<EventArgs> YouEventName;
And you call it like a method:
this.YourEventName(this,EventArgs.Emtpy);
Best practices say that you should create a method to call it like this:
protected virtual void OnYourEventName()
{
if (this.YourEventName != null)
{
this.YourEventName(this, EventArgs.Empty);
}
}
Check out this MSDN article for a complete tutorial on how to create and fire events.
http://msdn.microsoft.com/en-us/library/aa645739(v=vs.71).aspx
You can just propogate the event of the Treeview.
You can add this to your custom control, and it will have a SelectedNodeChanged event.
public event EventHandler SelectedNodeChanged
{
add { tree.SelectedNodeChanged += value; }
remove { tree.SelectedNodeChanged-= value; }
}
Creating a new event
public event EventHandler<EventArgs> myEvent;
You then invoke it from some method
this.myEvent(sender, e);
The actual event would look something like this:
protected void MyEvent(object sender, EventArgs e)
{
//Your code here
}
Your code can be like this:
public delegate void ChangedEventHandler(object sender, EventArgs e);
class TreeViewEx : TreeView
{
...
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}
}
and it usage
TreeViewEx tree = ...
tree.Changed += new EventHandler(TreeChanged);
// This will be called whenever the tree changes:
private void TreeChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
I have a listBox that I would like to carry out a method when its loaded up, although I can't use the Form "On_Load" trigger, since the ListBox is within a TabControl.
Is there a way of getting a method to execute when the object initializes?
The closest analog for controls is the HandleCreated event. This will fire when the underlying control handle is created, which is slightly before the Loaded event of the parent window would fire.
As #SLaks stated, you could put in your class's constructor. However, if what you want to prepare relies on other elements in the form, you can add to the event handler queue at the end of a form loading, but before its actually presented to the user.
In the constructor code of your form (not the designer code), add to the load event, then add your own custom function
public partial class frmYourForm : Form
{
public frmYourForm()
{
Load += YourPreparationHandler;
}
private void YourPreparationHandler(object sender, EventArgs e)
{
// Do you code to prepare list, combos, query, bind, whatever
}
}
Have the same problem, the previous answers apply well, for a single case.
But, I require to do something in most controls, in several forms, in an app.
Solved by using an interface:
interface IOnLoad
{
void OnLoad();
}
And added to descendant control:
public partial class MyButton : Button, IOnLoad
{
void OnLoad() { // call "OnLoadDelegate" }
}
public partial class MyForm : Form
{
public void MyForm_Load(...) {
foreach(Control eachControl in Controls) {
if (eachControl is IOnLoad) {
IOnLoad eachOnLoadControl = (IOnLoad)eachControl;
eachOnLoadControl.OnLoad();
}
} // foreach
}
} // class
Its more complex, but it suit my requirements.
You can use OnHandleCreated(EventArgs e). However, it triggers during design time too. You can override it too.
Can you use the HandleCreated event?
You can just put your code in the constructor.
You usually don't need to wait for any initialization.
I have a control that handles commenting. In this control, I have set a delegate event handler for sending an email.
I then have various types of controls, e.g. blog, articles etc, each of which may or may not have the commenting control added (which is done dynamically with me not knowing the id's), i.e. the commenting control is added outside this control. Each of these controls handles it's emailing differently(hence the event).
What I'm trying to determine, is how to assign the event in the parent control. At the moment, I'm having to recursively search through all the controls on the page until I find the comment control, and set it that way. Example below explains:
COMMENTING CONTROL
public delegate void Commenting_OnSendEmail();
public partial class Commenting : UserControl
{
public Commenting_OnSendEmail OnComment_SendEmail();
private void Page_Load(object sender, EventArgs e)
{
if(OnComment_SendEmail != null)
{
OnComment_SendEmail();
}
}
}
PARENT CONTROL
public partial class Blog : UserControl
{
private void Page_Load(object sender, EventArgs e)
{
Commenting comControl = (Commenting)this.FindControl<Commenting>(this);
if(comControl != null)
{
comCtrol.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
}
Is there an easier way?
EDIT:
The reason I ask is that if I search from this.Page as the initial control, I am worried about time taken to search down the control tree to find it. Each different type of page would be different in how many control it would have. On some testing, it returns back quite quickly the result.
You could override the AddedControl event of your Blog class and check if the added control is instance of type Commenting. Something like:
public partial class Blog : UserControl {
protected override void AddedControl(Control control, int index) {
base.AddedControl(control, index);
Commenting commentingControl = control as Commenting;
if (commentingControl == null) return;
commentingControl.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
Of course, you can put this code on a base class of all your "commentable" user controls and have an abstract method to actually handle the event.
Just one thing: the AddControl event happens AFTER the Page_Load, so be careful.
Cheers,
André
I have a gridview and usercontrol on the page, I want to alter gridview from usercontrol. how can i do that?
And also how can I call functions in usercontrol's "host" page from the usercontrol?
UPDATE:
I have a dropdownlist inside this usercontrol, when the it's SelectedIndexChanged event is triggered i want the gridview on the host page to DataBind().
(Then, gridview using objectdatasource will read this dropdownlist selected item and use it as select parameter)
Also if it possible to make it as general as it possible, because the altered control is not always a gridview...
Thanks
First of all, you would not want to do neither. That's against the concept of user control.
If you want to modify a gridview using the user control's state, you can expose the control's state and make the gridview to behave according to this state.
// This handles rowdatabound of gridview
OnRowDataBound(object sender, RowDataBoundEventArgs e)
{
var control = e.Row.Find("UserControlId");
if (control.SomeProperty == SomeValue)
someTextBox.Value = "something";
}
If you really need to pass a handle of gridview to a user control, define a property on the user control of grid view type:
// This is a property of user control
public GridView Container { get; set; }
and set the control's container to the gridview, before accessing it.
userControl.Container = gridView;
If the user control is a part of the itemtemplate on the grid, since your user control is created while rows of the gridview are created, you can only this after you bind your gridview.
Finally, for calling a function on the container page, you can expose an event inside of your user control and bind to that event.
public delegate void SomethingHappenedEventHandler(object sender, EventArgs e);
// In user control:
public event SomethingHappenedEventHandler SomethingHappened;
// Trigger inside a method in user control:
SomethingHappenedEventHandler eh = SomethingHappened;
if (eh != null) eh(this, EventArgs.Empty);
// In page:
userControl.SomethingHappened = new SomethingHappendEventHandler(OnSomething);
private void OnSomething(object sender, EventArgs e)
{
// When something happens on user control, this will be called.
}
Simply add a public method to your usercontrol's code-behind, then you can call this from the page and pass the gridview as a parameter:
public class MyUserControl : UserControl
{
public void MyMethod(GridView gridview)
{
// do stuff with the gridview
}
...
}
The second question is a bit more complicated. Because if you make your usercontrol depend on a specific page, it will no longer be reusable on other pages, e.g:
public class MyUserControl : UserControl
{
public void AnotherMethod()
{
//get the current page and cast it to its type
MyPage page = this.Page as MyPage;
// now I can call the public methods of MyPage
page.SomeMethod();
}
...
}
A better solution would be to define an interface, which is implemented by the page. Then you can methods of that interface by casting the current page to the interface (and the controls can still be reused on other pages implementing the same interface):
public interface IMyInterface
{
void SomeMethod();
}
public class MyPage : IMyInterface
{
public void SomeMethod() { ... }
}
public class MyUserControl : UserControl
{
public void AnotherMethod()
{
//get the current page and cast it to the interface
IMyInterface page = this.Page as IMyInterface;
// now I can call the methods of the interface
if (page != null) page.SomeMethod();
}
}
EDIT: In your user control, simply create a public property to hold the grid. That property will then hold a reference to grid in the host page. For example, you could add the following to your UserControl's code behind.
public string CurrentGridID
{
get;
set;
}
public void ModifyGrid()
{
//make changes to the grid properties
GridView grid = Page.FindControl(CurrentGridID);
grid.AutoGenerateColumns = false;
}
From your the page hosting the control, you could do something like this.
protected void Page_Load(object source, EventArgs e)
{
this.myUserControl1.CurrentGridID = this.gridView1.ID;
}
You should expose a public property that accepts the id of the server control you want to reference. You could then grab that control by querying Control.NamingContainer. You should not query Page, since you want to grab the control in your current context (you could be in a usercontrol with controls named the same as the page).
Dont forget the often overlooked IDReferencePropertyAttribute. It tells your designhost (usually Visual Studio) to provide autocompletion for the value using all controls in the current context.
To call stuff on your containing Page, just use the Page property. You need to cast this to your specific page class if you need to access your own methods.