Search code examples
c#asp.netwebformsevent-handlingaspx-user-control

How to correctly hookup UserControl EventHandler in ASPX Page (not code-behind)


I have a UserControl, a wrapper for asp:DropDownList. This is a control that will be repeated quite a few times, and I would like to avoid having to write event hook-ups in the code-behind for every place that it is written.

I have been trying every which way I could find to declare the event in the control declaration, however no matter what I do, it seems to end up being null.

Here's an example of my Dropdown.ascx file:

public partial class Dropdown : UserControl {
    public delegate void SelectedIndexChangedHandler(object sender, EventArgs e);
    public event SelectedIndexChangedHandler SelectedIndexChanged;

        protected void Page_Load(object sender, EventArgs e) {
        DataBind(); //including page_load calling databind in case that is somehow related
    }

    protected void dropDown_SelectedIndexChanged(object sender, EventArgs e) {
        if (SelectedIndexChanged != null) {
            SelectedIndexChanged(sender, e);
        }
    }   
}

where dropDown is an asp:DropDownList in the control.

Here is my declaration in the master aspx page:

<CustomControl:Dropdown 
    runat="server" 
    ID="DropdownOne" 
    SelectedIndexChanged="DropdownOne_SelectedIndexChanged"
>
...
</CustomControl:Dropdown>

where the page method is protected void DropdownOne_SelectedIndexChanged(object sender, EventArgs e)

At all points in the life-cycle, SelectedIndexChanged is null.

If I add DropdownOne.SelectedIndexChanged += DropdownOne_SelectedIndexChanged; to the Page_Load (or other parts of the lifecycle), everything works perfectly fine. Unfortunately this is not desired, and as far as I know what I am trying to accomplish is do-able.


Solution

  • Ok, so say this user control:

    markup:

    <asp:DropDownList ID="MyDrop" runat="server"
    
        OnSelectedIndexChanged="MyDrop_SelectedIndexChanged"
        AutoPostBack="true" >
    
    </asp:DropDownList>
    

    User code behind:

    public partial class MyDropDown : System.Web.UI.UserControl

    {        
        public string MySelectRow = "";
        private string m_SQL = "";
    
        public delegate void MySelectedIndexHandler(object sender, EventArgs e);
    
        public event MySelectedIndexHandler SelectedIndexChanged;
    
        protected void MyDrop_SelectedIndexChanged(object sender, EventArgs e)
        {
            SelectedIndexChanged(sender, e);
        }
    
    
        public string DataValueField
        {
            get
            { return MyDrop.DataValueField; }
            set
            {MyDrop.DataValueField = value;}
        }
    
        public string DataTextField
        {
            get
            { return MyDrop.DataTextField; }
            set
            { MyDrop.DataTextField = value; }
        }
    
    
        public DropDownList ThedropDownList   
        {
            get { return this.MyDrop; }
        }
    
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                LoadData();               
            }
        }
    
        void LoadData()
        {
            MyDrop.DataSource = General.MyRst(SQL);
            MyDrop.DataValueField = DataValueField;
            MyDrop.DataTextField = DataTextField;   
    
            MyDrop.DataBind();
            MyDrop.Items.Insert(0, new ListItem(MySelectRow, ""));
        }
    
    
       public ListItem SelectedItem
        {
            get { return MyDrop.SelectedItem; }
        }
    
        public int SelectedIndex
        {
            get { return MyDrop.SelectedIndex; }
        }
    
        public string SQL { 
            get { return m_SQL; } 
            set
            {
                m_SQL = value;
                LoadData();
            }
        }
    
    
    }
    

    Probably most important here is how to get the selected item. You HAVE to expose that as a public property.

    e.g., this from above:

       public ListItem SelectedItem
        {
            get { return MyDrop.SelectedItem; }
        }
    
        public int SelectedIndex
        {
            get { return MyDrop.SelectedIndex; }
        }
    

    If you MISS the above, then Selected index will not work. (Same goes for Selected Item) Now, on the aspx page, we have this markup:

            <uc1:MyDropDown runat="server" ID="MyDropDown"
                OnSelectedIndexChanged="MyDropDown_SelectedIndexChanged"
                />
    
    
        <%--   In this example we set Data Value and Data Text
               -- above example, we used code behind on page load to set Value & Text fields
               -- <uc1:MyDropDown runat="server" ID="MyDropDown"
                DataValueField="ID"
                DataTextField="HotelName"
                OnSelectedIndexChanged="MyDropDown_SelectedIndexChanged"
                />--%>
    

    And code behind for the aspx page:

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                // do NOT forget to wrap this code in !IsPostBack
                // (a dropdown list will not work, if we don't check
                // for !IsPostback - same goes for UC
    
                MyDropDown.DataValueField = "ID";
                MyDropDown.DataTextField = "HotelName";
    
            MyDropDown.MySelectRow = "Please Select Hotel";
            MyDropDown.SQL = @"SELECT ID, HotelName FROM tblHotelsA
                              ORDER BY HotelName";
            }
        }
    
        protected void MyDropDown_SelectedIndexChanged(object sender, EventArgs e)
        {
            Debug.Print($"Selected Text = {MyDropDown.SelectedItem.Text}");
            Debug.Print($"Selected Value = {MyDropDown.SelectedItem.Value}");
            Debug.Print($"Selected Value = {MyDropDown.SelectedIndex}");
        }
    

    It is not clear if you looking to wire up the event for the aspx page?

    However, intel-sense should work for you, but note this:

    enter image description here

    So, I used above create new event like we do for all controls (even non user controls).

    However, flipping over to code behind, you note that the sender and args is missing as above shows.

    So, you have to manually edit/add the parameters like this:

        protected void MyDropDown1_SelectedIndexChanged(object sender, EventArgs e)
        {
    
        }
    

    I don't know how to "fix" this bug, or get VS to add the "object sender" and the "EventArgs e", but it is a simple matter to add this after letting VS create the code behind event stub for you in the aspx page.

    While standard controls will create the event stub for you using intel-sense, a user control creates the code stub, but requires you to manually add the parameters to the code stub.