Update Panel .NET

Exploring Microsoft ASP.NET AJAX and jQuery

Posts Tagged ‘Sys.Binding

Getting started with Sys.Binding – Part 3

leave a comment »

In part one, I demonstrated how Sys.Binding can be used to bind properties of DOM elements, and in part two, I used a simple JavaScript object to share properties across multiple DOM elements.  In this post, I will expand on the topic by adding components to the mix.  By components I mean objects derived from Sys.Component, that includes Sys.UI.Behavior and Sys.UI.Control.

Sys.Binding works with components by hooking up the propertyChanged event to track changes made to the bound properties, and gets or sets values by invoking the relevant getter and setter functions.

As for the example, I will expand on the one given in part two, and add a slider to allow the user to adjust the range by dragging on the handles, like this:

Screen capture of example

I know there is a slider control in the AJAX Control Toolkit, but it currently does not support multiple handles, and I need two for this example.  I also know there is a MultiHandleSlider control in development, but I need it now!

Well, jQuery UI has a nice slider control that already supports multiple handles, so I will be using it for my example.  Download the 1.5.2 stable build, unzip the archive and add the folder to your web site:

Web site project structure

There is a “slight” problem – the jQuery UI slider cannot be used directly with Sys.Binding, becuase it does not have the necessary property getter and setter functions, i.e. get_value() and set_value(), nor the “propertyChanged” event, required by Sys.Binding.  As a workaround, I wrapped the jQuery UI slider inside a Sys.UI.Control:

