Roger Ebert and the age of credulity

by Administrator 24. September 2008 05:06

http://blogs.suntimes.com/ebert/2008/09/this_is_the_dawning_of_the_age.html

Art imitates life, and life in turn imitates art (which is a part of life)

I have often remarked to friends that the enduring success of Roger Ebert is hard to understand, though I am happy to see it.  After all, this is the 2000's we are living in.  He is, forgive me, not a particularly attractive man, nor does he speak and write at a level accessible to to the average high school graduate.  This is after all the age in which we vote for politicians based not on their qualifications to make decisions that are "far above our pay grade" but rather on whether or not we would feel comfortable having them over for BBQ chicken without having a chance to clean the house first or if they would fit in at our local church.  What I mean to say is that Roger Ebert does not have qualities which would immediately invoke the words "mass appeal".  He is, rather, an elitist - a word which should be badge of honor and not the insult is has become in our praise of mediocrity in this nation.

Roger Ebert writes thoughtful insights about Film, which I consider to be our highest art form to date.  Why?  Because it combines music, philosophy, dancing, drama, words, and technology into a single medium.  Each of these expressions can be profound in themselves, but combined into a single human experience moving through time can approach something truly sublime.   Surely all these things are excellent achievements of Humans and the combinatorial effects of overlapping them are worthwile of educated commentary.   The success of Roger Ebert, then, I attribute to there being just enough intelligent people in America who care just enough about things that matter to humans to give this man an enduring audience.

Ebert tells us that these days there are few rewards for critical thinking, and I must sadly agree to a point.  It would seem to me that his true talents lie not in interpreting Art, but in observing and reporting on human behavior.  Keep up the good work, sir.

Tags:

Run time is design time for AGT [10]

by Administrator 23. September 2008 03:28

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

Selection

Now that we’ve got things on the surface, we need to think about selecting, moving, editing, and resizing them.  It makes sense for Selection to come first.

Selection is slightly more complicated than it sounds.  In a tool like VS2008 or Visio things could be added or removed from the selection in several ways:

·         Clicking on a component

·         Control+Click on multiple components to select and deselect

·         Shift+Click to add contiguous components to the selection

·         Click and draw a rectangle, selecting components within the bounds of said rectangle.

Since I’m extending the olive branch to the dozens and dozens of unfortunate Mac users in the world, I’ll have to make any gesture code I write smart enough to not use any normal keys that are missing from the Mac, such as: Control, Window Key, Right-click Key, Pause/Break, up arrow, and the letter Q.

