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
2007 Microsoft MVP - Solution Architecture
 Tuesday, October 21, 2008

When one is giving a technical presentation, issues around source code can be some of the more difficult to plan for.  Do you have the code done already?  Do you write the code on the spot?  If you write code on the spot do you have the code divided into various snippets you can instantly drop in, or do you really type everything?  Is it OK to use a tool like CodeRush or ReSharper that not everyone will be familiar with?  Do you need to be prepared to offer code in both C# and VB.Net?

There has been a "current" or "meme" going around the local community here regarding how the code used in demonstrations is structured.  Some people, it seems, are tired of seeing Database Driven examples or One-Tier samples for .NET topics.  Most commonly picked on is LINQ-to-X: I have heard multiple complaints that it is bad form to bust out "var query = from x in context select x;" from an ASP.Net code behind to show how LINQ works.  In a real application one would have data access classes, Presenters or Controllers, possibly extra business logic classes, and so forth.  The argument goes on that giving a talk and failing to use appropriate separation of concerns like this teaches bad habits to developers and makes the members of the .NET community look like we all implement the Simpleton Pattern.

David Palfery vented to me that after his recent User Group talk on some Silverlight concepts someone approached his boss and gave a reproach for not using more a appropriate separation of concerns in his code demos.  What's the deal with that?  If you can't raise your hand to ask about this, talk to the presenter after the show, or comment on their blog - you are a coward.  Whatever your lot in life, it seems to me to be the most basic of courtesies to extend to someone who's taken their time to hopefully help you grok something.  But, leaving etiquette aside, was this person more or less correct?

The answer is - it depends. 

Was this a talk about Design Patterns or Silverlight Concepts?  What are the chances that an argument over Model-View-Presenter vs. Model-View-ViewModel would erupt?  I'm of the opinion that there are some basic questions that should be asked by an audience member if these things are a concern. 

Q: Excuse me, could you say a few words about what shortcuts you might be taking when demonstrating this concept? 

That's not hard, and assuming the presenter is knowledgeable and experienced they should be able to quickly describe how the quick sample app they are showing you differs from the real-world applications they have probably written before giving a talk in front of an audience.  If a quick description isn't enough to satisfy an audience member, you can put it on the back burner for after the talk.  If my talk is on the ins and outs of Silverlight Data Binding I might rather not spend time explaining how my data access layer, my presenters, my Commands, and my View Models work.  I have to make an assumption here.  If I'm going to use good patterns in my demo code, I'm going to have to assume that most or all of the audience members have at least passing familiarity with said patterns.  If I'm wrong, I'm going to have to set aside part of my presentation to explain the patterns.  This might be very enriching indeed, but myself and my audience may not be as satisfied as if we'd more thoroughly covered the intended topic.  If I'm right and the audience does have a certain level of pattern knowledge, they'll be thinking themselves about what the appropriate abstractions might be.

There are definitely cases though, beyond just knowing your audience, where it is absolutely important to utilize some patterns and practice separation of concerns within your demo code. 

  1. If the nature of the design affects your overall message
  2. If demonstration of a proper design would help neophytes fall into the Pit of Success easier and not detract from time spent on the Core Concepts of your talk
  3. If the quick-and-dirty demonstration method will not work for common situations

The last one probably needs some explanation, and I'm going to pick on Entity Framework here.  I generally like the Entity Framework, but every single early example I saw was creating an easy to follow breadcrumb trail to the Pit of Failure.  Consider a fairly common ASP.Net web forms scenario:

  1. Get some data objects on page load
  2. Bind objects to web controls
  3. Objects are likely to be updated, throw them in the session
  4. On some event, take data out of the web form controls, merge with object data in session, push back down to the data access layer

There were tons of quick and dirty LINQ to Entities examples with the LINQ query against an EntityContext in some Page_Load event, code behind, or other inappropriate place.  These set you up for disappointment.  If you try to push these objects back down to the database (after a couple of postbacks) using the Entity Framework you're going to get an exception saying that these objects are already a tied to some other EntityContext which is now lost.  If you destroy the EntityContext to get around this, you're going to find that you lost change tracking for your entities and wonder why your updates are updating Zero objects every time.  If you Detach() your objects from the EntityContext, thinking you'll re-attach later, you may find that your Entity relationships have disappeared.  You probably don't want to keep an EntityContext open for the lifetime of a Use Case for all sorts of reasons that should be obvious.

