Silverlight: Convert Ink to Xaml


I have to say that I absolutely love the path mini language that is part of XAML. If you haven't heard about it, there are more details here: http://msdn2.microsoft.com/en-us/library/ms752293.aspx

So, while playing with ink in Silverlight, I realized that there was no way that I could see where you could interact with an application using ink other than drawing on the surface. If you ever played (the highly addictive) inkball game that comes with Vista, you'll know what I mean.

So, I wondered to myself if it was possible to use the path mini language to generate a XAML representation of what I ink on the screen. Not only was it possible. It was easy.

So, first, let's see how to get started with ink in Silverlight. You get up an running with the InkPresenter object. Here's an example of a very simple XAML document that uses it.

<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="Black">
    <InkPresenter
        x:Name="inkEl"
        Background="transparent"
        Width="500" Height="500"
        MouseLeftButtonDown="inkMouseDown"
        MouseMove="inkMouseMove"
        MouseLeftButtonUp="inkMouseUp"/>

</Canvas>

Now, within JavaScript you want to capture the MouseDown, MouseMove and MouseUp events that you defined on the Ink Presenter. Here's the full HTML for the page, including the JavaScript.

 

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SilverlightJSApplication2</title>

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="Default.html.js"></script>
<script type="text/javascript" src="Scene.xaml.js"></script>
<script type="text/javascript">
    var theInk; // Reference to the ink presenter
    var newStroke; // Reference to a stroke
    var theControl; // Reference to the Silverlight control
    var theRoot;
    function handleLoad(control, userContext, rootElement)
    {
    // The Load event returns a reference to the control
    // But other event handlers do not. So we're going
    // to make a reference to the control here
        theControl = control;
        theRoot = rootElement;
    // Here we will create a reference to the ink element
        theInk = control.content.findName("inkEl");
    }

    function inkMouseDown(sender,args)
    {
    // Capture the Mouse
        theInk.CaptureMouse();
    // Create a new stroke
        newStroke = theControl.content.createFromXaml('<Stroke/>');
    // Assign a new drawing attributes element to the stroke
    // This, as its name suggests, defines how the stroke will appear
        var da = theControl.content.CreateFromXaml('<DrawingAttributes/>');
        newStroke.DrawingAttributes = da;
    // Now that the stroke has drawing attributes
    // Let's define them...
        newStroke.DrawingAttributes.Width = 1;
        newStroke.DrawingAttributes.Height = 1;
        newStroke.DrawingAttributes.Color = "Black"
        newStroke.DrawingAttributes.OutlineColor = "Black"
        newStroke.StylusPoints.AddStylusPoints(args.GetStylusPoints(theInk));
        theInk.Strokes.Add(newStroke);
    }

    // Add the new points to the Stroke we're working with
    function inkMouseMove(sender,args)
    {
        if (newStroke != null)
        {
            newStroke.StylusPoints.AddStylusPoints(args.GetStylusPoints(theInk));
        }
    }

    // Release the mouse
    function inkMouseUp(sender,args)
    {
        var pathString = ""
        var pathElement = "<Path Stroke='Black' Data='"
        var bDoPath = window.confirm("Convert to Path?");
        if(bDoPath)
        {
            var n = newStroke.StylusPoints.Count;
            for(i=0;i<n;i++)
            {
                if(i==0)
                {
                    pathString +="M "
                }
                else
                {
                    pathString +="L "
                }
                pathString+= + newStroke.StylusPoints.GetItem(i).X + "," +            

                                newStroke.StylusPoints.GetItem(i).Y + " "
            }
            pathElement+=pathString+"' />"
            thePath = theControl.content.createFromXaml(pathElement);
            theRoot.children.add(thePath);
            theInk.Strokes.Remove(newStroke);
        }
        newStroke = null;
        theInk.ReleaseMouseCapture();
    }
</script>
</head>

<body>
<div id="SilverlightControlHost">
    <script type="text/javascript">
        createSilverlight();
    </script>
</div>
</body>
</html>

 

The workhorse of the conversion is in the inkMouseUp function. Here, the stroke has been completed, and a Silverlight ink stroke has a collection of StylusPoints. You can get the contents of a specific StylusPoint in this collection using GetItem(). The Path Mini language uses M for 'move' followed by X,Y co-ordinates, and L for 'line' followed by X,Y co-ordinates.

So M0,0 L100,100 L200,200, will move the pen to 0,0, draw a line to 100,100 and draw another to 200,200. The StylusPoints have X and Y co-ordinates, so we can build up a string like this easily.

The format of a <Path> node is <Path Stroke='Color' Data='Expression in Path mini Language' />.

So, we just build a string like this, sticking the path language string we generated from the strokes, and then use createFromXaml to stick this into the Xaml DOM. We then remove the strokes collection generated by the pen, and we have XAML in the DOM being rendered instead.

Try it!

 If your browser supports IFrame, you can see it here too (Scribble in the space with mouse, pen or whatever):

Comments (3)
  1. Cream , as in &#39;Pick of the Litter&#39; is always subjective, but this is my list for today as I view

  2. hughpyle says:

    Sorry for the extended absence. I’ve been heads down planning for Office "14" Groove, and quite removed

Comments are closed.

Skip to main content