Damon Payne: Hand waving software architect

103db signal to noise ratio at < .03% total harmonic distortion
Solution Architect, software developer, geek
Damon Payne at Blogged
2009 Microsoft MVP - Client App Dev
2007 Microsoft MVP - Solution Architecture
 Sunday, October 19, 2008
« Argentum Tangent - adding another projec... | Main | Designing Code Demos »

The AGT (Argentum Tela) series of articles is an effort to do two things.  Usually an idea is presented only in its finished form.  The first goal is to do some Reality Blogging, to show an idea evolve over time without pulling any punches.  The second goal, and the example vehicle for the evolution aspect, is an extensible Design Surface for Silverlight similar to what we have in Visual Studio 2008.   This type of application has all sorts of interesting uses.  My example is a Home Theater layout tool.  Read the entire saga: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Moving the Selection with the mouse

Now that select and multi-select are working the next logical step is to allow the user to move things around on the design surface, and I have an idea of what I’d like to support. 

·         A single selected component is straightforward: drag and move.

·         If there are multiple components selected, clicking on any one of them should move the entire selection without changing relative space.

The first item is probably best accomplished by handling mouse events and mouse capture on the invisible “Glass” component of the DesignSite.

Refactor: Some of the mouse event handlers in DesignSite were still named Visual_MouseXXX and should be called Glass_MouseXXX now.

Refactor: Right about this time, Silverlight Release To Web came out.  So far, I’ve not had to change anything from my RC0 code.

We need to decide where to wire up the mouse events associated with moving – in the DesignSurface or in the DesignSite.   Since we are already using the ISelectionService within DesignSite, I’ll try there first.  Remember that the “Glass” control in the DesignSite is the 99% transparent overlay on top of the hosted content.  We need to capture the mouse from the Glass component:

        void Glass_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            _selSvc.Select(new List<IDesignableControl> { this });

            _isMoving = true;

            _localMovePoint = e.GetPosition(Glass);

            _surfaceMousePoint = e.GetPosition(DesignParent);

            e.Handled = true;

            Glass.CaptureMouse();

        }

First I test to make sure I can move a single selected component around:

        private void Glass_MouseMove(object sender, MouseEventArgs e)

        {

            if (_isMoving)

            {

                Point surfacePoint = e.GetPosition(DesignParent);

                //Since we can't grab from outside ourself:

                double left = surfacePoint.X - _localMovePoint.X;

                double top = surfacePoint.Y - _localMovePoint.Y;

               

                SetValue(Canvas.LeftProperty, left);

                SetValue(Canvas.TopProperty, top);

            }

        }

This works exactly as I’d expect it to.  In order to move multiple selected components at once, I’m expecting that I’ll have to calculate a relative change from the previous mouse location and iterate across all IDCs in the selection, updating their positions.

My selection-code-under-Glass was clearing the multi-selection and replacing it with a single selection, so when I update the Glass_MouseLeftButtonDown method, everything works.

        void Glass_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            //if this is already selected, we might want to move.

            if ( (_selSvc.SelectionCount > 0 && _selSvc.GetSelection().Contains(this)))

            {

                StartMove(e);

            }

            else if (!_selSvc.GetSelection().Contains(this))

            {

                _selSvc.Select(new List<IDesignableControl> { this });

                StartMove(e);

            }

            e.Handled = true;

        }

 

        private void StartMove(MouseButtonEventArgs e)

        {

            _isMoving = true;

            _localMovePoint = e.GetPosition(Glass);

            _surfaceMousePoint = e.GetPosition(DesignParent);

            Glass.CaptureMouse();

        }

Refactor: I found the app behaved more intuitively when I set it so that clicking on the surface itself would clear the current selection.  This is done by passing null to ISelectionService.Select().

I can now select and move things around on the surface.

In the next article I’m going to finally start working on replacing that section on the right that has said “I am a property grid” for 15 articles so far.  The live demo has been updated, which means you’ll need Silverlight RTW to run it.

Source code: DamonPayne.AGT[14].zip (805.28 KB)