As it turns out, hiding the Entity Framework behind a traditional Data Access Layer and using typical DAO semantics requires some clever Extension methods, a few extra lines of code on your part, resigning yourself to updating more fields than actually changed, and some consideration of how this might cause the whole Entity Framework abstraction to leak out into other parts of your application.  It's still worth it, but I wish I'd seen some more real Entity Framework demonstrations while the excitement was building.

It's my opinion that the level of Architecture you need in demo applications is determined by knowing your audience and knowing how naturally the quick and dirty vanilla demo code can be translated into the design of a production ready application.  Those who attend and present at User Group talks should be ready to ask and answer these questions.



Tuesday, October 21, 2008 11:59:37 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, October 19, 2008

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)



Sunday, October 19, 2008 2:50:48 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Back in the first Code Refactoring article in the AGT series I mentioned that I'd like to keep in mind flexibility for using aspects of the framework that were not specific to the designer issue on other projects.  I sat down to do some work on another Silverlight project tonight, and found that I needed to do make some changes to the DamonPayne.AG.IoC framework.  I didn't have to change much, and indeed many people would have foreseen these things before now.  I am a big believer in Just in Time Design - plan for just as much as you know you need, or at least strongly suspect you'll need, and let other patterns emerge through use.  This only works if you're doing enough Use to actually facilitate some meaningful Learning.  Branching out to the second client project, one that does something utterly different than AGT, is a good step forward.

Refactor: I immediately found that I needed a way to Remove an IView that had served its purpose, and that this method was missing from the IRegionManager interface, so I added this.

Refactor: I found that there were some cases where I wanted an AggregateEvent to signify that something had happened, but there was no event payload type necessary.  I changed EventAggregator to allow this and created AggregateEvent, differentiated from AggregateEvent<T>.  While doing this, I found another instance where I was wasting time with ConstructorInfo instead of just using Activator.

In the end, that's not much refactoring for a first attempt at using a framework for another project.  I'm getting ready to possibly use this for more Silverlight projects at CarSpot which should further increase the quality and flexibility of the codebase.



Sunday, October 19, 2008 9:19:42 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 15, 2008

We interrupt normally scheduled article writing to bring you a public service announcement concerning the PS3/Xbox360/PC game called Dead Space, widely available today.  I pre ordered this game thinking I'd take a chance; survival horror Movies are my thing, games not so much.  The early impressions started rolling in and they were so overwhelmingly positive I started to get excited.  Two-channel music and gaming in my home theater are excellent sources of stress relief, but Cod4 has started to get old.  I've been eagerly awating Dead Space and the Ars Technica Review did not encourage patience.  These days I do my gaming in a very large dedicated room on a 106" screen with good surround sound.  Let me just say that barely 2 hours into this game, it is executed so well it's unbelievable.  The use of immersive sound scares you, you'll ooh and ahhh at the attention to detail, and then you will witness something horrific that requires a a forceful and horrific response on your part.  This game has flow down to a science, and has other reviewers have noted, they took an innovated approach to the necesary evils of inventory and weapon switching and such: it's all integrated into the game.  You never leave the action.  Enemies will ambush you while you are reading the various logs that you find.  This game is awesome.  If you like horror, do not have a weak stomach, and can play games in the dark, you must buy it.

 



Wednesday, October 15, 2008 10:12:24 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, October 14, 2008

Silverlight 2 is a real, shipping product today.

http://www.hanselman.com/blog/Silverlight2IsOut.aspx

Later this week, I'll be converting my development environment and projects (including AGT) over to the Real Deal, and hopefully releasing some stuff at CarSpot too.



Tuesday, October 14, 2008 8:13:23 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, October 13, 2008

