Using Client Callbacks for Building the ListBoxesFTs_C ASP.NET Web User Control: Part I

In the article we discussed the web user control (ListBoxesFT_C) that includes two ListBox controls and allows to exchange data between them by two ways. The first way is the "clear server side" with postback and the second one is the "clear client side" without any use of server methods. Of course, building your own control you can choose any of these ways (it will  depend on requirements to your project). But there is the third way and we have solved we shall necessarily discuss this way. This is one of the new built-in features of ASP.NET 2.0 , known as Client Callbacks feature. With the help of this mechanism you can use server side methods for changing data of your control without postback and thus to avoid recreating the parent page with all its controls and data . Using this feature we don't need to use HTTP object and to create for it some code, we just use ICallbackEventHandler interface and two methods : RaiseCallbackEvent and GetCallbackResult. In this article with the help of our old friend (ListBoxesFT_C)  I share how you can build your own ASP.NET web user control with the help of  the Client Callbacks. The examples are written using C# and JavaScript.

 

We define the task as follows (in analogues with the task for  the ListBoxesFT_C but with some changes).

 

To build Web User Control , named ListBoxesFTs_C , that allows to select from input data (as "input DataTable") needed items and then to get selected data ( as "output DataTable") on the parent web page. The control should include the following basic built-in ASP.NET Web server controls : two ListBox controls , named ListBoxFrom and ListBoxTo (to display and select "input" and "output" items); two Button controls to manipulate data ("to transfer" items from one ListBox into another) ; two Label control to specify ListBox controls (with the text "List" and "Select"). Data exchange do not entail re-creation of the page; with this purpose use the Client Callbacks  mechanism .The control should look as follows (fig 1.).

 

Figure 1.

 

The ListBoxesFTs_C control should have the following properties:

  • C_DataIn , type of "DataTable", that allows to set item list  for the control;
  • C_DataOut, type of "DataTable", that allows to get selected items on the parent page;
  • C_LabelFrom , type of "string", that allows to set and get the text  of  the button, "transfering" items from "List" into "Select"; default text is ">>";
  • C_LabelTo , type of "string", that allows to set and get the text  of  the button, "transfering" items from "Select" into "List"; default text is "<<";
  • C_WidthLB , type of "int" , that allows to set and get the width of the ListBoxes controls; default value is 60;
  • C_HeightLB , type of "int" , that allows to set and get the height of the ListBoxes controls; default value is 120;
  • C_SortBy , type of  "int" , that allows to set and get number of the column for sorting the "input" DataTable; default value is 1.

Of course we have to begin with the Web site project to which we will be able to add our web user control. We will use our "WebSites_Test" project. We just add to the project Web user control , named "ListBoxesFTs_C". In order to test the control we add the web form named "WF_ListBoxes_s". To the Menu control of Default.aspx we add one more MenuItem. Now our solution looks so (fig.2) :

 

 

Figure 2.

 

The design of the ListBoxesFTs_C control is similar to design of the ListBoxesFT_C control. There are a few changes. The first one is absence of the server Button controls. We just don't need them. The second change is "inclusion" the ListBox controls into "div" like this:

 

<div id="divListBoxTo"  runat=server>

          <asp:ListBox ID="ListBoxTo" runat="server">

          </asp:ListBox>

</div>

 

We need the last change to "re-write" the ListBox control on the client side according to "id" and data, received from the server side.

 

The design of the control you can see on fig. 3:

 

 

Figure 3.

 

The source of the design is the following:

 

<div style="text-align: center">

    <table id="TableBorder" runat=server border="1">

        <tr>

            <td align="center" style="width: 100px" valign="top">

                <div style="text-align: center">

                    <table id="TableListBoxes"

                            style="width: 100%; height: 100%" >

                        <tr align =center >

                            <td style="width: 100%; height: 100%" >

<asp:Label ID="LabelFrom" runat="server" Text="List"></asp:Label></td>

                            <td style="width: 100%; height: 100%">&nbsp;

                            </td>

                            <td style="width: 100%; height: 100%"  >

<asp:Label ID="LabelTo" runat="server" Text="Select"></asp:Label></td>

                        </tr>

                        <tr align =center >

                            <td style="width: 100%; height: 100%">

                            <div id="divListBoxFrom"  runat=server>

<asp:ListBox ID="ListBoxFrom" runat="server"></asp:ListBox>

                            </div></td>

                            <td style="width: 100%; height: 100%">

<p >

    &nbsp;<input id="ButtonHTMLFrom" type="button" value=">>"  runat=server

        /></p>

<p>

    &nbsp;<input id="ButtonHTMLTo" type="button" value="<<" runat=server

         /></p></td>

                            <td style="width: 100%; height: 100%" >

                            <div id="divListBoxTo"  runat=server>

<asp:ListBox ID="ListBoxTo" runat="server"></asp:ListBox>

                            </div>

                            </td>

                        </tr>

                    </table>

                </div>

                <input id="HiddenFromValue" runat="server" style="width: 23px"

                    type="hidden" />

                <input

                    id="HiddenFromText" runat="server" style="width: 23px"

                    type="hidden" />

                 <input id="HiddenToValue"

                        runat="server" style="width: 23px" type="hidden" />

                 <input id="HiddenToText" runat="server"

                            style="width: 23px" type="hidden" /></td>

        </tr>

    </table>

</div>

 

OK! Now we are ready to write code (server and client ) for our control.

 

First of all we should add the ICallbackEventHandler interface declaration to our class:

 

