1

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.

1 Answers1

1

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.

Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
  • The issue is not that I was unable to access properties in the master method, but that the master method was not being fired when wired up. Weirdly enough the Intellisense did work (albeit missing parameters as you pointed out) but when the control event fired, the EventHandler was null despite being hooked up in the declaration. – Battle J. Butts Aug 15 '23 at 17:44
  • 1
    Well, as above shows, I found the event handler returned null for the dropdown list, but exposing the properties as per above fixed that issue. So, my event was triggering, but when I attempted to get selectedIndex, or selectedItem, they were null. As above shows, to fix that issue, expose SelectedINdex, or selectedItem as a public property in the UC and it should then work. If you issue was the event not triggering in the aspx page? Then follow the above example of how public delegate and public event are setup. – Albert D. Kallal Aug 15 '23 at 17:48
  • The Properties did not fix this for me. However, after reviewing, it seems that `SelectedIndexChanged="DropdownOne_SelectedIndexChanged"` on the master page will **not** work, as the ASPX page wants you to put **On** before the name of your event, thus `OnSelectedIndexChanged="DropdownOne_SelectedIndexChanged"` In that sense, your answer is correct and solves my question. – Battle J. Butts Aug 15 '23 at 17:59
  • 1
    For future readers that are confused due to the naming conventions being used for the solution(s) (being that the event trying to be used is `OnSelectedIndexChanged (usercontrol)` and `OnSelectedIndexChanged (DropDownList)`, if you have `public event EventHandler Foo;`, ASP.NET will wire up from a declaration of `OnFoo="..."` in the ASPX page. – Battle J. Butts Aug 15 '23 at 18:04
  • 1
    Indeed- I'll consider a edit to point out the "on" part- it's important. – Albert D. Kallal Aug 15 '23 at 18:30