Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

delegates and events issue

Status
Not open for further replies.

sds814

Programmer
Feb 18, 2008
164
US
On my aspx page, I have a dropdownlist and a user control that is used twice (ucA and ucB). If the dropdownlist changes then ucA and ucB have to be changed. Also, if ucA has any changes to it, then ucB has to be changed. I figured out the code for the dropdown list so if it changes ucA and ucB get changed as well. I'm having a difficult time figuring out the code to create a SelectedIndexChanged event for the user control. Here is the code that I have on the code-behind in the user control:

public class IndexChangedEventArgs : EventArgs

{

}

public delegate void IndexChangedEventHandler(object sender, IndexChangedEventArgs e);

public class IndexChangedEvent

{

public event IndexChangedEventHandler IndexChanged;


protected virtual void OnIndexChanged(IndexChangedEventArgs e)

{

if (IndexChanged != null)

{

//Invokes the delegates.

IndexChanged(this, e);

}

}

}

I'm not sure how to raise the IndexChanged event in the user control and how to call the user control's IndexChanged event from the aspx page. Also, any good links that explain how to do this will be appreciated.

Thanks for the help!

 
What is the content of the user control? Also, what do you mean by a SelectedIndexChanged event for the control. Are you wanting the event to fire when a user selects a new item in the user control? Or do you want the event to fire when a new value is selected in the drop down list? If it is the ladder then the control needs to subscribe to the event. Please provide a little more detail and post your markup and I will be happy to help out.







 
DropDownList.SelectedIndexChanged uses a standard EventHandler. I don't see a IndexChangedEventHandler assignment for this event.
Code:
public event EventHandler MakingAChange 
{
   //assuming a button is clicked on the user control to notify a change
   add {AButton.Clicked += value;}
   remove {AButton.Clicked -= value;}
}

public void DoSomething(object sender, EventArgs e)
{
   //logic for UC1
}
Code:
public void DoSomethingElse(object sender, EventArgs e)
{
   //logic for UC2
}
Code:
protected override void OnInit(EventArgs e)
{
   base.OnInit(e);
   DDL.SelectedIndexChanged += UC1.DoSomething;
   DDL.SelectedIndexChanged += UC2.DoSomethingElse;
   UC1.MakingAChange += UC2.DoSomethingElse;
}

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
KDavie: To answer your question, I would like an event to fire when a user selects a new item in the user control. The user control is composed of a couple of server controls (including dropdown list, listbox, checkbox list, and ajax extenders). So if a value is selected in a dropdownlist or items are selected to a listbox the event should fire. I'm not exactly sure what you mean by markup.

Thanks for the help.
 
markup is the aspx file. codebehind is the aspx.cs file. (vs also creates a aspx.designer.cs file which defines the server controls in markup for the code behind)

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Here is the markup:

Code:
<tr>
  <td align="left" style="width: 110px" valign="top">
     Effective Date Year
  </td>
  <td align="left" valign="top" style="width: 75px">
      <asp:DropDownList ID="ddEffectiveDateYear" runat="server" OnSelectedIndexChanged="ddEffectiveDateYear_SelectedIndexChanged" AutoPostBack="True" SkinID="DropDownListSkin" Width="65px"/> 
  </td>
  <td align="left" width="120px" valign="top">
    Effective Date Month
  </td>
  <td style="width: 60px">
     <asp:UpdatePanel ID="updEffectiveDateMonth" runat="server" UpdateMode="Conditional">                                              <ContentTemplate>
                                                    <uc1:ParameterDropdown ID="pddEffectiveDateMonth" runat="server" OnPreRender="pddEffectiveDateMonth_PreRender"/>
                                                </ContentTemplate>
                                                <Triggers>
                                                    <asp:AsyncPostBackTrigger ControlID="ddEffectiveDateYear" EventName="SelectedIndexChanged" />
                                                </Triggers>
                                            </asp:UpdatePanel>
                                        </td>
                                    </tr>