Type.registerNamespace("UPDN.jQuery.UI");
UPDN.jQuery.UI.Slider = function(element) {
    UPDN.jQuery.UI.Slider.initializeBase(this, [element]);
    this._slider = $(element);
    this._animate = false;
    this._enabled = true;
    this._handles = [];
    this._max = 100;
    this._min = 0;
    this._orientation = "horizontal";
    this._range = false;
    this._stepping = 0;
    this._steps = 0;
}
UPDN.jQuery.UI.Slider.prototype = {
    initialize: function() {
        var options = {
            animate: this.get_animate(),
            axis: this.get_orientation(),
            handles: this.get_handles(),
            max: this.get_max(),
            min: this.get_min(),
            range: this.get_range(),
            steps: this.get_steps(),
            change: Function.createDelegate(this, this._raiseChangeEvent),
            slide: Function.createDelegate(this, this._raiseSlideEvent),
            start: Function.createDelegate(this, this._raiseStartEvent),
            stop: Function.createDelegate(this, this._raiseStopEvent)
        }
        this._slider.slider(options);
        // Dynamically add value property getter and setter for each handle
        // Property name is "value" + handle.id, as specified in the options.
        // If handle.id is "1" then property is get_value1() and set_value1(value).
        var handles = this.get_handles();
        for (var i = 0; i < handles.length; i++) {
            this._addValueProperty(i, handles[i]);
        }
    },
    dispose: function() {
        this._slider.slider("destroy");
    },
    _addValueProperty: function(i, handle) {
        var propertyName = "value" + handle.id;
        this["get_" + propertyName] = function(index) {
            return function() {
                return this.getValue(index);
            }
        } (i);
        this["set_" + propertyName] = function(index) {
            return function(value) {
                this.setValue(index, value);
            }
        } (i);
        this.add_slide(Function.createDelegate(this, function() {
            this.raisePropertyChanged(propertyName);
        }));
    },
    _raiseChangeEvent: function() {
        var handler = this.get_events().getHandler("change");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },
    _raiseSlideEvent: function() {
        var handler = this.get_events().getHandler("slide");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },
    _raiseStartEvent: function() {
        var handler = this.get_events().getHandler("start");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },
    _raiseStopEvent: function() {
        var handler = this.get_events().getHandler("stop");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },
    add_change: function(handler) {
        /// <summary>Adds a event handler for the change event.</summary>
        /// <param name="handler" type="Function">The handler to add to the event.</param>
        this.get_events().addHandler("change", handler);
    },
    remove_change: function(handler) {
        /// <summary>Removes a event handler for the change event.</summary>
        /// <param name="handler" type="Function">The handler to remove from the event.</param>
        this.get_events().removeHandler("change", handler);
    },
    add_slide: function(handler) {
        /// <summary>Adds a event handler for the slide event.</summary>
        /// <param name="handler" type="Function">The handler to add to the event.</param>
        this.get_events().addHandler("slide", handler);
    },
    remove_slide: function(handler) {
        /// <summary>Removes a event handler for the slide event.</summary>
        /// <param name="handler" type="Function">The handler to remove from the event.</param>
        this.get_events().removeHandler("slide", handler);
    },
    add_start: function(handler) {
        /// <summary>Adds a event handler for the start event.</summary>
        /// <param name="handler" type="Function">The handler to add to the event.</param>
        this.get_events().addHandler("start", handler);
    },
    remove_start: function(handler) {
        /// <summary>Removes a event handler for the start event.</summary>
        /// <param name="handler" type="Function">The handler to remove from the event.</param>
        this.get_events().removeHandler("start", handler);
    },
    add_stop: function(handler) {
        /// <summary>Adds a event handler for the stop event.</summary>
        /// <param name="handler" type="Function">The handler to add to the event.</param>
        this.get_events().addHandler("stop", handler);
    },
    remove_stop: function(handler) {
        /// <summary>Removes a event handler for the stop event.</summary>
        /// <param name="handler" type="Function">The handler to remove from the event.</param>
        this.get_events().removeHandler("stop", handler);
    },
    get_animate: function() {
        /// <value type="Boolean">True if slider handle animation is enabled, false if disabled.</value>
        return this._animate;
    },
    set_animate: function(value) {
        this._animate = value;
    },
    get_enabled: function() {
        /// <value type="Boolean">True if slider is enabled, false if disabled.</value>
        return this._enabled;
    },
    set_enabled: function(value) {
        if (this._enabled !== value) {
            this._enabled = value;
            if (value) {
                this._slider.slider("enable");
            } else {
                this._slider.slider("disable");
            }
            this.raisePropertyChanged("enabled");
        }
    },
    get_handles: function() {
        /// <value type="Object">Specify boundaries for one or more handles.</value>
        return this._handles;
    },
    set_handles: function(value) {
        this._handles = value;
    },
    get_max: function() {
        /// <value type="Number">The maximum value of the slider.</value>
        return this._max;
    },
    set_max: function(value) {
        this._max = value;
    },
    get_min: function() {
        /// <value type="Number">The minimum value of the slider.</value>
        return this._min;
    },
    set_min: function(value) {
        this._min = value;
    },
    get_orientation: function() {
        /// <value type="String">The orientation of the slider.</value>
        return this._orientation;
    },
    set_orientation: function(value) {
        this._orientation = value;
    },
    get_range: function() {
        /// <value type="Boolean">True if slider range is enabled, false if disabled.</value>
        return this._range;
    },
    set_range: function(value) {
        this._range = value;
    },
    get_stepping: function() {
        /// <value type="Number">The number to jump per slide.</value>
        return this._stepping;
    },
    set_stepping: function(value) {
        this._stepping = value;
    },
    get_steps: function() {
        /// <value type="Number">The number of steps the slider have.</value>
        return this._steps;
    },
    set_steps: function(value) {
        this._steps = value;
    },
    getValue: function(index) {
        /// <summary>Gets the current value of the specified handle.</summary>
        /// <param name="index" type="Number">The index/number of the handle, zero-based.</param>
        return this._slider.slider("value", index);
    },
    setValue: function(index, value) {
        /// <summary>Sets the value of the specified handle.</summary>
        /// <param name="index" type="Number">The index/number of the handle, zero-based.</param>
        /// <param name="value" type="Number">The value to be set.</param>
        this._slider.slider("moveTo", value, index);
    }
}
UPDN.jQuery.UI.Slider.registerClass("UPDN.jQuery.UI.Slider", Sys.UI.Control);

Save this script as UPDN.jQuery.UI.Slider.js and add it the web site project.

By wrapping the jQuery UI slider inside a Sys.UI.Control, I have exposed the necessary property getters and setters and hooked up the “slide” event to raise the propertyChanged event whenever the user moves the slider handle(s).