Back in the Dark Ages when I was writing code to host the WinForms 2 design surface, I think my selection service implementation was quite a few lines of code.  While I still like the idea of a selection service, I’m not going to go look at the old code and corrupt my pure thoughts.  First, let’s create a cross platform Keyboard service:

    public class CrossPlatformKeyboardService : IKeyboardService

    {

        /// <summary>

        /// Control key, or open-apple on the Mac "platform"

        /// </summary>

        public bool ControlKeyDown

        {

            get

            {

                return ( Test(ModifierKeys.Control) || Test(ModifierKeys.Apple));

            }

        }

 

        public bool AltKeyDown

        {

            get { return Test(ModifierKeys.Alt); }

        }

 

        public bool ShiftDown

        {

            get { return Test(ModifierKeys.Shift); }

        }

 

        protected bool Test(ModifierKeys target)

        {

            return target == (target & Keyboard.Modifiers);

        }

Next I’ll define what I expect the interface for a selection service to be.

    public interface ISelectionService : IService

    {

        void SetSelectedControls(IList<IDesignableControl> selection);

        IList<IDesignableControl> GetSelection();

        int SelectionCount { get; }

        IDesignableControl PrimarySelection { get; }

        event EventHandler SelectionChanged;

    }

In WinForms, adding to the ISelectionService can optionally come with a parameter of type “SelectionType”, which is a discriminator for things like adding to a selection vs. starting a new selection.  I’d rather leave those details behind the ISelectionService wherever possible.  You will also note that I am using a plain old .Net event here and not something from EventAggregator.  I feel this is highly appropriate in this case.  The EventAggregator exists so that components can respond to interesting events without referencing each other, but selection events are specific to this Service, and components that need ISelectionService will magically receive it from the container.   Now I can start working on a default ISelectionService implementation, with a dependency on IKeyboardService.

Siteing, not Sighting

We need to take another step before Selection can make sense.  The WinForms designer has the notion of an ISite, where an ISite is an adapter between a designed component and its designer.  Once sited, the component can request various things of its container.  While not quite what I’m about to do, it’s a point for interested users to research.   I will explain this concept further in the next article.  I have created a UserControl called DesignSite.  DesignSite contains a border whose thickness is set to zero by default.  Inside DefaultDesignTypeCreator I shove the real IDesignableControl inside my DesignSite and let DesignSite handle the selection code when the real IDesignableControl is clicked on.

Back to Selection

The Site mentioned above wires up selection by resolving the ISelectionService.  Here, then, is the selection service implementation.

    public class DefaultSelectionService : ISelectionService

    {

        public DefaultSelectionService()

        {

            _selection = new List<IDesignableControl>();

        }

 

        public IKeyboardService KBService { get; set; }

 

        private List<IDesignableControl> _selection;

 

        public void Select(System.Collections.Generic.IList<IDesignableControl> incoming)

        {

            if (!KBService.ControlKeyDown)

            {

                _selection.Clear();

                _selection.AddRange(incoming);

                OnSelectionChanged();           

            }

            else

            {

                foreach (IDesignableControl idc in incoming)

                {

                    if (!_selection.Contains(idc))

                    {

                        _selection.Add(idc);

                    }

                    OnSelectionChanged();           

                }

            }           

        }

 

        public System.Collections.Generic.IList<IDesignableControl> GetSelection()

        {

            return _selection;

        }

 

        public int SelectionCount

        {

            get { return _selection.Count; }

        }

 

        public IDesignableControl PrimarySelection

        {

            get

            {

                return _selection[0];

            }

        }

 

        public event EventHandler SelectionChanged;

 

        protected void OnSelectionChanged()

        {

            if (null != SelectionChanged)

            {

                SelectionChanged(this, EventArgs.Empty);

            }

        }

Much simpler and shorter than I remember the WinForms implementation I remember.  We’ll see if I eat crow for saying so later.  Here is my site-style selection with a single component selected.

Shown here with multiple components selected by holding in Control or Open Apple:

You will note I don’t have any little “knobs” on the corners or anything.  I like how this looks better with a solid border and a gradient brush.  As we saw with Couch v1, perhaps my taste in visual appeal should not be trusted.  Speaking of couches, I have a chair now too.  You can take a look at my freshly painted Red Chair in the live demo.  Live demo….?

More ControlExtensions

Ever since being enlightened by Generics (the answer to everything) using the (cast) bothers me.  Casting using “as” bothers me more.  For this reason, I don’t know why the whole DependencyProperty framework deals with getting and setting of values as Object.  When I ported some code from Silverlight 2 beta 1 to beta 2, some of it was broken because I was using SetValue(Canvas.Left, 3) when in fact Canvas.Left wanted to be of type double and some auto-conversion was lost in the move to beta 2.  This error only pops up at runtime since SetValue takes type Object.  At least I can save myself some casting here and there by adding another extension method:

        public static T GetValue<T>(this DependencyObject d, DependencyProperty p)

        {

            return (T)d.GetValue(p);

        }

Which allows glorious code like:

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

It isn’t much but I’ll take it.

Conclusion & Next Steps

I have created a live demo at http://www.damonpayne.com/agt ; obviously this is a work in progress but feedback is welcome. 

I have a pretty good idea how I’m going to do resizing, that will come next.  After that I will do multi-select by drawing a rectangle and then Moving.  After moving works I will have to think of how I want object editing to work, some kind of Property Grid I’m sure.  From there, I’ll have to take stock of where I’m at, review my goals, and decide what to do next.

Source code:DamonPayne.AGT[10].zip (1.28 MB)

Tags:

Run time is design time for AGT[9]

by Administrator 22. September 2008 01:25

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

Visual Refactoring

Since this is a Silverlight application I thought I should spend some time making it look good. I hit my frustration limit in a very short period of time and what you see here is as far as I got. I am guessing Styling is not entirely baked in Silverlight 2 beta 2, or else doing a complete control template is really the way to go.

My first issue was with fonts. I downloaded a copy of the Trajan Regular font. Ever since Klipsch started using Trajan pro in the Palladium branding I’ve been digging the Trajan font family. At any rate, it seems that Silverlight is very picky about using extra fonts, be they set from a style or per control. Worse, it’s very difficult to track down where the issue might be. I have three primary tools available to me for Silverlight debugging: Blend 2.5 June CTP, VS2008 SP1, and running the application for inspection. Debugging is not fun when all three tools show different output, check out the ToolboxView:

clip_image002

Blend shows the correct font and color. VS2008 shows the correct nothing. Runtime shows the correct color but not font. I’ve tried obvious things like double checking relative paths and so forth. At this point I’m admitting defeat and moving on. At least my bad taste and visual look stolen from Klipschcorner’s CSS is better than black and grey. Right? Let’s skip to the punchline, here is what the application looks like now:

clip_image004

Couch++

I tricked a visual designer into making me a better purple couch. This person happens to be an extreme Adobe fanboy, to the degree that they didn’t believe me when I said there were other drawing programs for both vector and raster graphics. The Adobe fans are easily worse than the Mac fans. At any rate, that sure looks like a couch, at least. It’s rather large in relation to the design surface, but we’ll fix that with resizification later.

Speakers

I haven’t really thought of what all I want to include in the example HT Layout program, but I figured some speakers would be nice. I’m getting slightly better at drawing some things in Blend: these speakers look better than my original couch. If you’re not familiar with horn-loaded speakers you’ll have to trust me that the white horn shape is actually a reasonable caricature.

Conclusion

I’ll refactor later as I get to read more about the control templating and style creation. I think the app is starting to look like a real program, and we’re still less than 1000 lines of code. The next articles are not written yet, but the next step is to implement functionality that allows me to move, resize, and otherwise Transform things once they are on the designer surface. As it turns out, this is a very interesting problem for all sorts of reasons. I’m not breaking my Reality Blogging pledge, but remember I’ve done this in WinForms before, and I recall things like “selection” being more complex than it sounds: http://msdn.microsoft.com/en-us/library/system.componentmodel.design.iselectionservice_members.aspx . Once selection is implemented moving and resizing come free in WinForms, and I’ve got to build those from scratch now. I’m sure I’ll be spending some time in traffic this week and I’ll spend many mental cycles thinking about the interactions.

Source Code:DamonPayne.AGT[9].zip (1.26 MB)

Tags:

Windows Live Writer

by Administrator 22. September 2008 01:16

This article and AGT[8] were written with Windows Live Writer, after Larry talked me into trying it.  So far, I really like the idea, but the implementation could be better.  I typically write my articles in Word 2007, since I may be working on one for days or weeks.  Cut 'n pasting out of Word into the DasBlog FreeTextBox just works.  Sure, it generates the ugliest HTML you've ever seen, but it looks like code in both IE7 and FF3.  Copy/pasting out of VS2008 into Live Writer looks like this:

ToolboxItem speakItem = new ToolboxItem("Tower speaker", "This is a generic 2-way tower speaker with horn-loaded mid & high range", typeof(HornSpeaker));
Toolbox.AddItem(speakItem, "Audio");

Copy/pasting out of MS Word 2007 into Live Writer looks like this, using the "keep formatting" option.

            ToolboxItem speakItem = new ToolboxItem("Tower speaker", "This is a generic 2-way tower speaker with horn-loaded mid & high range", typeof(HornSpeaker));

            Toolbox.AddItem(speakItem, "Audio");

It's not bad, but it insists on inserting some extra spaces between lines.  I've also been uploading a .zip file of the source code as it stands at the end of each article, and I find no way to do this from Live Writer - perhaps that's not supported by the blogger API.  I do like it inserting & uploading images for me.

For complex articles, I'll still be copy/pasting from Word, but for a free program I do rather like Live Writer.

Tags:

Run time is design time for AGT[8]

by Administrator 21. September 2008 12:32

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-time environment 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

Code Refactoring

I’m eager to get on to some of the cool next steps like the ability to move and resize things on the Design Surface, but my TODOs are starting to pile up.  I’d rather not get too far into this and find that some fundamental thing that was bothering me is now a real issue.  Using the Task List feature in VS2008, I’ve got 20 things I should at least look at.

Namespaces

I’ve been thinking lately about some other Silverlight projects I have on the back burner, and how nice it would be to keep them in mind for the IoC stuff.  Therefore, I should rename DamonPayne.AGT.IoC to just DamonPayne.AG.IoC.  I like the fact that VS2008 makes the default namespace of a project match the project name, but the tooling stops there.  I have to rename the project, edit the default namespace, edit the assembly name, and do a global find/replace on the top level namespace.  Lame.  58 occurances replaced, and the Design and HTLayout projects reflect the new name; it even built the first time.

Events

While sitting in traffic I had the idea to overload += for my AggregateEvent<T> and make it feel more like a built in .NET event type.  As it turns out this won’t work the way I wanted it too so I erased that TODO.  I also got rid of the static “Test” method in EventAggregator.

Reflection

I have several TODOs related to reflection code.  The first one suggests Caching Type and Property information to make reflection go faster.  I’ll leave that alone for now and come back to it.

Tidy up: I found that there is a Type.EmptyTypes static var, similar to EventArgs.Empty.  I found three places where I could substitute Type.EmptyTypes.

I also found that I had some duplicate interface code and I could add some things to my TypeExtensions static class:

       public static bool ImplementsInterface(this Type t, Type intefaceType)

        {

            if (intefaceType.IsInterface)

            {

                Type[] interfaces = t.GetInterfaces();

                if (interfaces.Contains<Type>(intefaceType))

                {

                    return true;

                }

            }

            return false;

        }

 

        public static bool ImplementsInterface(this object o, Type intefaceType)

        {

            return ImplementsInterface(o.GetType(), intefaceType);

        }  

I was able to go through ServiceManager and ComponentBuilder and clean up some code using these Extension methods.

Several calls to invoking a Type’s default constructor can be replaced with Activator.CreateInstance; this was done throughout.  The 1-arg constructor for Presenter types was also replaced with a call to Activator.

Control Extensions

On suspicion that I would need the ability to scale controls without distorting their aspect ratio in the future, I left myself a note to create an extension method to determine an aspect ratio preserving method:

    public static class ControlExtensions

    {

        public static double UniformScaleAmount(this Control c, double maxDimension)

        {

            double originaltWidth = c.Width;

            double originalHeight = c.Height;

            double uniformScaleAmount = Math.Min(maxDimension / originaltWidth, maxDimension/ originalHeight);

            return uniformScaleAmount;

        }

    }

At this point, I am staring sideways with some dread at the remaining TODO items.  The remaining items involve:

1.       Revisit the relationship between ServiceManager and ComponentBuilder to see what, if anything, should be done about the crosstalk.

2.       The ToolboxCategory code doesn’t work how I’d like, and fixing it will involve a lot of digging deeper on the Silverlight control templating framework.

3.       There are several visual notes I’ve made to myself, I will address those in the next article.

4.       Something is amuck with InterfaceMocker, it only works once.

5.       I need to decide how I want configuration to work.

Configuration can have its own article I think, for now I’ll at least look into ServiceManager, ComponentBuilder, and InterfaceMocker.

Reflection Emit Redux

Inside ServiceManager.GenerateProxy, I did not take into account the fact that more than one client may ask for a given interface implementation.  In my startup code, I was able to get “Duplicate Type name in assembly” from time to time.  I just needed to add a check before building the type mock:

private static Dictionary<string, object> _proxies;

            string proxyName = t.Name+"TempProxy";

            if (_proxies.ContainsKey(proxyName))

            {

                return _proxies[proxyName];

            }

 

            object o = null;

            ModuleBuilder module = GetDynamicModuleBuilder();

            //

            TypeBuilder typeBuilder = module.DefineType(proxyName,

                TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Class,typeof(object) );

            typeBuilder.AddInterfaceImplementation(t);

No more proxy errors.

ServiceManager loves ComponentBuilder

As soon as I introduced ComponentBuilder I should have moved all object creation code to it.  But I didn’t, so now here I am looking at several TODO items.  From ServiceManager.ObtainDefaultInstance:

                o = Activator.CreateInstance(t);

                //TODO: maybe use the ComponentBuilder to create the damn thing in the first place

                ComponentBuilder.ResolveServiceDependencies(o);

                ComponentBuilder.BuildPresenters(o);

ComponentBuilder.Create:

        public static T Create<T>() where T :new ()

        {

            T instance = new T();

 

            ResolveServiceDependencies(instance);

            BuildPresenters(instance);

            ServiceManager.ManageInstance(instance);//TODO: verify this was the right design choice later

 

            return instance;

        }

Furthur, ServiceManager.ManageInstance (which might have been a hairy issue otherwise) is used from only a single place, ComponentBuilder.Create.  Creating a non-generic override of Create is step 1:

        public static T Create<T>() where T :new ()

        {

            T instance = default(T);

            instance = (T)Create(typeof(T));          

            return instance;

        }

 

        public static object Create(Type t)

        {

            object instance = Activator.CreateInstance(t);

            ResolveServiceDependencies(instance);

            BuildPresenters(instance);

            return instance;

        }

I hate having to go back and write code that involves casting and Type, when generics are so much cleaner, but such is life.  While chasing down the resulting bugs, I realized Manage(List<Type> types) and ManageInstance(object provider) were almost the same method.  The Manage method is refactored to call ManageInstance:

        public static void Manage(List<Type> types)

        {

            Type baseServiceType = typeof(IService);

 

            foreach (Type t in types)

            {

                object instance = ObtainDefaultInstance(t);

                ManageInstance(instance);

            }

        }

Lovely, more TODOs off my list and some unintended good side effects as well.

Unit Testing

Unit Tests allow us to refactor with impunity.  The fact that this project was ~500 loc doesn’t hurt either.

I Google “Silverlight 2 beta 2 unit test” and look what I find: http://code.msdn.microsoft.com/silverlightut/Release/ProjectReleases.aspx?ReleaseId=1543

I have to admit I had been thinking about making my own or Porting XUnit, but now I don’t have to.  I’ve grown to like the minimalistic XUnit interface, and I feel blind without NCover, but this will do for now.

Source code: DamonPayne.AGT[8].zip (622.87 KB)

Tags:

Run time is design time for AGT [7]

by Administrator 20. September 2008 19:06

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-time environment 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

Part 8 in a 50 part series begins...

Drag and Drop

It’s time to try dragging something out of the Toolbox and onto the DesignSurface.

We start dragging by wiring up events inside the ToolboxCategoryControl.  We’ve already established a mechanism for making sure only one ToolboxItem is ever selected, and that the ToolboxView owns the ToolboxCategoryControl.  We define a normal local dragstart event:

        void cat_DragStart(object sender, EventArgs e)

        {

            ToolboxItem tbi = SelectedItem;//Will already be populated from event wireup

            DragDropManager.BeginDrag(tbi);

        }

I have implemented the cleverly named DefaultDragDropManager in the DamonPayne.AGT.Design.Services namespace to handle the task.  The first thing DDDM does is obtain a drag-time representation of the Control visual.  This is one of the places where we start diverging more and more from how VS2008 works and looks today. While I’m not ready for the Visual Refactoring stage yet, we can clearly do better than a tiny mouse cursor with a “+” next to it.  This is what IDragTypeCreator is for: to provide a pluggable mechanism for building a better representation of what you’re dragging around than a mouse cursor.  I created a special DragContainer control for this purpose in DamonPayne.AGT.Design.Controls.  Here is the entire DefaultDragTypeCreator.CreateDragRepresentation method:

        public Control CreateDragRepresentation(Type t)

        {

            //Default behavior:

            DragContainer dc = new DragContainer();

            Control c = (Control)Activator.CreateInstance(t);

            //Thanks to http://www.jeffblankenburg.com/2008/04/how-about-some-code-simple-resizing-in.html for scaling code

            //TODO, extension method for uniform scale size?

            double originaltWidth = c.Width;

            double originalHeight = c.Height;

            double uniformScaleAmount = Math.Min(MAX_DIMENSION / originaltWidth, MAX_DIMENSION / originalHeight);

            ScaleTransform st = new ScaleTransform();

            st.ScaleX = uniformScaleAmount;

            st.ScaleY = uniformScaleAmount;

            c.RenderTransform = st;

            c.InvalidateMeasure();           

            c.UpdateLayout();

           

            //TODO: why couldn't I force this to recalc size here after setting render transform?

            double estimatedNewWidth = c.Width * uniformScaleAmount;

            double estimatedNewHeight = c.Height * uniformScaleAmount;

            double left = (dc.Width / 2.0D) - (estimatedNewWidth / 2.0D);

            double top = (dc.Height / 2.0D) - (estimatedNewHeight / 2.0D);

            c.SetValue(Canvas.LeftProperty, left);

            c.SetValue(Canvas.TopProperty, top);

            dc.LayoutRoot.Children.Add(c);

            dc.Opacity = .65D;

            dc.LayoutRoot.Opacity = .65D;

           

            return dc;

        }

I am basically creating an instance of whatever Type I was given, which ultimately came from ToolboxItem.Type.  Then I scale the instance down to where it will fit in my DragContainer and return the DragContainer.  With this in hand, DefaultDragDropManager wires up the mouse events for dragging around: basically when mouse left button goes up we do a hit check to see if there is an IDropTarget registered with us that would be under the current mouse position.  With the events set, we add the DragContainer control to the root visual of the Silverlight application.

Design Surface

The next thing I need to actually implement is the DesignSurface control, implementing IDropTarget.   This needs to be added into the IRegionManager way back when the ToolboxView and MessageConsole are created.  I’ve resized the whole Page such that I can fit a 480x640 gray DesignSurface in there.  You can now see me take my drag-time couch representation over to the design surface:

As soon as I got into developing the drop part, I realized I was in the Silverlight world, thinking like a WinForms developer.  Silverlight has built in hit test code already and I don’t need to do anything with Bounds.  The IDropTarget interface is thefore modified to use UIElement.HitTest(Point), so the EndDrag method can now do this:

        public virtual void EndDrag(MouseButtonEventArgs e)

        {

            RegionManager.TopLevelContainer.Children.Remove(_dragRepresentation);

            _dragRepresentation = null;

           

            foreach (IDropTarget target in _dropTargets)

            {

                if (target.IsHitTestVisible)

                {

                    IEnumerable<UIElement> elements = target.HitTest(_mousePos);

                    if (elements.GetEnumerator().MoveNext())//hit test succeed

                    {

                        IDesignableControl ctrl = DesignCreator.CreateInstance(_draggingType);

                        target.OnDrop(ctrl);

                    }

                }

            }

_draggingType = null;

        }

As soon as we enter OnDrop, the design time representation is worthless and we throw it away.  We’ll either be building the real control the surface or simply cleaning up.  I believe in the last article I described the interaction as the DesignSurface calling the creation method from OnDrop, but it makes more sense to put it here now.    For now, the IDesignTypeCreator simply instantiates the appropriate Type.  DesignSurface.OnDrop is incredibly simple for now:

        public void OnDrop(IDesignableControl dc)

        {

            dc.Visual.SetValue(Canvas.LeftProperty, _localMousePos.X);

            dc.Visual.SetValue(Canvas.TopProperty, _localMousePos.Y);

            LayoutRoot.Children.Add(dc.Visual);

        }

Success!  Here’s the very nice looking purple couch on the design surface.

Clearly there is tons of work to be done still, but I was shocked when I actually ran metrics: this just a little over 500 lines of code so far!

Source code: DamonPayne.AGT[7].zip (613.16 KB)

Tags:

Run time is design time for AGT [6]

by Administrator 19. September 2008 01:43

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-time environment 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

Designer surface interactions

My goal now is to actually start thinking about creating and designing things.  The preferred means of using this is as follows:

1.       From the toolbox I start dragging something out towards the Great Wide Open.

2.       Somehow visually indicate that I am dragging something.

3.       When I let go, see where I have dropped what I was dragging.

4.       If what I dropped upon was something that cares about being dropped on, do something useful like create a design-time representation of the thinger.

Some “improved” traffic patterns downtown where I work has left me with a lot of time to think about design interactions on my way home every day.  As I’m coming to expect, every single thing related to Drag ‘n drop in WPF or WinForms is not in Silverlight, but we can start defining some constructs of our own.  I’m adding a Design Solution Folder, within which will be a StarUML project.  Sometimes it’s better to explain using pictures.

  

1.       IToolboxService should use IDragDropManager to indicate that Drag operation is beginning.

2.       IDragDropManager will query the IToolboxService to determine what is the type of the dragging item.

3.       IDragDropManager will use IDragTypeCreator to build some type of visual representation to indicate where the dragging item is.  It will attach some events to this Control and place it as a child of the RootVisal of the IRgeionManager, in the front of the Z-order.

4.       The end of a drag/drop will be determined by the events attached to the Control, such as Left mouse button up.

5.       IDragDropManager will query all the IDropTarget instances registered with it.  If the Location of the where the dragging Control was dropped falls within the Bounds of one, it will call that instances OnDrop event with the original ToolboxItem.  This allows the IDropTarget to do whatever it wants using the original information.  Otherwise, the drag representation will be destroyed by IDragDropManager.

6.       In our case, if we successfully drop on top of the visual representation of a DesignSurface instance, DesignSurface will use IDesignTypeCreator to create the final design-time representation of the IDesignableControl.  This allows different visuals and behaviors to be attached such as adorners for resizing and registration with various other design-time services.

 

I am nearly certain I am going to have to change RootVisual of IRegionManager to be a Panel rather than UIElement.  UIElement has no “children” collection for me to use for my drag-representation.  Based on some things I want to support later (alignment snap lines, possible extra adorners) the DesignSurface is ultimately going to have to be a base type or implement some kind of interface with some kind of collection of “filters” or IDesignSurfaceBehavior implementations.

The source code as of the end of this article contains stubbed out unfunctional versions of the interfaces mentioned here. In the next article, we’ll actually try to drag/drop around a purple couch.  After that we’ll spend some time doing code refactoring (the TODO are building up) and some visual refactoring before moving on to more complex interactions.

Source code: DamonPayne.AGT[6].zip (598.45 KB)

Tags:

AGT[4] and AGT[5]

by Administrator 18. September 2008 23:42

The RSS aggregators chopped off AGT[4], probably for being too long.  Still, you'll want to read #4 since we make huge progress in that article.  AGT[5] deals with the wild tangent involving dynamic proxy generation with Reflection Emit.

AGT[4] http://www.damonpayne.com/2008/09/18/RunTimeIsDesignTimeForAGT4.aspx

AGT[5] http://www.damonpayne.com/2008/09/18/RunTimeIsDesignTimeForAGT5.aspx

Tags:

Run time is design time for AGT [5]

by Administrator 18. September 2008 21:28

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-time environment 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

Tangent: Dynamic proxy generation in Silverlight using Reflection Emit

In the last article I made mention of a tangent, an evil waste of time.  If a component asks for an IService implementation that cannot be found, I started out throwing an exception saying so.  Then I found a situation where I wanted to allow a Service to be registered after it was needed, and only have an exception be thrown if the missing service was accesses before it could be somehow injected later.  This is the solution.

While I’m not very interested in Dynamic Languages right now, I am reaping the benefits of Microsoft’s attention to them.  Without consideration for the DLR in Silverlight, I doubt they would have included such a complete Reflection Emit implementation.

ServiceManager

We started out with a ServiceManager.Resovle method that looked like this:

        public static object Resolve(Type t)

        {

            object svc = null;

            if (_services.ContainsKey(t))

            {

                svc = _services[t];

            }

            else

            {

                throw new NotSupportedException(string.Format("A provider for {0} was not found", t));

            }

            return svc;

        }

We need to replace what’s in the else clause and make ServiceManager and ComponentBuilder a little smarter.  The first step is to create a way to keep track of the situation inside ComponentBuilder:

        /// <summary>

        /// Key=IService type, value=list of objects waiting on that IService

        /// </summary>

        private static Dictionary<Type, List<object>> _lateBinding;

        static ComponentBuilder()

        {

            _lateBinding = new Dictionary<Type, List<object>>();

        }

                {

                    object svc = null;

                    try

                    {

                        svc = ServiceManager.Resolve(propType);

                    }

                    catch (NoImplementationException)//TODO: fix this so an exception is not thrown?

                    {

                        svc = SetupLateBinding(propType, component);

                    }

        private static object SetupLateBinding(Type interfaceType, object client)

        {

            object svc = InterfaceMocker.GenerateProxy(interfaceType);

 

            if (!LateBinding.ContainsKey(interfaceType))

            {

                LateBinding.Add(interfaceType, new List<object>());

            }

 

            LateBinding[interfaceType].Add(client);

 

            return svc;

        }

We change the ServiceManager to throw NoImplementationException instead of NotSupportedException.  When ServiceManager is given something new to Manage now, we check to see if anyone out there was waiting on a service of this type:

        private static void CheckLateBinding(Type interfaceType, object provider)

        {

            if (ComponentBuilder.LateBinding.ContainsKey(interfaceType))

            {

                List<object> clients = ComponentBuilder.LateBinding[interfaceType];

                foreach (object c in clients)

                {

                    var propQuery = from p in c.GetType().GetProperties().AsQueryable<PropertyInfo>()

                                    where p.PropertyType == interfaceType

                                    select p;

                    foreach (PropertyInfo propInfo in propQuery)

                    {

                        propInfo.SetValue(c, provider, null);

                    }

                }

                ComponentBuilder.LateBinding.Remove(interfaceType);

            }

        }

 

 

TypeMocker

The magic of all the code shown above is contained in TypeMocker.  Our goal is this: given an Interface type we cannot find an implementation for, make up a very simple placeholder one which could potentially do something useful if we call methods on it.  Unfortunately the most useful things raise more issues than they solve in .NET:

·         I don’t have pointers here, so I can’t very well try to replace “this”, nor get at the enclosing object instance to do any sneaky replacement.

·         If I wanted to do a delegation model, where the proxy ultimately just obtained a ‘normal’ instance and delegated to that via inheritance or something, those methods would need to be virtual and I don’t want to force that on users of this library, especially myself.

The first thing we need to be able to do is create the assembly. You may recognize the name from the Badge of Honor article.

        private static ModuleBuilder GetDynamicModuleBuilder()

        {

            if (null == _dynamicModuleBuilder)

            {

                AppDomain currentDomain = AppDomain.CurrentDomain;

                AssemblyName proxyName = new AssemblyName("DamonPayne.IoC.TempProxy");

                AssemblyBuilder aBuilder = currentDomain.DefineDynamicAssembly(proxyName, AssemblyBuilderAccess.Run);

                //

                ModuleBuilder module = aBuilder.DefineDynamicModule(proxyName.Name);

                _dynamicProxyAssembly = aBuilder;

                _dynamicModuleBuilder = module;

            }

 

            return _dynamicModuleBuilder;

        }

 

        private static AssemblyBuilder _dynamicProxyAssembly;

        private static ModuleBuilder _dynamicModuleBuilder;

Now we can work on dynamically creating a class that implements an interface in GenerateProxy:

        public static object GenerateProxy(Type t)

        {

            if(!t.IsInterface)

            {

                throw new ArgumentException(t + " Is not an interface");

            }

            //Useful:

            //http://msdn.microsoft.com/en-us/library/3y322t50(VS.95).aspx

            object o = null;

            ModuleBuilder module = GetDynamicModuleBuilder();

            //

            TypeBuilder typeBuilder = module.DefineType(t.Name+"TempProxy",

                TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Class,typeof(object) );

            typeBuilder.AddInterfaceImplementation(t);

We create a class that extends Object and implements interface “t”.  Because we are writing IL ourselves, lots of things that would ordinarily be done by the compiler have to be taken into consideration.  This includes calling the base class constructor from within our own:

            ConstructorBuilder conBuilder =

                typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName, CallingConventions.Standard, Type.EmptyTypes);//TODO: use Type.EmptyTypes elsewhere!

            //Define the reflection ConstructorInfor for System.Object

            ConstructorInfo conObj = typeof(object).GetConstructor(new Type[0]);

 

            //call constructor of base object

            ILGenerator il = conBuilder.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);

            il.Emit(OpCodes.Call, conObj);

            il.Emit(OpCodes.Ret);

This next bit is tedious but easy to understand once you know what you’re trying to do.  Create a method on my new type for each interface method I need to implement.  The implementation currently just throws an exception saying that our attempt to allow friendly late binding failed:

            List<MethodInfo> interfaceMethods = new List<MethodInfo>();

            interfaceMethods.AddRange(t.GetMethods());

 

            Type[] extraInterfaces = t.GetInterfaces();

            foreach (Type extraIface in extraInterfaces)

            {

                interfaceMethods.AddRange(extraIface.GetMethods());

            }

 

            foreach (MethodInfo method in interfaceMethods)

            {

                ParameterInfo[] mParams = method.GetParameters();

                Type[] paramTypes = new Type[mParams.Length];

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

                {

                    paramTypes[i] = mParams[i].ParameterType;

                }

 

                MethodBuilder methodBuilder =

                    typeBuilder.DefineMethod(method.Name,

                    MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.SpecialName, method.ReturnType, paramTypes);

               

                ILGenerator ilGen = methodBuilder.GetILGenerator();

                if (method.Name.Equals("Startup") || method.Name.Equals("Shutdown")) //TODO: dehackify this

                {

                    ilGen.Emit(OpCodes.Ret);

                }

                else

                {

                    ilGen.ThrowException(typeof(NoImplementationException));

                }               

            }

 

            Type finalType = typeBuilder.CreateType();

 

            o = Activator.CreateInstance(finalType);//TODO: replace other default constructor code with Activator

 

            return o;

There’s probably some work to do here to make sure attempts at creating  a proxy for the same type more than once are handled and so forth, but I’ll get to that when I decide on a unit test platform.  For now, its solving my late order-of-creation problems and would event support a circular dependency should one arrive.  My region manager/Page startup now goes through this sequence:

1.       We ask ServiceManager to handle Page, which is an IRegionManager.

2.       Page contains a RootPresenter, RootPresenter is created and needs an IToolboxService.

3.       IToolboxService is not found, but we get a dynamically created IToolboxServiceTempProxy instead.

4.       The UserControl_Loaded event of Page is called, which creates the toolbox service.

5.       Page gets a real IToolboxService implementation and everyone is happy.

At the end of this article, I am clearly not happy with the relationship between ComponentBuilder and ServiceManager.  I’m going to have to fix that, but first I want to get some more work done on my actual problem domain.

 

The source code is the same as what I posted for AGT[4].

Tags:

Run time is design time for AGT [4]

by Administrator 18. September 2008 21:26

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-time environment 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

Toolbox Service UI

It is now time to do some slightly more meaningful XAML work and create the visual representation of the toolbox service: this is where we’ll stow the designable components and drag them off onto the yet-to-be-created designer surface.  I am NOT going to worry about making it look snappy until later.

In order to test the ToolboxView, I have created the first designable item.  It is a big purple “Couch”.  I must stress to you that it does look like a couch.  It’s purple and has cushions, as anyone can clearly see.

 

What did I tell you?  Couchtastic.  I’m working on getting a better XAML couch or finding some patience to draw a better one.  This will be good enough to test with.

Looking back at the previous article, I chose to support a categorized toolbox like VS2008.  Therefore I’m going to make another UserControl called ToolboxCategory which is simply a heading stating the category name and a ListBox full of some representation of the ToolboxItems.  Simple XAML for the Category to start with:

<UserControl x:Class="DamonPayne.HTLayout.Controls.ToolboxCategoryControl"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="205" Height="Auto">

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid.RowDefinitions>

            <RowDefinition></RowDefinition>

            <RowDefinition></RowDefinition>

        </Grid.RowDefinitions>

        <TextBlock x:Name="_categoryHeader" Grid.Row="0">Category Header</TextBlock>

        <ListBox x:Name="_itemLst" Grid.Row="1" DisplayMemberPath="Name">           

        </ListBox>

    </Grid>

</UserControl>

I’ll add some public properties to ToolboxCategoryControl to communicate with its parent.  I’m not ready to rewrite Silverlight data binding so I’ll have to make some concessions.  I’ll go ahead and start writing the little code I need to populate the IToolboxService.

Create the ToolboxView and hand it over to the region manager as soon as the Page (our entry point UserControl) is loaded:

            ToolboxView tbView = ComponentBuilder.Create<ToolboxView>();

            mgr.AddView(tbView, "Toolbox");

Create a RootDesignerPresenter to handle logic for the DamonPayne.HTLayout.Page:

        [Presenter()]

        public RootDesignerPresenter Presenter { get; set; }

             Presenter.InitializeDesignableControls(); …

When I run this code I realize I made a mistake earlier.  The whole point of making the ComponentBuilder helper methods internal and not private was to use them to wire-up dependencies from service types as I manage them in ServiceManager.  Adding these calls to ServiceManager.CreateDefaultInstance fixes the issue and I’ve added another todo item to take a look at this after I have some more real code.   

                //TODO: maybe use the ComponentBuilder to create the damn thing in the first place

                ComponentBuilder.ResolveServiceDependencies(o);

                ComponentBuilder.BuildPresenters(o);

And another mistake pops up when I run this code. Page is created on AppStartup.  Page depends on RootDesignerPresenter, which depends on IToolboxService.  The IToolboxSerivce implementation is also our ToolboxView UserControl, which does not get created until the Page.UserControl_Loaded event completes.  So while we are wiring up the dependencies for Page, we get “A provider for DamonPayne.AGT.Design.Contracts.IToolboxService was not found.”  Now the previous design decisions come back to haunt us again and I’m facing another round of decisions.

1.       I could build the UI components in the proper order in Application_Startup.  This requires me, the developer, to be smart about what depends on what.  In fact, this also begs the question of what to do later if I find that there is a circular dependency between services.

2.       Return to the idea of making a class that implements IToolboxService and interacts with the visual representation using this other mechanism. 

3.       Create a mechanism for late-binding IService implementations.  By this I mean, rather than throw an exception from ServiceManager.Resolve() if an implementation cannot be found, store the fact that I’m missing an implementation for a certain interface on a certain type and inject this IService later if it becomes available.

4.       Make ServiceManager smarter.  Suppose we gave ServiceManager 100 types to manage at once, and some of those had dependencies on each other.  We could make ServiceManager either build a dependency tree, or even easier, take multiple passes over the collection of 100 types in a sort of poor-man’s (lame programmer’s) tree-flattening.

 

#1 would get me on my way the fastest, and also seems lame.  If I have circular dependencies, #1 will never work, although circular dependencies should be avoided.  I may also find myself spending a lot of time later keeping track of dependencies in my head and change the order of type registration around.

#2 will almost certainly just move the problem elsewhere, unless this new magical class does not have its visual representation provided to it as an automatic service.  This may be the cleanest option.

#4 might be useful on its own, but would also involve re-thinking how I create and manage Views and such.  I’m going to reject this one for now.

#3 has some appeal.  This would even allow for (accursed) circular dependencies.  The first drawback that comes to mind is that throwing an exception right away gives an immediate and unambiguous message that something is wrong and what that something is.  Attempting to use an IService that is waiting around for someone to create it by registering it with ServiceManager or ComponentBuidler would just result in a null reference – a ubiquitous and annoying exception.

There is an additional, and totally evil, option I could tack on to #3.  Silverlight is a partial implementation of the base class libraries we are used to, like the Compact Framework.  Because of this, one always has to go back and inspect class availability using Intellisense or the reference documentation.  Probably because of Silverlight’s dynamic language support – a fairly complete System.Reflection.Emit implementation is present.  In addition to remembering that I’m “waiting around for an ISuck implementation”, I could dynamically create a class that implements ISuck and throws an exception saying “Sorry, this is a placeholder and we never found an implementation for you” in case any methods are called before the implementation can be found. 

Using the great sanity check mechanism known as “Who’s on MSN Messenger” I happened to find a respectable software architect online to bounce this notion off of.  The conversation went something like this:

Damon: <explains situation and #3 solution>
Damon: So, is this overkill just to keep from throwing a null reference?

OtherDude: Uh, yeah, way overkill.  Save that for when your whole application works and you want to start gold-plating.  Version 3 or something.

Unfortunately I was 90% done with the reflection emit idea before this dissenting opinion arrived.  Honestly this was a ludicrous tangent but is also fun.  I will cover the solution in the next article.  Suffice to say that we are supporting Clever Late Binding for IService implementations and we can continue with the Toolbox Service.

The RootDesignerPresenter contains logic, such as “What am I putting in this toolbox?” For now I’ll only add one thing and be sure this all works.

        public void InitializeDesignableControls()

        {

            ToolboxItem couchItem = new ToolboxItem("Purple Couch", "This is clearly a couch", typeof(Couch));

            Toolbox.AddItem(couchItem, "Furniture");

        }

Making this work is painful because Silverlight does not support RelativeSource for data binding.  I can’t use a property on “this” for setting the Category Name TextBlock.  I’m now actually terrified of what I’m going to discover for data binding in Silverlight, but for now I’ll just make the property set the value. 

Of course the very next thing I try to do exposes another data binding shortcoming.  List<T> does not implement INotifyPropertyChange so that if I add a ToolBoxItem to a List<ToolBoxItem> which service as ItemsSource the GUI does not update itself.   I have three primary choices here.  I can build a special ScreenModel object for this screen which contains a List property I can fire INotifyPropertyChange for, I can try to implement a custom List<T> that does what I want, or I can mess with everything completely manually.  For now, I will use a ViewModel, which also erases my previous manual setting of the Category Name.  It’s worth reproducing the whole View Model here to point out how it’s implemented for automatic data binding goodness.

using System.Collections.ObjectModel;

using System.ComponentModel;

using DamonPayne.AGT.Design.Types;

 

namespace DamonPayne.HTLayout.ViewModels

{

    public class ToolboxCategoryModel : INotifyPropertyChanged

    {

        public ToolboxCategoryModel()

        {

            ToolboxItems = new ObservableCollection<ToolboxItem>();

        }

 

        private string _CategoryName;

        public string CategoryName

        {

            get { return _CategoryName; }

            set

            {

                _CategoryName = value;

                OnPropertyChanged("CategoryName");

            }

        }

 

        private ObservableCollection<ToolboxItem> _ToolboxItems;

        public ObservableCollection<ToolboxItem> ToolboxItems

        {

            get { return _ToolboxItems; }

            set

            {

                _ToolboxItems = value;

                OnPropertyChanged("ToolboxItems");

            }

        }

 

        public event PropertyChangedEventHandler PropertyChanged;

 

        public void OnPropertyChanged(string pname)

        {

            if (null != PropertyChanged)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(pname));

            }

        }

    }

}

 INotifyPropertyChange does not work for collection types.  INotifyCollectionChanged  does not seem to be used by data binding if I put it on my ToolboxCategoryViewModel.  The answer is System.Collections.ObjectModel.ObservableCollection<T>, which implements INotifyCollectionChanged  in the way the data binding code in Silverlight is expecting .  I’ve made myself some code snippets for the INotifyPropertyChanged code since I suspect I’ll need them later.