If the value of ddEffectiveDateYear changes, in the selectedindexchanged event of the dropdownlist I make the appropriate changes to pddEffectiveDateMonth and pddEffectiveDateCurrencies. Similarly, when the user makes a change to pddEffectiveDateMonth I would like to make changes to pddEffectiveDateCurrencies. Right now there is some code in the prerender event of pddEffectiveDateMonth and A LOT of code in the prerendent event of pddEffectiveDateCurrencies. I believe the correct way is to add a SelectedIndexChanged event and maybe other events to the user control.

How do I do that??
 
Each on of the server control's you listed have their own event for handling state change. What you're going to want to do is use those events to fire your custom event.

From where are you planning on subscribing to the event? Are you wanting ucB to listen for an event on ucA (and vice versa)? If so, the easiest way to accomplish this probably going to be by using and interface. The following example illustrates how you can do this:

I've created an interface for both the containing page as well as the control. This way we don't have to loop through all the controls when it comes time subscribe to the event.

The container interface:

Code:
using System.Collections.Generic;
/// <summary>
/// Interface for the page containing the IStateChangeControls
/// </summary>
public interface IStateChangeContainer
{
    void RegisterControls();

    List<IStateChangeControl> StateChangeControlList { get; }
}

The control interface:

Code:
/// <summary>
/// Interface for controls that need IndexChanged event
/// </summary>
public interface IStateChangeControl
{
    event IndexChangedEventHandler IndexChanged;

}

Notice the RegisterControls method and the StateChangeControlList in the container interface. The RegisterControls method will be responsible for adding all the IStateChangeControls to the StateChangeControlList collection. The IStateChangeControl interface only contains the event we'll need to subscribe to later.

Here is the code and markup for the page that contains the controls:

Markup:
Code:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="StateChangeExample.aspx.cs" Inherits="StateChangeExample" %>
<%@ Register TagPrefix="uc" TagName="ControlA" Src="~/Controls/StateChangeExample.ascx" %>
<%@ Register TagPrefix="uc" TagName="ControlB" Src="~/Controls/StateChangeExample.ascx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[URL unfurl="true"]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">[/URL]

<html xmlns="[URL unfurl="true"]http://www.w3.org/1999/xhtml">[/URL]
<head runat="server">
    <title>State Change Event Example</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
       <uc:ControlA ID="ucA" runat="server" />
       <uc:ControlB ID="ucB" runat="server" />
    </div>
    </form>
</body>
</html>

Code-behind:
Code:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Generic;

public partial class StateChangeExample : System.Web.UI.Page, IStateChangeContainer 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Add IStateChangeControls to StateChangeControlList collection
        RegisterControls();
    }

    #region IStateChangeContainer Members

    public void RegisterControls()
    {
        this.StateChangeControlList.Add(ucA as IStateChangeControl);
        this.StateChangeControlList.Add(ucB as IStateChangeControl);
    }

    private List<IStateChangeControl> stateChangeControlList;
    public List<IStateChangeControl> StateChangeControlList
    {
        get 
        {
            if (stateChangeControlList == null)
                stateChangeControlList = new List<IStateChangeControl>();

            return stateChangeControlList;
        }
    }

    #endregion
}

There isn't to much going on in the above code. I'm just adding the IStateChangeControls to the StateChangeControlList so we don't have to use the Page.FindControl method when it comes time to subscribe to the custom event.

Next comes the markup and code-behind for the user control:

Markup:
Code:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="StateChangeExample.ascx.cs" Inherits="Controls_StateChangeExample" %>
<table>
    <tr>
        <td>
            <asp:DropDownList ID="ddlExample" runat="server" AutoPostBack="true"
                onselectedindexchanged="ddlExample_SelectedIndexChanged">
                <asp:ListItem Text="Option 1" Value="1"></asp:ListItem>
                <asp:ListItem Text="Option 2" Value="2"></asp:ListItem>
                <asp:ListItem Text="Option 3" Value="3"></asp:ListItem>
            </asp:DropDownList>
            
            </td>
    </tr>
    <tr>
        <td>
            <asp:ListBox ID="lbExample" runat="server" AutoPostBack="true"
                onselectedindexchanged="lbExample_SelectedIndexChanged">
                <asp:ListItem Text="Option 1" Value="1"></asp:ListItem>
                <asp:ListItem Text="Option 2" Value="2"></asp:ListItem>
                <asp:ListItem Text="Option 3" Value="3"></asp:ListItem>
            </asp:ListBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:CheckBoxList ID="cblExample" runat="server" AutoPostBack="true"
                onselectedindexchanged="cblExample_SelectedIndexChanged">
                <asp:ListItem Text="Option 1" Value="1"></asp:ListItem>
                <asp:ListItem Text="Option 2" Value="2"></asp:ListItem>
                <asp:ListItem Text="Option 3" Value="3"></asp:ListItem>
            </asp:CheckBoxList>
        </td>
    </tr>