public partial class UserControls_ListBoxesFTs_C : System.Web.UI.UserControl, System.Web.UI.ICallbackEventHandler 

 

Then we create region named "Callback". Here we are going to write all server code for Callback mechanism. In order to dynamically create a function named CallTheServer (this function invokes the callback) we write the method ForCallBackOnLoad:

 

private void ForCallBackOnLoad()

{

    ClientScriptManager CS = Page.ClientScript;

    String cbRef = CS.GetCallbackEventReference(this, "arg",

        "ReceiveServerData", "");

    String cbScript = "function CallTheServer(arg) {" +

        cbRef + "; }";

    CS.RegisterClientScriptBlock(this.GetType(), "CallTheServer",

        cbScript, true);

}

 

Here the GetCallbackEventReference defines a client function (ReceiveServerData) which receive the result from the server.

 

This method we add to the Page_Load method:

 

protected void Page_Load(object sender, EventArgs e)

{

    //some code for the Page_load  

    ForCallBackOnLoad();

}

 

The members of the ICallbackEventHandler are the GetCallbackResult and RaiseCallbackEvent methods. The second one processes a callback event that targets a control and has one parameter (string eventArgument). In our case this parameter includes selected index of ListBox, what ListBox is selected (ListBoxFrom or ListBoxTo) and list of items of the ListBox controls. The parameter has to have type of string; we form this string on client side (onclick event and "send" with the help of the  CallTheServer function). On server side we receive this string as follows:

 

string _cb_Return = "";

public void RaiseCallbackEvent(string eventArgument)

{

    _cb_Return  = eventArgument;

}

 

Now we can use the _cb_Return variable in our further logic.

 

With the help of the GetCallbackResult, which returns the result of a callback event, we process the _cb_Return variable :

  • with the split method we create Array, 
  • get data for exchange(getDT_Hidden method), 
  • build the ListBox controls (onClickFromTo method),
  • render control, 
  • create string (for the ReceiveServerData client function) and 
  • return this string: 

public string GetCallbackResult()

{

    StringBuilder SB = new StringBuilder();

    bool bFrom;

    int iSelected;

    string sSplit = _Split;

    Array arrReturn;

    char[] cIndexOf = { '!' };

    StringWriter sWriteTo = new System.IO.StringWriter();

    HtmlTextWriter htmlWriterTo = new HtmlTextWriter(sWriteTo);

    StringWriter sWriteFrom = new System.IO.StringWriter();

    HtmlTextWriter htmlWriterFrom = new HtmlTextWriter(sWriteFrom);

 

    arrReturn = _cb_Return.Split(cIndexOf);

 

    iSelected = Convert.ToInt32(arrReturn.GetValue(0).ToString().Trim());

    bFrom = Convert.ToBoolean(arrReturn.GetValue(1).ToString().Trim());

    getDT_Hidden(arrReturn.GetValue(2).ToString().Trim(),

        arrReturn.GetValue(3).ToString().Trim(),

        arrReturn.GetValue(4).ToString().Trim(),

        arrReturn.GetValue(5).ToString().Trim());

    onClickFromTo(bFrom,iSelected );

         

    ListBoxTo.RenderControl(htmlWriterTo);

    ListBoxFrom.RenderControl(htmlWriterFrom);

 

    SB.Append(FindControl(divListBoxTo.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(sWriteTo.ToString());

    SB.Append(sSplit);

    SB.Append(FindControl(divListBoxFrom.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(sWriteFrom.ToString());

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(ListBoxFrom.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(ListBoxTo.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(HiddenFromText.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(HiddenFromValue.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(HiddenToText.ID).ClientID);

    SB.Append(sSplit);

    SB.Append(this.Parent.ID + "." +

        FindControl(HiddenToValue.ID).ClientID);

 

    return SB.ToString();

}

 

private void onClickFromTo(bool clickFrom,int iSelected)

{

    ListBox lb;

    DataTable dtFrom;

    DataTable dtTo;

    int iItemsList = 0;

    int iCount = 0;

    int iCountHelp = 0;

    if (clickFrom)

    {

        lb = ListBoxFrom;

        dtFrom = _DT_In;

        dtTo = _DT_Out;

    }

    else

    {

        lb = ListBoxTo;

        dtFrom = _DT_Out;

        dtTo = _DT_In;

    }

    iItemsList = dtFrom.Rows.Count;

    for (iCount = 0; iCount < iItemsList; iCount++)

    {

        if (iCount == iSelected)

        {

            DataRow dr;

            dr = dtTo.NewRow();

            dr[0] = dtFrom.Rows[iCount - iCountHelp][0];

            dr[1] = dtFrom.Rows[iCount - iCountHelp][1];

            dtTo.Rows.Add(dr);

            dtFrom.Rows[iCount - iCountHelp].Delete();

            iCountHelp = iCountHelp + 1;

        }

    }

    if (clickFrom)

    {

        _DT_In = dataTable_Sort(dtFrom.Copy());

        _DT_Out = dataTable_Sort(dtTo.Copy());

    }

    else

    {

        _DT_Out = dataTable_Sort(dtFrom.Copy());

        _DT_In = dataTable_Sort(dtTo.Copy());

    }

    buildListBox(ListBoxFrom, _DT_In);

    buildListBox(ListBoxTo, _DT_Out);

}

 

The full text of the ListBoxesFTs_C.ascx.cs  and client script of the ListBoxesFTs_C.ascx  and test result you can find in the next part

 

Good luck in programming ! 

Next Recommended Readings