<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.3.2" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>ILOG .NET Visualization Blog &#187; Uncategorized</title>
	<link>http://blogs.ilog.com/netvisu</link>
	<description>Postings from the Visualization .NET Development Team</description>
	<pubDate>Fri, 28 Nov 2008 13:39:40 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.3.2</generator>
	<language>en</language>
			<item>
		<title>How to Create a User Symbol That Acts as a Container</title>
		<link>http://blogs.ilog.com/netvisu/2008/07/30/how-to-create-a-user-symbol-that-acts-as-a-container/</link>
		<comments>http://blogs.ilog.com/netvisu/2008/07/30/how-to-create-a-user-symbol-that-acts-as-a-container/#comments</comments>
		<pubDate>Wed, 30 Jul 2008 16:37:31 +0000</pubDate>
		<dc:creator>Eric Durocher</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[Diagram]]></category>

		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[container]]></category>

		<category><![CDATA[user symbol]]></category>

		<guid isPermaLink="false">http://blogs.ilog.com/netvisu/2008/07/30/how-to-create-a-user-symbol-that-acts-as-a-container/</guid>
		<description><![CDATA[With ILOG Diagrammer for .NET, you can create user symbols very easily, and this is covered extensively in the user manual and <a href="http://blogs.ilog.com/netvisu/2008/04/25/diagrammer-net-tips-and-tricks-explained-through-the-uml-class-diagram-sample/">this post</a>, among others.

ILOG Diagrammer for .NET also provides several built-in container classes, for example the Canvas&#8230;]]></description>
			<content:encoded><![CDATA[<p>With ILOG Diagrammer for .NET, you can create user symbols very easily, and this is covered extensively in the user manual and <a href="http://blogs.ilog.com/netvisu/2008/04/25/diagrammer-net-tips-and-tricks-explained-through-the-uml-class-diagram-sample/">this post</a>, among others.</p>
<p>ILOG Diagrammer for .NET also provides several built-in container classes, for example the Canvas class, that is very useful to create composite graphic objects.</p>
<p>Something that you may need to do is to combine these two features, that is, create a user symbol that acts as a container. This is pretty easy to do, but there is no sample that shows it, so here we go. We will implement a very simple ContainerSymbol that looks like this:</p>
<p><a href="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol1.png" title="containersymbol1.png"><img src="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol1.png" alt="containersymbol1.png" /></a></p>
<p>The ContainerSymbol is a simple rectangular symbol with a title, and it can contain children (two rectangles in the picture above).</p>
<p>As usual, we will use Visual Studio to build our symbol. It is made of a DockPanel that contains a title and a nested Canvas:</p>
<p><a href="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol2.png" title="containersymbol2.png"><img src="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol2.png" alt="containersymbol2.png" /></a></p>
<p>Now, we need to add a bit of code to make sure that our symbol will accept children.</p>
<p>First, the symbol must have a property that will hold the collection of children.We will call this property SymbolChildren (not Children, because this is an existing property of the UserSymbol class):</p>
<pre>        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Collection&lt;GraphicObject&gt; SymbolChildren
        {
            get { return canvas1.Objects; }
        }</pre>
<p>As you can see the SymbolChildren property just returns the Objects property of the nested Canvas, so when a graphic object is added to the SymbolChildren collection, it will really be added to the nested Canvas. Note the DesignerSerializationVisibility attribute: it makes sure the collection&#8217;s contents will be serialized even though the property is read-only.</p>
<p>OK, now we can already add children to our symbol by writing code like this:</p>
<pre>        containerSymbol.SymbolChildren.Add(new Rect(0, 0, 100, 100));</pre>
<p>But we want our symbol to behave like a fully functional container, that is, we want to be able to drag-and-drop children into it when we use the Visual Studio designer. For this, let&#8217;s add this other piece of code:</p>
<pre>        protected override GraphicContainer GetContainerForLogicalChildren()
        {
            return canvas1;
        }</pre>
<p>This override tells the user symbol that it has a special element that will contain the &#8220;logical&#8221; children of the symbol, which means that the children will appear to be added to the symbol, whereas in fact they will be added to the nested Canvas.</p>
<p>Finally, one last piece of code is necessary to make sure that the children of the container symbol are correctly serialized in XML. For this, we need to define a custom designer for our symbol:</p>
<pre>   [Designer(typeof(ParentSymbolDesigner))]
    public partial class ContainerSymbol : UserSymbol
    {</pre>
<pre>        ...</pre>
<pre>    public class ParentSymbolDesigner : UserSymbolDesigner
    {
        protected override PropertyDescriptor GetChildrenPropertyDescriptor()
        {
            return TypeDescriptor.GetProperties(Component)["SymbolChildren"];
        }
    }</pre>
<p>The GetChildrenPropertyDescriptor virtual method tells ILOG Diagrammer for .NET which property to use to serialize the children of the container symbol.</p>
<p>We are done! Our container symbol acts as a basic container, you can add or remove children to it in the Visual Studio designer, select and move the children, etc.<a href="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol.zip" title="containersymbol.zip"></a></p>
<p>Here is the full  <a href="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/07/containersymbol.zip" title="source code">source code</a> of the sample.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.ilog.com/netvisu/2008/07/30/how-to-create-a-user-symbol-that-acts-as-a-container/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Creating Bezier Curves in an  Ajax DiagramView (Part 2)</title>
		<link>http://blogs.ilog.com/netvisu/2008/06/20/create-bezier-curve-in-an-ajaxdiagramview-part-2/</link>
		<comments>http://blogs.ilog.com/netvisu/2008/06/20/create-bezier-curve-in-an-ajaxdiagramview-part-2/#comments</comments>
		<pubDate>Fri, 20 Jun 2008 09:32:20 +0000</pubDate>
		<dc:creator>Patrick Ruzand</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[Ajax]]></category>

		<category><![CDATA[Diagram]]></category>

		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.ilog.com/netvisu/2008/06/20/create-bezier-curve-in-an-ajaxdiagramview-part-2/</guid>
		<description><![CDATA[<a href="http://blogs.ilog.com/netvisu/2008/06/10/creating-bezier-curves-in-an-ajax-diagramview-part-1/" title="Part 1">In the previous article</a>, I have explained the fundations of the Ajax interaction framework of <a target="_blank" href="http://www.ilog.com/products/diagrammernet/" title="ILOG Diagrammer for .NET">ILOG Diagrammer for .NET</a>. It is now time to dive into the implementation details.

From the first article, we know an <code>AjaxDiagramView</code> interactor inherits from the&#8230;]]></description>
			<content:encoded><![CDATA[<p><a href="http://blogs.ilog.com/netvisu/2008/06/10/creating-bezier-curves-in-an-ajax-diagramview-part-1/" title="Part 1">In the previous article</a>, I have explained the fundations of the Ajax interaction framework of <a target="_blank" href="http://www.ilog.com/products/diagrammernet/" title="ILOG Diagrammer for .NET">ILOG Diagrammer for .NET</a>. It is now time to dive into the implementation details.</p>
<p>From the first article, we know an <code>AjaxDiagramView</code> interactor inherits from the <code>System.Web.UI.ExtenderControl</code> class. As such, it is composed of two parts: an ASP.NET server control and a JavaScript client control.</p>
<h2>The server control</h2>
<p>The ASP.NET server control is responsible for describing the client resources to the underlying ASP.NET framework (the script resources to deploy, the optional initialization tasks to perform on the client, etc.), and for handling the possible actions coming from the client control. In our case, the server control implementation handles the client action (i.e. create an <code>ILOG.Diagrammer.Graphic.BezierCurve</code> instance and add it to the view content) via an asynchronous callback. Let&#8217;s first begin with the implementation of the client resources description.</p>
<p>For this purpose, the <code>ILOG.Diagrammer.Web.UI.ViewInteractor</code> class defines the following methods:</p>
<pre>
ScriptBehaviorDescriptor CreateScriptBehaviorDescriptor(Control targetControl)
IEnumerable&lt;ScriptReference&gt; GetScriptReferences()</pre>
<p>The <code>CreateScriptBehaviorDescriptor</code> method must be overriden to return a <code>ScriptBehaviorDescriptor</code> instance that specifies the name of the client-side class of the interactor (in other words the name of the JavaScript class).<br />
The <code>GetScriptReferences</code> method returns the script resources needed by your client control. This is needed by ASP.NET to be able to automatically deploy the required client resources.</p>
<p>Here is a first implementation of the <code>MakeBezierCurveInteractor</code> class that illustrates the implementation of these methods :</p>
<pre>
public class MakeCurveInteractor : ViewInteractor {        

  protected override ScriptBehaviorDescriptor CreateScriptBehaviorDescriptor(Control targetControl) {
    return new ScriptBehaviorDescriptor("AjaxBezierSample.MakeCurveBehavior",
                                        targetControl.ClientID);
  }                

  protected override IEnumerable&lt;scriptreference&gt; GetScriptReferences() {
    IEnumerable&lt;ScriptReference&gt; references = base.GetScriptReferences();
    List&lt;ScriptReference&gt; list = new List&lt;ScriptReference&gt;(references);
    list.Add(new ScriptReference("AjaxBezierSample.script.MakeCurveBehavior.js",
                                 Assembly.GetExecutingAssembly().FullName));
    return list;
  }</pre>
<p>which means the client control implementation is the <code>AjaxBezierSample.MakeCurveBehavior</code> class defined in the MakeCurveBehavior.js file, packaged as an embedded resource in the application assembly (packaging a script resource in the assembly requires the .js file be built with the Embedded Resource build action, and the resource declared in the AssemblyInfo.cs).</p>
<p>The next step, and it completes the server control implementation, is to handle the action. As explained above, we made the choice to process the interaction via an asynchronous ASP.NET callback, which is possible because the <code>ViewInteractor</code> base class implements the ASP.NET 2.0 <code>ICallbackEventHandler</code> interface. Therefore, our custom interactor must override the <code>ViewInteractor.RaiseCallBackEvent</code> method that is called when a callback request is received by our control.</p>
<p>The implementation is quite straightforward: it first reads the curve definition points from the method parameter (passed by the client control as a JSON string), converts these points from the view coordinate system to the view graphic container coordinate system, then creates the corresponding Diagrammer for .NET graphic object (a <code>BezierCurve</code> instance) and finally adds it to the view graphic container.</p>
<p>Here is the code:</p>
<pre>
        protected override void RaiseCallbackEvent(string eventArgument) {
            ProcessRequest(eventArgument);
            base.RaiseCallbackEvent(eventArgument);
        }
        protected virtual void ProcessRequest(string eventArgument) {
            if (string.IsNullOrEmpty(eventArgument))
                return;
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(BezierArg));
            BezierArg args = null;
            using (Stream input = new MemoryStream(Encoding.Unicode.GetBytes(eventArgument))) {
                args = serializer.ReadObject(input) as BezierArg;
            }
            if (args != null) {
                Point2D[] points = args.points;
                if (points.Length == 0)
                    return;
                Transform t = View.Transform.Inverse();
                t.TransformPoints(points);
                BezierCurve curve = new BezierCurve(points);
                IManageChildren container = View.Content as IManageChildren;
                if (container != null) {
                    container.AddChildren(new GraphicObject[] { curve });
                }
            }
        }</pre>
<p>The server control implementation is done. It&#8217;s now time to wear our JavaScript developer suit for the client side control implementation. From now on, all class names refer to the Microsoft JavaScript Library.</p>
<h2>The client control</h2>
<p>Implementing the client part of a <code>ViewInteractor</code> consists on writing a JavaScript class inheriting from the <code>ILOG.Diagrammer.Web.UI.ViewBehavior</code> class which offers the basic services to communicate with the server control. In the Microsoft JavaScript world, writing a new class consists on :</p>
<ul>
<li>defining the namespace</li>
<li>defining the class object</li>
<li>implementing the class methods via the class object prototype</li>
<li>registering the class in the class hierarchy</li>
</ul>
<p>In terms of code, it means:</p>
<ul>
<li>defining the name space:</li>
</ul>
<pre><font size="2">Type.registerNamespace(</font><font size="2" color="#a31515">&#8216;AjaxBezierSample&#8217;</font><font size="2">);</font></pre>
<p>This line of code creates a new namespace called &#8216;AjaxBezierSample&#8217; and registers it in the <code>Type</code> object.</p>
<ul>
<li>defining the class object:</li>
</ul>
<pre>AjaxBezierSample.MakeCurveBehavior = <font color="#0000ff">function</font>(element) {
  &#8230;
  AjaxBezierSample.MakeCurveBehavior.initializeBase(<font color="#0000ff">this</font>, [element]);
};</pre>
<p>The namespace object being created, we define the MakeCurveBehavior class object (the constructor) and call the initializeBase() method to initialize the base class. Latter, we will add to the constructor the fields initialization code.</p>
<ul>
<li>implementing the class methods via the class object prototype</li>
</ul>
<pre>
AjaxBezierSample.MakeCurveBehavior.prototype = {
  initialize : <font color="#0000ff">function</font>() {
    AjaxBezierSample.MakeCurveBehavior.callBaseMethod(<font color="#0000ff">this</font>, <font color="#a31515">&#8216;initialize&#8217;</font>);
  },
  dispose : <font color="#0000ff">function</font>() {
    AjaxBezierSample.MakeCurveBehavior.callBaseMethod(<font color="#0000ff">this</font>, <font color="#a31515">&#8216;dispose&#8217;</font>);
  },
  &#8230;
}</pre>
<p>The methods of the class are defined on the prototype following the prototype model. For more information on the prototype model, see the <a target="_blank" href="http://asp.net/AJAX/Documentation/Live/tutorials/CreatingClientComponentPrototype.aspx"></a>title=&#8221;Prototype model&#8221;&gt;MSDN documentation. For the time being, we only need to call the <code>initialize()</code> and <code>dispose()</code> methods of the base class.</p>
<ul>
<li>registering the class in the class hierarchy</li>
</ul>
<pre>
AjaxBezierSample.MakeCurveBehavior.registerClass(<font color="#a31515">&#8216;AjaxBezierSample.MakeCurveBehavior&#8217;</font>, ILOG.Diagrammer.Web.UI.ViewBehavior);</pre>
<p>Finally, we register our new class in the class hierarchy, specifying its fully qualified name (that is the class name prefixed with the namespace) and the base class it inherits from. This skeleton is the minimum code required to fully define a new <code>ViewBehavior</code> subclass.</p>
<p>The last step is to eventually implement the code needed to create bezier curves.</p>
<h2>Drawing Bezier curves in DHTML</h2>
<p>As explained in the first part of this article, the interaction ghost of the bezier curves will be implemented either in VML or SVG depending on the browser. The ghost will be composed of three primitive objects:</p>
<ul>
<li>a circle that represents the edited passage point.</li>
<li>two rectangles that represent the control points.</li>
</ul>
<p>These primitives will be added to a parent container positionned in absolute above the image. To ease the primitives update, both the primitives and the container will have a corresponding class field (a reference to the corresponding node in the DOM).</p>
<p>The code below shows the updated constructor (with the primitive fields) and the DOM initialization :</p>
<pre>
AjaxBezierSample.MakeCurveBehavior = function(element) {
    this._ghostContainer = null;
    this._ptMarker = null;
    this._ctrl1 = null;
    this._ctrl2 = null;
    this._useVML = Sys.Browser.agent == Sys.Browser.InternetExplorer;
    this._curveCount = 0;
    this._svg  = null;
    ...
    AjaxBezierSample.MakeCurveBehavior.initializeBase(this, [element]);
};        

AjaxBezierSample.MakeCurveBehavior.prototype = {        

    initialize : function() {
        AjaxBezierSample.MakeCurveBehavior.callBaseMethod(this, 'initialize');
        ...
        this.initDOM();
    },
    initDOM : function() {
        this._ghostContainer = document.createElement("div");
        this.get_element().appendChild(this._ghostContainer);
        this._ghostContainer.style.zIndex = 3;
        this._ghostContainer.style.position = "absolute";
        this._ghostContainer.style.left="0px";
        this._ghostContainer.style.top="0px";
        this._ghostContainer.style.width="100%";
        this._ghostContainer.style.height="100%";
        if (!this._useVML) {
            this._svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
            this._ghostContainer.appendChild(this._svg);
        }
        var parentNode = this._useVML ? this._ghostContainer : this._svg;
        // passage point marker
        this._ptMarker = this.createMarker(parentNode);
        // control points markers
        this._ctrl1 = this.createControlPt(parentNode, 'ctrl1');
        this._ctrl2 = this.createControlPt(parentNode, 'ctrl2');
    },        

    createMarker : function(parentNode) {
        var marker = this._useVML ? this._createMarkerVML(parentNode) : this._createMarkerSVG(parentNode);
        return marker;
    },        

    _createMarkerVML : function(parentNode) {
        var marker = document.createElement("oval");
        parentNode.appendChild(marker);
        marker.id = "ptmarker";
        marker.style.behavior = "url(#default#VML)";
        marker.strokeweight = 1;
        marker.filled = 'true';
        marker.fillcolor = 'black';
        marker.style.position = "absolute";
        marker.style.width = "7px";
        marker.style.height = "7px";
        marker.style.display='none';
        return marker;
    },        

    _createMarkerSVG : function(parentNode) {
        var marker = document.createElementNS("http://www.w3.org/2000/svg", "circle");
        parentNode.appendChild(marker);
        marker.style.display = 'none';
        marker.setAttributeNS(null, 'id', "ptmarker");
        marker.setAttributeNS(null, 'r', 3);
        marker.setAttributeNS(null, "fill", 'black');
        marker.setAttributeNS(null, "stroke", "none");
        return marker;
    },        

    createControlPt : function(parentNode, id) {
        var ctrl = this._useVML ? this._createControlVML(parentNode, id) : this._createControlSVG(parentNode, id);
        return ctrl;
    },        

    _createControlVML : function(parentNode, id) {
        var ctrl = document.createElement("rect");
        parentNode.appendChild(ctrl);
        ctrl.id = id;
        ctrl.style.behavior = "url(#default#VML)";
        ctrl.strokeweight = 1;
        ctrl.filled = 'false';
        ctrl.style.position = 'absolute';
        ctrl.style.width = '11px';
        ctrl.style.height = '11px';
        ctrl.style.display='none';
        return ctrl;
    },        

    _createControlSVG : function(parentNode, id) {
        var ctrl = document.createElementNS("http://www.w3.org/2000/svg", "rect");
        parentNode.appendChild(ctrl);
        ctrl.id = id;
        ctrl.setAttributeNS(null, "width", "11px");
        ctrl.setAttributeNS(null, "height", "11px");
        ctrl.setAttributeNS(null, "stroke-width", 1);
        ctrl.setAttributeNS(null, "fill", "none");
        ctrl.setAttributeNS(null, "stroke", "black");
        ctrl.style.display='none';
        return ctrl;
    },</pre>
<h2>Handling interactions</h2>
<p>The interaction process follows the following sequences:</p>
<ul>
<li>When the left mouse button is pressed, a new passage point and two control points are created.</li>
<li>When the mouse is dragged, the position of the control points is adjusted accordingly.</li>
<li>On a double-click, the curve is validated and a request is sent to the server with the curve points.</li>
<li>When the ESC key is pressed, the interaction is cancelled.</li>
</ul>
<p>Therefore, the interactor has to listen the mousedown, mousemove, mouseup and keydown events. For this purpose, we define four handlers that will be wired/unwired depending on the value of the <code>active</code> property.</p>
<p>The code below shows the handler initialization and the required methods to wire/unwire the handlers on a <code>active</code> property change:</p>
<pre>
AjaxBezierSample.MakeCurveBehavior = function(element) {        

    this._mouseDownHandler = null;
    this._mouseMoveHandler = null;
    this._mouseUpHandler = null;
    this._keyDownHandler = null;
    this._propertyChangedHandler = null;
    this._startPoint = null;
    this._points = null;
    this._size = 0;
    ...        

    AjaxBezierSample.MakeCurveBehavior.initializeBase(this, [element]);
};
AjaxBezierSample.MakeCurveBehavior.prototype = {        

    initialize : function() {
        /// <summary></summary>
        /// Initializes a MakeCurveBehavior instance.
        ///         

        AjaxBezierSample.MakeCurveBehavior.callBaseMethod(this, &#8216;initialize&#8217;);        

        this._mouseDownHandler = Function.createDelegate(this, this._onMouseDown);
        this._mouseMoveHandler = Function.createDelegate(this, this._onMouseMove);
        this._mouseUpHandler = Function.createDelegate(this, this._onMouseUp);
        this._keyDownHandler = Function.createDelegate(this, this._onKeyDown);
        this._doubleClickHandler = Function.createDelegate(this, this._onDoubleClick);
        if (this.get_active()) {
            this._wireHandlers();
        }
        this._propertyChangedHandler = Function.createDelegate(this, this._onPropertyChanged);
        this.add_propertyChanged(this._propertyChangedHandler);
        &#8230;
    },        

    dispose : function() {
        this._imgElt = null;
        $clearHandlers(this.get_element());
        AjaxBezierSample.MakeCurveBehavior.callBaseMethod(this, &#8216;dispose&#8217;);
    },        

    _wireHandlers : function() {
        $addHandler(this.get_element(), &#8216;mousedown&#8217;, this._mouseDownHandler);
        $addHandler(this.get_element(), &#8216;mousemove&#8217;, this._mouseMoveHandler);
        $addHandler(this.get_element(), &#8216;mouseup&#8217;, this._mouseUpHandler);
        $addHandler(this.get_element(), &#8216;keydown&#8217;, this._keyDownHandler);
        $addHandler(this.get_element(), &#8216;dblclick&#8217;, this._doubleClickHandler);
        this.ensureFirefoxKeyEvents();
    },        

    _unwireHandlers : function() {
        $removeHandler(this.get_element(), &#8216;mousedown&#8217;, this._mouseDownHandler);
        $removeHandler(this.get_element(), &#8216;mousemove&#8217;, this._mouseMoveHandler);
        $removeHandler(this.get_element(), &#8216;mouseup&#8217;, this._mouseUpHandler);
        $removeHandler(this.get_element(), &#8216;keydown&#8217;, this._keyDownHandler);
        $removeHandler(this.get_element(), &#8216;dblclick&#8217;, this._doubleClickHandler);
    },        

    _onPropertyChanged : function(sender, args) {
        if (&#8217;active&#8217; == args.get_propertyName()) {
            if (this.get_active()) {
                this._wireHandlers();
            } else {
                this._unwireHandlers();
            }
        }
    },</pre>
<p>Next, we need to handle the points and create the ghost curve. For this purpose, all the points (the passage points and the control points) are stored in an array in the following sequence : the passage point, the first control point, the second control point. We hence have three indices per point.</p>
<p>The code below illustrates this (for the sake of readability, the method updateCurveGhostPoints() that updates the ghost position is not shown. Please refer to the source code for more details). When the mouse is pressed, the addPoint() method is invoked to add a new passage point and two control points. The three points have initially the same coordinates until the next mouse drag. Then, when the mouse is dragged, the control points position is adjusted proportionally (in the react() method) :</p>
<pre>
    _onMouseDown : function(e) {
        if (e.button == Sys.UI.MouseButton.leftButton) {        

            if (this._points == null) {
                this._points = [];
                this._size = 0;
            }
            this._dragging = true;
            var loc = Sys.UI.DomElement.getLocation(this.get_element());
            var p = {'x':e.clientX - loc.x, 'y':e.clientY - loc.y };
            // Add the new point
            this._addPoint(p);
            // ... and compute the control points
            this._react(p);
            // update ghost
            if (this._size == 0) {
                this.updateCurveGhostPoints();
            } else {
                this._points[this._size-1] = p;
                this.addNewCurve();
            }
            this.updateMarkerPos(p);
            this.onStartInteraction();        

            e.stopPropagation();
            e.preventDefault();
        }
    },        

    _onMouseMove : function(e) {
        if (this._size &gt; 0 &amp;&amp; this._dragging) {
            var loc = Sys.UI.DomElement.getLocation(this.get_element());
            var p = {'x':e.clientX - loc.x, 'y':e.clientY - loc.y };
            if (this._size == 1) {
                // If user is dragging the starting point, add the second point.
                this._addPoint(p);
                // and compute the control points
                this._react(p);
            } else {
                // computes the control points position
                this._react(p);
                this._points[this._size-1] = p;
            }
            // update ghost
            this.updateCurveGhostPoints();        

            e.stopPropagation();
            e.preventDefault();
        }
    },        

    _addPoint : function(p) {
        if (this._size &gt; 1) {
            this._points[(++this._size)-1] = p;
            this._points[(++this._size)-1] = p;
            this._points[(++this._size)-1] = p;
        } else {
            this._points[(++this._size)-1] = p; // start point
        }
    },        

    _react : function(p) {
        if (this._size &gt; 3) {
           // Updates the position of the control point.
           var prevp = this._size - 3; // previous left handle
           var pivot = this._size - 2; // previous end point
           this._points[prevp] = this.symmetric(this._points[pivot], p);
        }
    },        

    _onMouseUp : function(e) {
        this._dragging = false;        

    },        

    _onDoubleClick : function(e) {
        if (this._points != null) {
            // Commit the action.
            this.doCreateCurve();
            this.onEndInteraction();
            this.reset();        

            e.stopPropagation();
            e.preventDefault();
        }
    },        

    _onKeyDown : function(e) {
        if (this._points != null &amp;&amp; e.keyCode == Sys.UI.Key.esc) {
            // Cancel interaction
            this.onEndInteraction();
            this.reset();
            e.stopPropagation();
            e.preventDefault();
        }
    },</pre>
<h2>Commit the new curve to the server</h2>
<p>Finally, the points defining the new curve are sent to the server-side part of our interactor to create the corresponding <code>ILOG.Diagrammer.Graphic.BezierCurve</code> object and add it to the view <code>GraphicContainer</code>. To do this, we create an asynchronous ASP.NET callback invoking the <code>ViewBehavior.callServerAsync</code> method passing the points array as parameter. At this time, the request has been processed but the image has not been updated to show the new content. In order to fix this, we override the ViewBehavior.onCallbackReceived method to refresh the view when the callback response is received :</p>
<pre>
    doCreateCurve : function() {
        var args = {};
        if (this._size &gt; 4) {
            // get rid of the extra passage point coming from the double-click.
            if (this._useVML)
                args.points = this._points.slice(0, this._points.length-1-3);
            else // under FF, we get one more mouse-down from the double-click
                args.points = this._points.slice(0, this._points.length-1-2*3);
        } else {
            args.points = this._points.slice(0);
        }
        this.callServerAsync(args);
    },        

    onCallbackReceived : function(args) {
        this.updateView();
    },</pre>
<p>You can find the VS2008 project here: <a href="http://blogs.ilog.com/netvisu/wp-content/uploads/2008/06/ajaxbeziersample.zip" title="AjaxBezierSample VS2008 Project">AjaxBezierSample VS2008 Project</a>. Do not hesitate to look at the MakeCurveBehavior.js file to see how the the primitives points are updated.</p>
<p>The purpose of this implementation being a tutorial, many improvements are still to be done. For example, you could add properties to be able to customize the ghost rendering (the stroke color, the stroke style, etc.), an optional PostBack behavior (see ViewBehavior.doPostBack() method), additional editing capabilities (add/supress points or closed curves support).</p>
<p>As you can see, writing a new interactor for an AjaxDiagramView is essentially a client-side task. Fortunately, the Microsoft JavaScript Library and the ILOG.Diagrammer.Web.UI.ViewBehavior class provide a rich set of functionalities and services that make the JavaScript implementation easier and poweful. Also, the use of asynchronous callbacks to handle the interaction allows a very smooth user experience avoiding the annoying cost of a PostBack.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.ilog.com/netvisu/2008/06/20/create-bezier-curve-in-an-ajaxdiagramview-part-2/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