I will not be there due to family being in town, but I encourage you to check out David Palfery's presentation in the Milwaukee area tomorrow night: http://www.wi-ineta.org/.  If you'd like a preview of what he's presenting, check out his blog at http://palfery.spaces.live.com/ .

There's also a Silverlight SIG branch of our User Group now, and I sadly missed the first meeting.  Perhaps in the future Milwaukee Silverlight coders would like to see a presentation on a Silverlight IoC framework and its application to a Visio-type drawing tool?



Monday, October 13, 2008 1:22:26 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Hey, I got mentioned on the world famous Thirsty Developer podcast.



Monday, October 13, 2008 8:44:32 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

No, I'm not going to the PDC, much to my dismay, so I have to live on the crumbs we'll get from blogs. 

PFX will be a part of the .NET framework 4.0.  This is good news!  I have to wonder, though, if this will make it to Silverlight and the Compact Framework?  I am working on some Silverlight performance benchmark stuff and was contemplating porting some of my own work just to see how much effort it would be.



Monday, October 13, 2008 8:32:04 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, October 09, 2008

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

Rectangular Lasso

I find it incredibly useful to be able to draw a temporary rectangle on a design surface in order to select multiple Controls at once for moving or property editing.  I’d like to support this feature in AGT as well; I call it the rectangular lasso.  I had a very good notion of how I was going to do this: use a method somewhat similar to how I calculated render size in my resizing code, but for drawing a Rectangle object on my design surface.  I’m going to introduce a new convention for these articles: a quick heading to summarize any time that I find I needed to refactor my design to meet current needs or refine thinking.  Since refactoring is good, these will be green and easy to see.

Refactor: I made a static class called ControlExtensions and included a Generic Method to be more convenient with returning Dependency Property values.  When I tried to use this with the Rectangle code below I realized (duh) that this should not be for Control, but DependencyObject.  A DependencyObjectExtensions class has been introduced.

The stage is set when I click on the design surface Canvas. 

        private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            _isSelecting = true;

            _selectingRect = new Rectangle();

            _selectingRect.StrokeThickness = SELECT_RECT_STROKE_WIDTH;

            _selectingRect.RadiusX = SELECT_RECT_RADIUS_X;

            _selectingRect.RadiusY = SELECT_REDT_RADIUS_Y;

            _selectingRect.Stroke = new SolidColorBrush(Colors.Red);

            _selectingRect.Fill = new SolidColorBrush(Colors.Magenta);

            Point clickLoc = e.GetPosition(LayoutRoot);

            _selectingRect.Opacity = .65;

            _selectingRect.SetValue(Canvas.ZIndexProperty, 100);

            _selectingRect.SetValue(Canvas.LeftProperty, clickLoc.X);

            _selectingRect.SetValue(Canvas.TopProperty, clickLoc.Y);

            _selectAnchor = clickLoc;

            LayoutRoot.Children.Add(_selectingRect);

            LayoutRoot.CaptureMouse();

        }

This sets up the Rectangle for resizing.  It will be on top of all other controls and transparent so we can see the controls beneath it.

Note: since some Silverlight components cannot be extended, one is likely to be working with a lot of UserControls.   I like the convention of keeping the top level content of each UserControl named LayoutRoot regardless of its type, it’s a good name.

We next need to handle the mouse move event as long as the left button is pressed.  I have noticed that various programs with a design surface seem to allow resizing only to the east and south, but selecting in either direction.  Since this seems to be a convention, I’ll follow it. 

        private void LayoutRoot_MouseMove(object sender, MouseEventArgs e)

        {

            _localMousePos = e.GetPosition(this);

            if (_isSelecting)//southeast drag

            {

                double rectLeft = _selectingRect.GetValue<double>(Canvas.LeftProperty);

                double rectTop = _selectingRect.GetValue<double>(Canvas.TopProperty);

                if (rectLeft < _localMousePos.X)

                {

                    double width = _localMousePos.X - rectLeft;

                    double height = _localMousePos.Y - rectTop;

                    _selectingRect.Width = width;

                    _selectingRect.Height = height;

                }

                else // northwest drag

                {

                    double width = _selectAnchor.X - _localMousePos.X;

                    double height = _selectAnchor.Y - _localMousePos.Y;

                    if (width > 0 && height > 0)//need this safety here in case a resize rectangle "crosses" itself.

                    {

                        _selectingRect.Width = width;

                        _selectingRect.Height = height;

                        _selectingRect.SetValue(Canvas.LeftProperty, _localMousePos.X);

                        _selectingRect.SetValue(Canvas.TopProperty, _localMousePos.Y);

                    }

                }

            }

        }