</table>


Code-behind:
Code:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Generic;

public partial class Controls_StateChangeExample : System.Web.UI.UserControl, IStateChangeControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Cast Page to IStateChangeContainer so we can access the control list
        IStateChangeContainer page = Page as IStateChangeContainer;
        foreach (IStateChangeControl control in page.StateChangeControlList)
        {
            //Subscribe to the IndexChanged event on the opposite control
            if ((control as Control).ID != this.ID)
                control.IndexChanged += new IndexChangedEventHandler(control_IndexChanged);
        }
    }

    /// <summary>
    /// Event handler for IndexChanged event. Sychronizes this control with firing control. 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void control_IndexChanged(object sender, IndexChangedEventArgs e)
    {
        //Find the control that need to change state
        Control control = this.FindControl(e.ControlId);
        //If the control isn't a checkbox list set the selected index property 
        //**This will only work on controls that have a selected index..
        if (control.GetType() != typeof(CheckBoxList))
        {
            System.Reflection.PropertyInfo pi = control.GetType().GetProperty("SelectedIndex");
            if(pi != null)
                pi.SetValue(control, e.Index[0], null);
        }
        else
        {
            //Check/Uncheck desired items
            List<int> selectedItems = new List<int>();
            foreach (int i in e.Index)
                selectedItems.Add(i);

            int x = 0;
            foreach (ListItem item in cblExample.Items)
            {
                if (selectedItems.Contains(x))
                    item.Selected = true;
                else
                    item.Selected = false;

                x++;
            }
            
        }
        

    }

    protected virtual void OnIndexChanged(IndexChangedEventArgs e)
    {
        if (IndexChanged != null)
            IndexChanged(this, e);
    }

    #region IStateChangeControl Members

    public event IndexChangedEventHandler IndexChanged;

    #endregion


    protected void ddlExample_SelectedIndexChanged(object sender, EventArgs e)
    {
        DropDownList ddl = sender as DropDownList;
        OnIndexChanged(new IndexChangedEventArgs(ddl.ID, new int[] {ddl.SelectedIndex}));
    }
    protected void lbExample_SelectedIndexChanged(object sender, EventArgs e)
    {
        ListBox lb = sender as ListBox;
        OnIndexChanged(new IndexChangedEventArgs(lb.ID, new int[] {lb.SelectedIndex}));
    }
    protected void cblExample_SelectedIndexChanged(object sender, EventArgs e)
    {
        CheckBoxList cbl = sender as CheckBoxList;
        List<int> selectedItems = new List<int>();
        int i = 0;
        //Get all selected items
        foreach(ListItem item in cbl.Items)
        {
            if (item.Selected)
                selectedItems.Add(i);

            i++;
        }
        OnIndexChanged(new IndexChangedEventArgs(cbl.ID, (int[])selectedItems.ToArray<int>()));

    }
}

Finally we have the IndexChangedEventArgs class and the delegate

Code:
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

/// <summary>
/// Summary description for IndexChangedEventArgs
/// </summary>
public class IndexChangedEventArgs : EventArgs
{
    public IndexChangedEventArgs(string controlId, int[] index)
    {
        ControlId = controlId;
        Index = new int[index.Length];
        for(int i = 0; i < index.Length; i++)
            Index[i] = index[i];
        
    }

    private string controlId;
    public string ControlId
    {
        get { return controlId; }
        set { controlId = value; }
    }

    private int[] index;
    public int[] Index
    {
        get { return index; }
        set { index = value; }
    }

}

public delegate void IndexChangedEventHandler(object sender, IndexChangedEventArgs e);

This example has been tested and works. Hopefully this will serve as a useful illustration on how you can complete your task. Please let me know if you have any questions.

Good luck!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top