Assign delegate event handler from dynamically added child control - c#

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é

Related

How to stop my WPF usercontrol from loading, if i have basecontrol

I have a BaseControl, of which almost all my controls inherit from.
Right now I am working on security and I want to block users from viewing certain items if they do not have access.
I am able to do so on the Loaded event of the base control, but this is too late, this meanse the whole control gets rendered, and then replaced. I would like to replace it before it renders
here is an example of my code:
public class BaseControl : UserControl
{
public BaseControl()
{
this.Loaded +=BaseControl_Loaded;
}
private void BaseControl_Loaded(object sender, RoutedEventArgs e)
{
if (!userHasAccess)
{
this.Content = new AccessDenied();
}
}
}
The above code works perfectly, but a bit too late, Is there a way that I can do this before the Loaded event?
You want to override the OnInitialized where the constructor and properties are being set up before the Render happens.
protected override void OnInitialized(EventArgs e)
{
if (!userHasAccess)
{
this.Content = new AccessDenied();
}
base.OnInitialized(e);
}
More information can be found here.
Check out following sites:
http://msdn.microsoft.com/en-us/library/ms745025(v=vs.110).aspx
Also check object lifetime events that is what you asking for.
http://msdn.microsoft.com/en-us/library/ms754221(v=vs.110).aspx

Creating events for CustomControl

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.");
}

How to create an event by a self-made control in WinForms?

I am working on my first C# and .NET project that uses WinForms, as WPF would be an overkill for our purposes. I created a so-called ButtonMenu that comprises of all menu points, which are buttons. This class is derived from the Windows class Control.
One of the buttons of the ButtonMenu is the "Culture" button that, when pressed in this single-touch application, should change the language of all the forms the application has.
Originally, the ButtonMenu was just a kind of overlay class that accessed the controls of the BaseForm and contained methods. The BaseForm was holding the buttons inside a GroupBox of its own. Later, I run into problems with this kind of design and decided to make a separate control out of it.
My question
How can I create an event (or something similar to it) that can be caught by BaseForm, where the ButtonMenu is placed? The BaseForm can currently not react on this event and cannot change the language of all its own controls, such as text fields and buttons.
Thank you for your help!
What I have tried till now is shown below. Unfortunately, I cannot reach the marked line.
public class BaseForm : Form
{
[…]
protected static ButtonMenu m_ButtonMenu = null;
protected override void OnResize(EventArgs e)
{
[…]
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
m_ButtonMenu.Visible = true;
[…]
}
public override void UpdateWidgets()
{
[…]
try
{
[…]
// Translate button menu into current language:
m_ButtonMenu.AdaptButtons(m_CurrentCulture);
}
catch (ArgumentOutOfRangeException aaore)
{
[…]
[…]
}
protected void InitializeWidgets()
{
{
string strMethod = Name + ":InitializeWidgets(): ";
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
SuspendLayout();
Controls.Add(m_ButtonMenu);
m_ButtonMenu.Top = Height - m_ButtonMenu.Height;
ResumeLayout();
[…]
m_ButtonMenu.Click += new System.EventHandler(this.ButtonMenu_CultureClick);
}
private void ButtonMenu_CultureClick(object sender, EventArgs eas)
{
int iSelection = listViewMessages.SelectedIndices[0]; // <<<<< NEVER REACHED!
[…]
}
Just define an event in your class.
Whenever you want it to fire, call it.
// field
event EventHandler somethingHappened;
// in a method:
var threadSafeCopy = somethingHappened;
if(threadSafeCopy != null)
{
threadSafeCopy(this, e);
}
Where e is an instance of EventArgs or a sub type of EventArgs.

What is the Form "On_Load" equivalent for Controls?

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.

How to pass an ASP server control as parameter to a usercontrol?

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.

Categories

Resources