Include a few stylesheets to the page, between the <head> tags:

<link href="jquery.ui-1.5.2/themes/flora/flora.css" rel="stylesheet" type="text/css" />
<link href="jquery.ui-1.5.2/themes/flora/flora.slider.css" rel="stylesheet" type="text/css" />
<style type="text/css" media="all">
#Slider1 { margin-top: 1.5em; }
.label-1 { position: absolute; left: 0; top: -1.1em; }
.label-2 { position: absolute; right: 0; top: -1.1em; }
</style>

Add the <asp:ScriptManager> control and include the necessary scripts:

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/MicrosoftAjaxTemplates.js" ScriptMode="Auto" />
        <asp:ScriptReference Path="~/jquery.ui-1.5.2/jquery-1.2.6.js" ScriptMode="Auto" />
        <asp:ScriptReference Path="~/jquery.ui-1.5.2/ui/ui.core.js" ScriptMode="Auto" />
        <asp:ScriptReference Path="~/jquery.ui-1.5.2/ui/ui.slider.js" ScriptMode="Auto" />
        <asp:ScriptReference Path="~/UPDN.jQuery.UI.Slider.js" ScriptMode="Auto" />
    </Scripts>
</asp:ScriptManager>

Add the mark-up for the slider to the page:

<asp:CheckBox ID="CheckBox1" runat="server" Text="Use slider" Checked="true" />
<div id="Slider1" class="ui-slider-2">
    <span class="label-1">0</span>
    <span class="label-2">100</span>
</div>

Add the following blocks of JavaScript to the pageLoad() function:

var slider = $create(UPDN.jQuery.UI.Slider, {
    handles: [ { start: 0, min: 0, max: 100, id: "1" },
               { start: 100, min: 0, max: 100, id: "2" } ],
    range: true
}, null, null, $get("Slider1"));

This block creates the slider component, and assigns it the the slider variable for later usage.

$create(Sys.Binding, {
    target: slider,
    targetProperty: "enabled",
    source: $get("CheckBox1"),
    path: "checked",
    mode: Sys.BindingMode.oneWay
});

This block creates the binding between the check box element and the “enabled” property of the slider component, so the user can enable or disable the slider by selecting the check box.

$create(Sys.Binding, {
    target: slider,
    targetProperty: "value1",
    source: range,
    path: "min",
    mode: Sys.BindingMode.twoWay
});
$create(Sys.Binding, {
    target: slider,
    targetProperty: "value2",
    source: range,
    path: "max",
    mode: Sys.BindingMode.twoWay
});

This blocks binds the slider handles to the range JavaScript object.

Start the web site and you should be able to change the min and max values by entering a value into the text boxes or moving the handles on the slider, and watch the changes reflected in all the bound elements.

Written by tzkuei

November 16, 2008 at 10:58 pm

Posted in Microsoft AJAX Library

Tagged with ,

Getting started with Sys.Binding – Part 2

leave a comment »

In part one I have shown how you can bind properties of two DOM elements, in this post, I will demonstrate how you can bind properties of DOM elements to a JavaScript object.

Under the hood, Sys.Binding uses the Sys.Observer component to track changes to objects.  An “observable” object can be a DOM element, a component (i.e. derived Sys.Component), or for that matter, any JavaScript object.

For the example, first create a blank ASP.NET Web Site and add the MicrosoftAjaxTemplates.js script.

Add the following mark-up to the page (between the <div> tags):

<asp:Label ID="Label1" runat="server" Text="Min:" AssociatedControlID="TextBox1" />
<asp:TextBox ID="TextBox1" runat="server" />
<asp:Label ID="Label2" runat="server" Text="Max:" AssociatedControlID="TextBox2" />
<asp:TextBox ID="TextBox2" runat="server" />
Range - min: <asp:Label ID="Label3" runat="server" /> max: <asp:Label ID="Label4" runat="server" />

Then add the following script inside a <script> block just before the closing </form> tag:

var range = { min: 0, max: 100 };
function pageLoad() {
    $create(Sys.Binding, {
        target: $get("TextBox1"),
        targetProperty: "value",
        source: range,
        path: "min",
        mode: Sys.BindingMode.twoWay
    });
    $create(Sys.Binding, {
        target: $get("TextBox2"),
        targetProperty: "value",
        source: range,
        path: "max",
        mode: Sys.BindingMode.twoWay
    });
    $create(Sys.Binding, {
        target: $get("Label3"),
        targetProperty: "innerHTML",
        source: range,
        path: "min",
        mode: Sys.BindingMode.oneWay
    });
    $create(Sys.Binding, {
        target: $get("Label4"),
        targetProperty: "innerHTML",
        source: range,
        path: "max",
        mode: Sys.BindingMode.oneWay
    });
}

Start the web site and you should see a page like this:

Page output

Try changing the value in either text box, the corresponding label will be updated automatically.

This is how it works – firstly, I define a JavaScript object named “range” (using JavaScript object notation). The range object has two properties, “min” and “max”.

var range = { min: 0, max: 100 };

I then bind the value of one text box to the min property of range and the value of the other text box to max. The bindings in both cases are two way, because I want any change made to the text box be reflected in the range object and vice versa.

$create(Sys.Binding, {
    target: $get("TextBox1"),
    targetProperty: "value",
    source: range,
    path: "min",
    mode: Sys.BindingMode.twoWay
});
$create(Sys.Binding, {
    target: $get("TextBox2"),
    targetProperty: "value",
    source: range,
    path: "max",
    mode: Sys.BindingMode.twoWay
});

To complete the example, I bind the innerHTML property of Label3 and Label4 to the min and max properties of the range object respectively, only this time, the binding is one way, because I only want the changes to the range object be reflected in the labels.

$create(Sys.Binding, {
    target: $get("Label3"),
    targetProperty: "innerHTML",
    source: range,
    path: "min",
    mode: Sys.BindingMode.oneWay
});
$create(Sys.Binding, {
    target: $get("Label4"),
    targetProperty: "innerHTML",
    source: range,
    path: "max",
    mode: Sys.BindingMode.oneWay
});

Please note that you should not “live bind” the innerHTML property of an element, because your page will potentially be at risk to script injection attacks.

In my next post, I will expand on this example by adding a slider control.

For more information on bindings, please refer to these posts:

Written by tzkuei

November 14, 2008 at 1:58 pm

Getting started with Sys.Binding – Part 1

leave a comment »

The Sys.Binding component in ASP.NET 4.0 Preview is really cool, you can use it to “bind” properties of two DOM elements and have them “synchronised” when the property value of either element changes.

Sys.Binding will automatically attach the relevant DOM event handler (i.e. change, click, keyup),  to monitor user input and have the change reflected in the bound elements.

I will now walk you through a simple example:

Start Microsoft Visual Studio (2008) and create a blank ASP.NET Web Site (.NET Framework 3.5).

Download AspNetAjaxPreview3.zip, un-zip it then copy and paste the script files into your web site project.

Web site project structure

Just after the opening <form> tag, add the ScriptManager control and a reference to MicrosoftAjaxTemplates.js

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/MicrosoftAjaxTemplates.js" ScriptMode="Auto" />
    </Scripts>
</asp:ScriptManager>

Then add two TextBox controls to the page (between the <div> tags):

<asp:TextBox ID="TextBox1" runat="server" />
<asp:TextBox ID="TextBox2" runat="server" />

Lastly, add the following script inside a <script> block just before the closing </form> tag:

function pageLoad() {
    $create(Sys.Binding, {
        target: $get("TextBox2"),
        targetProperty: "value",
        source: $get("TextBox1"),
        path: "value",
        mode: Sys.BindingMode.twoWay
    });
}

This script creates a Sys.Binding component to bind the value property of TextBox1 (i.e. “source” and ”path”) to the value property of TextBox2 (i.e. “target” and “targetProperty”).  The binding “mode” is two way, so that any change made to TextBox1 will be reflected in TextBox2 and vice versa.

Start the web site, type something in either text box, and when you tab out, the value will be reflected in the other text box.

For more information on bindings, please refer to these posts:

In my next post, I will show you how to use Sys.Binding with JavaScript objects.

Written by tzkuei

November 13, 2008 at 9:42 am

Posted in Microsoft AJAX Library

Tagged with