For the basic implementation:

<?xml version="1.0" encoding="utf-8"?>

<CodeSnippets

    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>OnPropertyChanged</Title>

      <Author>DamonPayne.com</Author>

      <Description>Insert a default OnPropertyChanged method and INotifyPropertyChanged event</Description>

      <Shortcut>OnPropertyChanged</Shortcut>

    </Header>

    <Snippet>

      <Code Language="CSharp">

        <![CDATA[

        public event PropertyChangedEventHandler PropertyChanged;       

       

        protected void OnPropertyChanged(string pname)

        {

            if (null != PropertyChanged)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(pname));

            }

        }       

        ]]>

      </Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>

 

Per property:

<?xml version="1.0" encoding="utf-8" ?>

<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

    <CodeSnippet Format="1.0.0">

        <Header>

            <Title>INotifyPropertyChanged property</Title>

            <Shortcut>inotifyprop</Shortcut>

            <Description>INotifyPropertyChanged prop, assumes OnPropertyChanged</Description>

            <Author>DamonPayne.com</Author>

            <SnippetTypes>

                <SnippetType>Expansion</SnippetType>

            </SnippetTypes>

        </Header>

        <Snippet>

            <Declarations>

                <Literal>

                    <ID>type</ID>

                    <ToolTip>Property Type</ToolTip>

                    <Default>string</Default>

                </Literal>

                <Literal>

                    <ID>property</ID>

                    <ToolTip>Property Name</ToolTip>

                    <Default>BindingProperty</Default>

                </Literal>

            </Declarations>

            <Code Language="csharp">

                <![CDATA[

 

private $type$ _$property$;

public $type$ $property$

{

    get { return _$property$; }

            set

            {

                  _$property$ = value;

      OnPropertyChanged("$property$");

            }

}

$end$]]>

            </Code>

        </Snippet>

    </CodeSnippet>