Refactor: When testing the drawing of the selection Rectangle, I found that when resizing controls on the surface I would also have a tag-along selection rectangle.  The DesignSite resizing Border needed to mark the mouse click event has Handled=true in order to prevent this.

Now when the user releases the left mouse button, we destroy the selection Rectangle:

        private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

        {

            _isSelecting = false;

            LayoutRoot.ReleaseMouseCapture();

            LayoutRoot.Children.Remove(_selectingRect);

            _selectingRect = null;

        }

This now allows me to lasso in several controls at once like so:

The last aspect of this feature is to hook into the ISelectionService to select any components that are within the Rectangle bounds as I resize the Rectangle.  We need to create a SelectLassoComponents method and call it from within the MouseMove event.  As soon as this is implemented, I find some new strange behavior.  FindElementsInHostCoordinates does not work like I’d hoped.  It seems to need a much bigger Rectangle than it appears to visually to call a “hit” for a control:

       

So, I add logging to see the size of the rectangle I’m drawing in my message window.  I see nothing but extremely odd behavior.  The code for selecting the lassoed controls uses the VisaulTreeHelper class as follows:

        protected virtual void SelectLassoComponents(Rectangle lasso)

        {           

            double left = lasso.GetValue<double>(Canvas.LeftProperty);

            double top = lasso.GetValue<double>(Canvas.TopProperty);

            double width = lasso.Width;

            double height = lasso.Height;

            Rect r = new Rect(left,top,width,height);           

            IEnumerable<UIElement> hits =

                VisualTreeHelper.FindElementsInHostCoordinates(r, LayoutRoot);

VisualTreeHelper is not yet documented on MSDN, but my logging tells me that the Rect is being drawn with the correct bounds.  I had to write my own simple hit test function which will register a hit if any of the four corners of a control falls within the lasso.  This required a FrameworkElementExtensions class to contain a corner method:

        public static Point[] GetCanvasCorners(this FrameworkElement u)

        {

            if (!(u.Parent is Canvas))

            {

                throw new ArgumentException("This only works if FrameworkElement is on a Canas!");

            }

 

            Point[] corners = new Point[4];

 

            double left = u.GetValue<double>(Canvas.LeftProperty);

            double top = u.GetValue<double>(Canvas.TopProperty);

            double width = u.Width;

            double height = u.Height;

            if (double.IsNaN(width) || double.IsNaN(height))

            {

                width = u.RenderSize.Width;

                height = u.RenderSize.Height;

            }

 

            corners[0] = new Point(left, top);

            corners[1] = new Point(left + width, top);

            corners[2] = new Point(left, top + height);

            corners[3] = new Point(left + width, top + height);

 

            return corners;

        }

Following this, we can use a more basic hit testing algorithm like what is shown here, within the SelectLassoComponents implementation.

        protected virtual void SelectLassoComponents(Rectangle lasso)//TODO: make hit test strategy pluggable

        {           

            double left = lasso.GetValue<double>(Canvas.LeftProperty);

            double top = lasso.GetValue<double>(Canvas.TopProperty);

            double width = lasso.Width;

            double height = lasso.Height;

            Rect r = new Rect(left,top,width,height);           

            List<IDesignableControl> selection = new List<IDesignableControl>();

 

            foreach (var u in LayoutRoot.Children)

            {

                if (u is IDesignableControl)

                {

                    IDesignableControl test = (IDesignableControl)u;

                    Point[] corners = test.Visual.GetCanvasCorners();

                    for (int i = 0; i < corners.Length; ++i)

                    {

                        if (r.Contains(corners[i]))

                        {

                            selection.Add(test);

                            break;

                 &