interactive | editorial | code | resource 
Demonstrations > Client state
 

Client state

Select your product:     

Tip: Try keeping the DbCombo drop-down open while changing the category and supplier drop-downs. DbCombo will react to the changes and get the updated data-set.

The code...

The code for this page is a little long, but I've included some comments to show you what's being done:

The first section simply draws the table, includes the four WebControls, and includes the DbCombo control. Notice on each of the WebControls we call our UpdateCombo() javascript function. This will tell DbCombo that the state has changed.

<p>
    <table>
        <tr>
            <td>
                <asp:CheckBox Runat="server" 
                    Text="Limit results to supplier:" 
                    ID="SupplierCheckBox" 
                    onclick="UpdateCombo();"/>
            </td>
            <td>
                <asp:DropDownList 
                    Runat="server" 
                    ID="SupplierDropDown" 
                    onchange="UpdateCombo();" />
            </td>
        </tr>
        <tr>
            <td>
                <asp:CheckBox Runat="server" 
                    Text="Limit results to category:" 
                    ID="CategoryCheckBox" 
                    onclick="UpdateCombo();"/>
            </td>
            <td>
                <asp:DropDownList Runat="server" 
                    ID="CategoryDropDown" 
                    onchange="UpdateCombo();" />
            </td>
        </tr>
        <tr>
            <td>Select your product:</td>
            <td>
                <DbCombo:DbCombo runat="server" ID="Combo1" 
                    ClientStateFunction="StateFunction()" 
                    OnGetDownLevelState="DownLevelState"
                    CloseResultsOnBlur="false" />
            </td>
        </tr>
    </table>
</p>		

Next is the javascript function we attached to the change events of the WebControls earlier. This calls the DbComboStateChanged function, using the UniqueID of the Combo control as a parameter.

<script language=javascript>
    function UpdateCombo()
    {
        if (typeof(DbComboServerExists)!='undefined')
            DbComboStateChanged('<%# Combo1.UniqueID %>');
    }
</script>

Next is our javascript state function. This is referenced by the ClientStateFunction="StateFunction()" attribute of our DbCombo tag. This wraps several bits of information about the page state in a javascript "Object" object, and returns it.

<script language=javascript>
    function StateFunction()
    {
        var state = new Object();
        categoryDropDown = document.all["<%# CategoryDropDown.UniqueID %>"];
        categoryCheckBox = document.all["<%# CategoryCheckBox.UniqueID %>"];
        supplierDropDown = document.all["<%# SupplierDropDown.UniqueID %>"];
        supplierCheckBox = document.all["<%# SupplierCheckBox.UniqueID %>"];
        
        if (categoryCheckBox.checked){
            state["ConstrainCategory"]="true";
            state["CategoryID"]=
                categoryDropDown[categoryDropDown.selectedIndex].value;
        }
        else
            state["ConstrainCategory"]="false";
            
        
        if (supplierCheckBox.checked){
            state["ConstrainSupplier"]="true";
            state["SupplierID"]=
                supplierDropDown[supplierDropDown.selectedIndex].value;
        }
        else
            state["ConstrainSupplier"]="false";
        
        return state;
        
    }
</script>

Next is our Page_Load server code block. This simply populates our two conventional drop-down's with data from the Categories and Suppliers tables. Note we also do a Page.DataBind() to Bind our <%# %> blocks.

<script runat=server>
    private void Page_Load(object sender, System.EventArgs e)
    {
        // Put user code to initialize the page here
        if (!Page.IsPostBack)
        {
            DataSet dataset=new DataSet();
            SqlConnection conn = new SqlConnection("your-connection-string");
            SqlDataAdapter adapter = new SqlDataAdapter();
            adapter.SelectCommand = 
                new SqlCommand("SELECT * FROM Categories", conn);
            adapter.Fill(dataset);
            conn.Close();
            CategoryDropDown.DataSource=dataset;
            CategoryDropDown.DataTextField="CategoryName";
            CategoryDropDown.DataValueField="CategoryID";
            CategoryDropDown.DataBind();

            dataset=new DataSet();
            adapter.SelectCommand = 
                new SqlCommand("SELECT * FROM Suppliers", conn);
            adapter.Fill(dataset);
            conn.Close();
            SupplierDropDown.DataSource=dataset;
            SupplierDropDown.DataTextField="CompanyName";
            SupplierDropDown.DataValueField="SupplierID";
            SupplierDropDown.DataBind();
        }
        Page.DataBind();
    }
</script>

Next is our DbCombo server method. It first inspects the clientState Hashtable. We have populated this earlier in Javascript, and DbCombo has converted it to a Hashtable for us. Using this we create an SQL fragment which is then appended to a simple SQL statement. The SQL statement is used to create a DataSet, which is returned.

<script runat=server>
    [Cambro.Web.DbCombo.ResultsMethod(true)]
    public static object DbComboMethod(
        Cambro.Web.DbCombo.ServerMethodArgs args)
    {
        string extraWhereClause = "";
        if (args.ClientState!=null)
        {
            if (
                args.ClientState["ConstrainCategory"] != null && 
                args.ClientState["ConstrainCategory"].ToString()=="true" 
                )
            {
                extraWhereClause += " AND CategoryID = " + 
                    int.Parse(args.ClientState["CategoryID"].ToString()) + " ";
            }
            
            if (
                args.ClientState["ConstrainSupplier"] != null && 
                args.ClientState["ConstrainSupplier"].ToString()=="true"
                )
            {
                extraWhereClause += " AND SupplierID = " + 
                    int.Parse(args.ClientState["SupplierID"].ToString()) + " ";
            }
        }
        DataSet dataset=new DataSet();
        
        SqlConnection conn = new SqlConnection("your-connection-string");
        
        SqlDataAdapter adapter = new SqlDataAdapter();
        
        adapter.SelectCommand = new SqlCommand(@"
            SELECT TOP "+args.Top+@" 
                ProductName AS DbComboText, 
                ProductID AS DbComboValue 
            FROM Products WHERE 
            ProductName LIKE @Query "+extraWhereClause+@" 
            ORDER BY ProductName", conn);
            
        adapter.SelectCommand.Parameters.Add("@Query", args.Query+"%");
        
        adapter.Fill(dataset);
        
        conn.Close();
        
        return dataset;
        
    }
</script>

This server code function is only necessary for down-level browser compatibility. It is referenced in the DbCombo tag by the OnGetDownLevelState="DownLevelState" attribute. As you can see, it does the same job as the Javascript state function we saw earlier, but using PostBack data.

<script runat=server>
    protected Hashtable DownLevelState(DbCombo sender)
    {
        //This function is only used in down-level browser mode. 
        //If you know all browsers will be IE5+, this function may be
        //omitted.
        
        Hashtable state = new Hashtable();
        if (CategoryCheckBox.Checked){
            state["ConstrainCategory"]="true";
            state["CategoryID"]=CategoryDropDown.SelectedItem.Value;
        }
        else
            state["ConstrainCategory"]="false";
        
        if (SupplierCheckBox.Checked){
            state["ConstrainSupplier"]="true";
            state["SupplierID"]=SupplierDropDown.SelectedItem.Value;
        }
        else
            state["ConstrainSupplier"]="false";
        return state;
    }
</script>
			

 
Demonstrations > Client state