locked
InkCanvas (WPF) and StylusPlugIns RRS feed

  • Question

  • I have created my own StylusPlugIn that I am using in conjunction with the System.Input.Controls.InkCanvas. I have placed my plug in before the DynamicRenderer in the PlugIns collection.

    When I create strokes with my pen (Tera M4) I get the expected behavior where my plugin is applied to both dynamically rendered Ink and created strokes.  When I create a stroke using the mouse my plugin is applied to the dynamic rendering but not to the created strokes.

    In addition, it seems that even with the pen the first (one or few) StylusPoints or the first stroke are not passed trhough the plugin, so I end up with a funny looking end

    Thursday, February 2, 2006 11:28 PM

Answers

  • Hi Nathan.

    I assume you're doing filtering with your StylusPlugin by calling SetStylusPoints on the RawStylusInput argument to alter the StylusPointCollection being routed through the input stream.

    While calling SetStylusPoints has the effect of altering the Stylus input stream, when we promote to Mouse events, we use the original input stream.  This is done to prevent spoofing Mouse input.

    While InkCanvas uses the DynamicRenderer plugin to render Strokes in real time, it constructs the final Stroke based on input from the input system.  Thus, if a Stylus is being used, the InkCanvas will use the altered Stylus input stream to construct the Stroke, but if a Mouse is being used, the original (and unaltered) input stream will be used.

    As a workaround for plugin developers who need to do filtering that works with the Stylus and Mouse, we suggest having the plugin raise an event that your subclassed InkCanvas listens to.  The event could contain the altered StylusPointCollection and then you could use that to construct a Stroke and add it to the InkCanvas.

    Given a StylusPointCollection, this is how we construct the Stroke before adding it to the InkCanvas

    Stroke stroke = new Stroke(stylusPoints, InkCanvas.DefaultDrawingAttributes.Clone());

    We know that's an unfortunate workaround, but after a security review, we became convinced that we should not be allowing the Mouse input stream to be altered.  There are many bad things that can happen when you do.

    As far as the second part of your question; we'll look into if we're dropping StylusPoints to the plugin.  This could have been fixed recently, but we'll verify.  We'll make sure that by the time WPF ships this doesn't happen.

    Thanks for your interest in our platform.  Let me know if there is anything else I can help you with.

    -Sam George

    Tablet Avalon dev lead.

     

    Friday, February 3, 2006 6:12 PM

All replies

  • Hi Nathan.

    I assume you're doing filtering with your StylusPlugin by calling SetStylusPoints on the RawStylusInput argument to alter the StylusPointCollection being routed through the input stream.

    While calling SetStylusPoints has the effect of altering the Stylus input stream, when we promote to Mouse events, we use the original input stream.  This is done to prevent spoofing Mouse input.

    While InkCanvas uses the DynamicRenderer plugin to render Strokes in real time, it constructs the final Stroke based on input from the input system.  Thus, if a Stylus is being used, the InkCanvas will use the altered Stylus input stream to construct the Stroke, but if a Mouse is being used, the original (and unaltered) input stream will be used.

    As a workaround for plugin developers who need to do filtering that works with the Stylus and Mouse, we suggest having the plugin raise an event that your subclassed InkCanvas listens to.  The event could contain the altered StylusPointCollection and then you could use that to construct a Stroke and add it to the InkCanvas.

    Given a StylusPointCollection, this is how we construct the Stroke before adding it to the InkCanvas

    Stroke stroke = new Stroke(stylusPoints, InkCanvas.DefaultDrawingAttributes.Clone());

    We know that's an unfortunate workaround, but after a security review, we became convinced that we should not be allowing the Mouse input stream to be altered.  There are many bad things that can happen when you do.

    As far as the second part of your question; we'll look into if we're dropping StylusPoints to the plugin.  This could have been fixed recently, but we'll verify.  We'll make sure that by the time WPF ships this doesn't happen.

    Thanks for your interest in our platform.  Let me know if there is anything else I can help you with.

    -Sam George

    Tablet Avalon dev lead.

     

    Friday, February 3, 2006 6:12 PM
  • Thank you very much Sam.

    I ended up overrideing the OnStrokeCollected and using an ugly little hack to determine if the stroke was from a pen or a mouse.  If it came from a mouse I made my alterations to the stylus points right there.

    I couldn't figgure out how to disable Ink from the mouse, but it will be good enough for demo code.

    Thanks again!

    Nathan Bales

    Wednesday, February 8, 2006 2:25 AM
  • Hi Nathan,

    I have included some code below that shows how to create an InkCanvas with mouse-inking enabled.

    The code should work with Dec/Jan CTP builds. With the upcoming Feb CTP, this scenario will become a bit easier.

    Thanks,

    Stefan Wick

     

    using System;
    using System.Windows;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Input.StylusPlugIns;
    using System.Windows.Controls;
    using System.Windows.Media;

    class MyDynamicRenderer : DynamicRenderer
    {
        private int _stylusId = -1;
        private bool _isStylus = false;

        public bool IsStylus
        {
            get { return _isStylus; }
        }

        protected override void OnStylusDown(RawStylusInput rawStylusInput)
        {
            base.OnStylusDown(rawStylusInput);
            if (_stylusId == -1)
            {
                _stylusId = rawStylusInput.StylusDeviceId;
            }
        }

        protected override void OnStylusUp(RawStylusInput rawStylusInput)
        {
            base.OnStylusUp(rawStylusInput);
            _isStylus = (_stylusId != 0);
            _stylusId = -1;
        }

        protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
        {
            if (_stylusId != 0)
            {
                base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
            }
        }
    }

    class MyInkCanvas : InkCanvas
    {
        MyDynamicRenderer dr;
        public MyInkCanvas()
        {
            dr = new MyDynamicRenderer();
            this.DynamicRenderer = dr;
        }

        protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
        {
            base.OnStrokeCollected(e);
            if (!dr.IsStylus)
            {
                this.Strokes.Remove(e.Stroke);
            }
        }
    }

    class Program : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            Window win = new Window();
            win.Content = new MyInkCanvas();
            win.Show();
        }

        [System.STAThread]
        static void Main() { new Program().Run(); }
    }

    Wednesday, February 8, 2006 8:59 PM