</CodeSnippets>

 

Next I’ll add some Item selection code using my event aggregator.  I’m going to define a generic SelectedDataChangedEvent:

    public class SelectedDataChangedEvent<T>:AggregateEvent<T>

    {

        public T Payload { get; set; }

    }

Now my constructor for the View Model looks like this:

        public ToolboxCategoryModel()

        {

            ToolboxItems = new ObservableCollection<ToolboxItem>();

            _selectionEvent = EventAggregator.Get<SelectedDataChangedEvent<ToolboxItem>, ToolboxItem>();

            _selectionEvent.Subscribe(item => { SelectedToolboxItem = null; }, item => !ToolboxItems.Contains(item));

        }

 

        private SelectedDataChangedEvent<ToolboxItem> _selectionEvent;

Behold the simple power of the event filters.  These two lambdas just say “If anyone anywhere else selects a toolbox item that is not part of my collection, I’ll be a nice citizen and clear my own selection.”  The selected item now participates in data binding AND event aggregation and it feels cleaner than I expected:

SelectedItem="{Binding Path=SelectedToolboxItem, Mode=TwoWay}"

        private ToolboxItem _SelectedToolboxItem;

        public ToolboxItem SelectedToolboxItem

        {

            get { return _SelectedToolboxItem; }

            set

            {

                _SelectedToolboxItem = value;

                OnPropertyChanged("SelectedToolboxItem");

                if (null != value)

                {

                    _selectionEvent.Raise(value);

                }

            }

        }

Back in the real IToolboxService implementation, we need to subscribe to this event too.  And now we have a toolbox service with gui!

 

It doesn’t look like much, but in the future there will be some “visual refactoring” going on.  I’ll sure miss my awesome purple couch though.

Conclusion

This step ended up being very long since there were several rabbit holes to go down.  We’ve created a Toolbox service, proven the EventAggregator is going to work, learned some things about Silverlight data binding I didn’t know before, and introduced the concept of the View Model into our lexicon.  In the next few steps, things will get really interesting!

Source code: DamonPayne.AGT[4].zip (571.61 KB)

Tags:

About the author

Damon Payne is a Microsoft MVP specializing in Smart Client solution architecture. 

INETA Community Speakers Program

Month List

Page List

flickr photostream