Damon Payne: Hand waving Silverlight 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
 Monday, July 05, 2010

It’s often very useful to know when an object we are editing has Changed.  The most iconic example is the “*” that used to appear next to the title of Word documents once you’d made the first change no matter how small.  It’s not an unusual request that a “Save” button be disabled until the User has made a savable change.  This is often referred to as an object being Dirty.

This article is part of a series about useful MVVM friendly features for your data objects and favoring composition over inheritance.  You can read the other parts:

Dirty Aware

Now that we’ve introduced the idea of Property Change Behaviors, it’s easy for us to flag an object as having changed when we set any property value.  Let’s refer back to the BBGrill object in the Metro BBQ sample application.  I find it’s useful to standardize the HasChanges/Dirty state using an interface. 

/// <summary>
/// Capable of storing and notifying Dirty/HasChanged state
/// </summary>
public interface IDirtyAware
{
    bool IsDirty { get; set; }
    bool SuspendChangeNotification { get; set; }
}

We can now author a new IPropertyChangedBehavior that deals with IDirtyAware instances and sets their IsDirty flag to true whenever a property value changes.

public class DirtyPropertyBehavior : IPropertyChangedBehavior
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="owningInstance"></param>
    public DirtyPropertyBehavior(IDirtyAware owningInstance)
        : this(owningInstance, new List<string>() {"IsDirty"})
    {

    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="owningInstance"></param>
    /// <param name="exclusions"></param>
    public DirtyPropertyBehavior(IDirtyAware owningInstance, List<string> exclusions)
    {
        _owner = owningInstance;
        _propertyExclusions = exclusions;
    }

    IDirtyAware _owner;
    List<string> _propertyExclusions;

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="owningInstance"></param>
    /// <param name="oldVal"></param>
    /// <param name="newVal"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool PropertyChanged<T>(object owningInstance, T oldVal, T newVal, string propertyName)
    {
        if (!Object.Equals(oldVal, newVal) 
            && !_propertyExclusions.Contains(propertyName)
            && !_owner.SuspendChangeNotification)
        {
            _owner.IsDirty = true;
        }
        return true;
    }
}

Note that we can easily provide the ability to exclude certain properties from setting IsDirty, which is very useful for the IsDirty property itself.

Obviously we could put this functionality right inside the property setters but we want to make it easy to add/remove behaviors to objects.

Dirty Grill

Going back to the Metro BBQ sample application, I can now make BBQGrill implement IDirtyAware and also use the DirtyPropertyBehavior to flag when any change has been made.

public class BBQGrill : BindableType, INotifyDataErrorInfo, IDirtyAware
{
    public BBQGrill()
    {
        _changeBehaviors.Add(new DirtyPropertyBehavior(this));

Now, this is useful as we can key business logic off of the IsDirty property, however we can also bind to it in the UI which is just as useful.  Recall the Ideal Grill survey page from Metro BBQ.  We wanted to only enable the Save and Rest buttons once the user had made changes.  Now it’s easy to do so:

 <Button Content="Submit" Margin="10,0,10,0" IsEnabled="{Binding Grill.IsDirty}" />
 <Button Content="Reset" Margin="10,0,10,0"  IsEnabled="{Binding Grill.IsDirty}" />

This gives us the effect that we want in the UI for any property with extremely little code.

Screen default state:

buttonsdisabled

After changing one property:

buttonsenabled

Because of the way we structured Property Change Behaviors we just got a Binding-friendly “is dirty” behavior on all properties of the BBQGrill object.  Once we wrote the DirtyPropertyBehavior we got this essentially for free on all properties without writing extra code for every property.

Suspend Notifications

You may already be thinking of situations where you’d like to temporarily turn this property behavior off.  For example, you might be populating BBQGrill object values from a WCF service and obviously invoking property setters in that situation should not instantly mark the object as Dirty or this feature won’t be very useful.  You may have noticed that the IDirtyAware interface included a SuspendChangeNotification property and that the DirtyPropertyBehavior pays attention to this and does not set IsDirty when SuspendChangeNotification is set to true.  This solves the re-entrant problem but we can do a little better in terms of the developer experience around this code using a context class.

/// <summary>
/// Enable nice using(){} semantics for setting property values without firing Dirty state behaviors
/// </summary>
public class SuspendDirtyContext : IDisposable
{
    public SuspendDirtyContext(IDirtyAware target): this( new List<IDirtyAware> { target } )
    {
    }

    public SuspendDirtyContext(IEnumerable<IDirtyAware> targets)
    {
        _targets = targets;
        int index = 0;
        _targets.ForEach(d => 
        {
            _previousValues[index] = d.SuspendChangeNotification;
            d.SuspendChangeNotification = true;
            ++index;
        });
        _previousValues = new bool[_targets.Count()];
    }

    IEnumerable<IDirtyAware> _targets;
    bool[] _previousValues;

    public void Dispose()
    {
        int index = 0;
        _targets.ForEach(d =>
            {
                d.SuspendChangeNotification = _previousValues[index];
                ++index;
            });
    }
}

In C#, classes that implement IDisposable can be used in using() blocks.  While we’re not really getting rid of any resources here the syntactical sugar is too good to pass up. This SuspendDirtyContext allows us to write nice readable code like the following:

var bbqGrill = new BBQGrill();
using (var ctx = new SuspendDirtyContext(bbqGrill))
{
    //... set initial values
}

The using() block in C# winds up creating try/catch/finally semantics in IL, so even if something causes an exception to be thrown our object state won’t be wonky outside of this scope.  As you can see the SuspendDirtyContext can also handle more than one IDirtyAware instance. 

Conclusion

Knowing when a user has made a change to a data entity through the UI is a commonly requested and useful feature, and implementing it can be pretty easy with the right design of your data entities.  Using the Composition approach makes it very easy to turn this feature on and off for different objects or even different instances of the same type depending on your needs. 

There are still situations that can be easily supported using the same techniques we’ve already demonstrated and these will be explored in the following articles. 



Monday, July 05, 2010 7:33:05 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [9]  |  Trackback
 Sunday, July 04, 2010

I have in hand a couple of one year MSDN Subscriptions with Visual Studio 2010 Ultimate.  Never you mind where they came from, but they didn’t fall of the back of a truck, they are legitimate. 

These are not for me, they are for YOU.  However, there are only two of these and according to my quick Twitter poll, a lot more than two people who’d like their own MSDN Ultimate.  Therefore, I pose to you a very simple contest.  Leave me a comment or send me an email telling me why you should get a subscription.  I will choose the winners late this week.

{Edit: I should be clear: I’m looking for community influencers here.  Tell me how this prize will touch more lives than just your own}



Sunday, July 04, 2010 8:10:59 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [9]  |  Trackback
 Thursday, July 01, 2010

I am honored to be re-awarded the Microsoft MVP award in the Client App Dev competency.  As others have pointed out, re-awardees have to be vetted just like new awardees – no coasting!  I couldn’t have achieved this without all of you out there who keep reading my blog and inviting me back to your user groups, code camps, and conferences.  I have big plans for the next year so please keep coming back!

Cheers,

Damon



Thursday, July 01, 2010 7:24:24 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [7]  |  Trackback
 Thursday, June 24, 2010

Given the momentum behind MEF at Microsoft and the fact that MEF has shipped in the box with Silverlight 4, you may find it somewhat disappointing that MEF is not available for Windows Phone 7.  Due to low level differences in the CLR for Windows Phone 7, you can’t just grab MEF from Codeplex either – read on to see the solution.

The Goal

Given the Marketplace restrictions, we know that our application code must go through a QA process and be signed in order to be deployable to the Windows Phone 7 (henceforth The Phone, or WP7). It’s not surprising then that the popular Silverlight Dynamic Recomposition techniques using MEF and downloaded XAP files won’t work.  However, it’s still incredibly useful to be able to use DeploymentCatalog and friends to wire together components in an application.  Getting this scenario working on The Phone is how I’m defining success for now.

Getting MEF to Build and Run

Prior to .NET 4 and Silverlight 4 shipping MEF has been available in preview form on Codeplex.  I was surprised that, being a daily reader of Silverlightshow.net, I hadn’t already seen an article submitted by someone who had built MEF for Windows Phone 7.  Now I know why.

Trial 1 – Building MEF Preview 9 as a Silverlight 3 Project

Many Silverlight 3 assemblies will turn out to just work on The Phone.  It seems reasonable that the first thing to try is to get MEF Preview 9 from CodePlex.  You can build this as a Silverlight 3 project and then reference the binary output from a WP7 project.  Trying to use DeploymentCatalog immediately blows up at runtime though.  That would have been too easy.

Trial 2 – Referencing MEF Preview 9 as a Silverlight 3 Project with Source

Rather than just building and referencing System.ComponentModel.Composition and System.ComponentModel.Composition.Initialization as assemblies, I thought I’d add the source projects to my test solution and step through to see what was going on.  It could always be something simple and obvious.  I was getting some interesting MissingMethodException and MethodAccessException behavior so as feared this was likely going to involve source code modifications to get it working, if it was possible at all.  I moved on to the next logical choice.

Trial 3 – Building MEF as Windows Phone 7 Class Libraries

At this point I assumed some low-level binary incompatibility or missing Type was the issue so I decided to create two new WPF class library projects and add the respective MEF source files to these.  Building MEF using Windows Phone 7 Class Library as the project type would immediately show me any issues with base class library parity.   At this point I was confronted by three major issues I was previously unaware of.

No System.Reflection.Emit

I was always extremely happy that SRE was included in Silverlight, so I was a bit surprised to find it missing from The Phone.  Sure, we never had it in Compact Framework development but The Phone is a much more heterogeneous platform compared to the giant universe of Windows CE/Windows Mobile hardware.  This spells doom for some of my more advanced Silverlight development scenarios, and for certain MEF features as well.  MEF uses SRE to create dynamic implementations of interfaces for Metadata – very handy for Lazy Imports. 

I have a reasonably easy workaround for the Import Metadata issue, but eager to get my main success scenario working I commented this feature out for now and moved on.

No IQueryable

The MEF code uses a fair bit of IQueryable internally.  Not being familiar enough with LINQ Expression hacking I can’t fathom why this was left out.  Performance implications?  Required Reflection Emit?  I don’t know.  Nonetheless, this code had to be changed in places like TypeCatalog, AggregateCatalog, and the ComposablePartCatalog base class.  Until I at least got this to compile I wouldn’t know if IEnumerable would work as a surrogate for IQueryable or not.

{No IQueryable http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/79858a29-4db0-460d-9a75-3630211a28fb/ }

No Dynamic Assembly Loading.  Period.

Silverlight XAP files are packaged with a Deployment Manifest that makes it easy to enumerate the assemblies included in a given XAP file.  In the Full versions of Silverlight you can use these AssemblyParts to load the assemblies and do useful things like add them to an AssemblyCatalog for MEF.

In their Zeal to prevent you from somehow loading code onto The Phone that had not gone through The Marketplace, you can’t load assemblies by name using this technique or any other technique.  Unfortunately this even includes assemblies that are hard-referenced by your WP7 Application.  What the harm would be in allowing developers to get a handle to all the assemblies that are hard-referenced is beyond me but this one is a showstopper.  Or is it?

We Can Continue, but Should We?

Suppose we can keep going.  Is MEF worthless in the Windows Phone 7 scenario given these limitations or is there still value to be had?  MEF is about Composition, Isolation, Separation, and Extensibility.  MEF is about programming against abstractions and making it easy for third parties who are unknown at compile time to extend your applications.  While some of these benefits cannot be realized given the WP7 limitations there are still meaningful benefits.

  1. Suppose you are developing frameworks and using MEF as your mechanism of wiring up Abstractions to their Implementations.  If you want to share any code with The Phone, it would suck to not at least be able to use MEF to map “IWidget to DefaultWidget”.  Programming against abstractions is always good.
  2. You may simply prefer the Import/Export programming model to alternatives like Factories and Service Locators.  Doing these things manually can take a lot of code.
  3. You may be used to ImportMany semantics, which is not a common feature in IoC containers that I know of.
  4. Microsoft may loosen up the WP7 deployment story in the future and you want to be ready.
  5. You love MEF and you have a Man-crush on  Glenn Block and you want to achieve Universal MEFness

The Current Workaround

For my part, I am working on frameworks that I’d like to recompile for Windows Phone 7, and I’ve become very accustomed to the great programming model MEF enables.  I’m NOT going to manually register every single Type I use to get around the DeploymentCatalog issues though.  Given that we can’t load assemblies by name, is there any compromise we can make?  Yes there is.

Since we can only hard-reference assemblies for Windows Phone applications, we can cheat and get a handle to each assembly we want to participate in Composition by using any Type from each assembly.  By then modifying DeploymentCatalog to add an additional constructor we can at least run some sample code and see what’s working and what’s not.  I went ahead and created a Mobile version of the Metro BBQ example I’ve been using lately.  I have an entry point application and two module assemblies and I’d like to to use MEF to glue them all together.

projects

In the entry point application there is a simple UI that displays Gas Grills and Charcoal Grills, and I’d like to create a simple ViewModel that gets populated with IGrill implementations via MEF Catalogs.

namespace MetroBBQ.Mobile
{
    public class ShellViewModel : INotifyPropertyChanged
    {
        private IEnumerable<IGrill> _AllGrills;

        [ImportMany]
        public IEnumerable<IGrill> AllGrills
        {
            get { return _AllGrills; }
            set
            {
                _AllGrills = value;
                OnPropertyChanged("AllGrills");
            }
        }              

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
        
    }
}

Now using some bootstrapper types from each assembly along with my DeploymentCatalog modifications, I create a CompositionContainer, and give it all a try.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    var shimTypes = new List<Type> { typeof(MetroBBQ.Mobile.GasGrills.Bootstrapper), typeof(MetroBBQ.Mobile.CharcoalGrills.Bootstrapper), GetType() } ;
    var deploymentCatalog = new DeploymentCatalog(shimTypes);
    var container = new CompositionContainer(deploymentCatalog);
    var vm = new ShellViewModel();
    container.ComposeParts(vm);
    DataContext = vm;
}

Sure enough, I’m now able to Compose Parts just like my MEF experience on other platforms.  There are a LOT of other scenarios to test but I’m encouraged by this initial success.  My ViewModel gets populated with IGrill implementations from two other assemblies in my Windows Phone 7 application.

GrillsScreen

What do you think, dear reader?  Is it worth doing more testing or shall I add this to the collection of forgotten novelties on my shelf?  Would you like the source code?  Is this worthy of MEFContrib? Leave me a comment!  For my part, I’m happy to be able to continue my MEF experience on this new platform.



Thursday, June 24, 2010 9:52:19 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [14]  |  Trackback
 Sunday, June 20, 2010

In the next article in this series, I introduce the idea of Property Change Behaviors.  This concept is used to provide a means of easily attaching useful features to data objects.  Any number of features can be added without requiring a complex inheritance structures or keeping you from configuring the features on a per-property or per-instance basis.

This article is part of a series about useful MVVM friendly features for your data objects and favoring composition over inheritance.  You can read the other parts:

Can we all agree to call Silverlight/WPF development “XamlWorld” ?

Introducing Property Change Behaviors

Recall that we opened the first article with a list of actions we might want to take when a property on a XAMLWorld data object changes:

  1. Firing change notification
  2. Handling property-level validation
  3. Tracking when an object has changed (dirty/change tracking)
  4. Update dependent or calculated properties
  5. Providing Undo support

It would be ideal if we could provide a mechanism to do all of these things, and whatever else we think of in the future, in a generic fashion.  Since XAML technologies provide fantastic data binding support centered around the notion of CLR Properties, connecting our behaviors to properties is a very good approach.  If you want to use anything that’s built into XamlWorld, you need to make peace with the notion that The Property is the coin of the realm. 

We can think of Property Changed Behaviors as a sort of “visitor” or “Chain of Responsibility”.  Here is the interface for Property Change Behaviors:

public interface IPropertyChangedBehavior
{
    /// <summary>
    /// Do something useful when a property changes
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="owningInstance">Object instance whose property has changed</param>
    /// <param name="oldVal">current property value</param>
    /// <param name="newVal">new property value</param>
    /// <param name="propertyName"></param>
    /// <returns>Returns true of more behaviors can keep processing</returns>
    bool PropertyChanged<T>(object owningInstance, T oldVal, T newVal, string propertyName);
}

There are more complex scenarios for IPropertyChangedBehavior, but for this series of articles we’ll stick with the first draft.  This interface allows us to do interesting things when Property values change.

Remember what our BindableType property setter looked like previously?  Recall that we did this to avoid re-creating the basic INotifyPropertyChanged implementation over and over:

protected void OnPropertyChanged(string propName)
{
    if (null != PropertyChanged && !SuspendChangeNotification)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

In order to move forward I’ve changed the semantics somewhat.  The code snippet for setting properties in this style is provided at the end of this article.  Keeping with the Metro BBQ example from the previous article, here’s what the properties look like on my BBQGrill class.

Materials _KettleMaterial;

public Materials KettleMaterial
{
    get { return _KettleMaterial; }
    set { Set<Materials>(ref _KettleMaterial, value, "KettleMaterial"); }
}

This allows me to use semantics like the following on the BindableType base class.  Yes, we’re still extending BindableType for now, one step at a time.  We will eventually refactor to avoid the need to extend BindableType.

protected void Set<T>(ref T local, T newVal, string name)
{            
    T localCopy = local;
    local = newVal;

    if (null != _changeBehaviors)
    {                
        foreach (var behavior in _changeBehaviors)
        {
            bool @continue = behavior.PropertyChanged<T>(this, localCopy, newVal, name);
            if (!@continue) { break; }
        }
    }
    else//no behaviors, just carry on with the old way
    {
        OnPropertyChanged(name);
    }                
}

Now things are starting to get interesting.  BindableType has an instance variable to contain the IPropertyChangedBehaviors we want to use.

protected IEnumerable<IPropertyChangedBehavior> _changeBehaviors;

Each object now has a configurable list of interesting actions that we can go through any time a property value changes.  Based on the fact that we’d like to enable Binding updates, the choice for the first IPropertyChangedBehavior implementation is obvious:

public class NotifyPropertyBehavior : IPropertyChangedBehavior
{
    public NotifyPropertyBehavior(INotifyPropertyChanged target, Action<string> changedCallback)
    {
        Requires.NotNull(target, "target cannot be null");
        Requires.NotNull(changedCallback, "changedCallback cannot be null");
        Target = target;
        ChangeCallback = changedCallback;
    }

    public INotifyPropertyChanged Target { get; set; }

    /// <summary>
    /// Needed since only the owning instance can fire the event
    /// </summary>
    public Action<string> ChangeCallback { get; set; }


    /// <summary>
    /// Fire change notification only if the new value != old
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="owningInstance"></param>
    /// <param name="oldVal"></param>
    /// <param name="newVal"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool PropertyChanged<T>(object owningInstance, T oldVal, T newVal, string propertyName)
    {
        if (!Object.Equals(oldVal, newVal))
        {
            ChangeCallback(propertyName);
            return true;
        }
        else
        {
            return false;
        }
    }
}

… and in the constructor of BindableType, we create the following defaults used in the Setter, above:

_changeBehaviors = new List<IPropertyChangedBehavior>

{ new NotifyPropertyBehavior(this, s=> OnPropertyChanged(s) ) };

Now, we are in a situation where our data objects can have any number of IPropertyChangedBehaviors attached.  For now, the NotifyPropertyBehavior has merely replaced how we fire change notification, but the behaviors shown in the following articles will implement much more interesting functionality.

PropertyChangedBehaviorCD

Conclusion

So, we’ve created some basic functionality for iterating through an ordered list of interesting actions whenever a property value changes.  Based on the logic inside BindableType, if the property values are equal we would stop processing subsequent Property Change Behaviors for this instance by returning false from NotifyPropertyBehavior.

In the next article we’ll start adding more useful and interesting features using the IPropertyChangedBehavior approach.

Download the bprop code snippet.



Sunday, June 20, 2010 2:23:27 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Wednesday, June 16, 2010

Even if you’re using a framework like RIA Services, one of the first design decisions you’ll run into when building a new Silverlight application is how to represent your data on the client.  Above and beyond implementing change notification (INotifyPropertyChanged for bindings) there may be several other features you need such as Undo support, dirty tracking, dependent properties, and validation.  I’m going to show you how to do support these features in a plug-and-play style.

This article is the First Part in a series about useful MVVM friendly features for your data objects and favoring composition over inheritance.

{The future articles will be linked to here as well}

A Base Class and a Mess?

We need to start representing our data types in Silverlight.  If you’ve written a few Silverlight applications, you’ve probably implemented a base class to avoid retyping the INotifyPropertyChanged basics.  In my case, I also write some code snippets to be able to create these properties quickly.

namespace HandWaver.PresentationModel
{
    /// <summary>
    /// A handy base class for classes that want to participate in data binding
    /// </summary>
    public class BindableType : INotifyPropertyChanged
    {
        public BindableType()
        {
            SuspendChangeNotification = false;
            _changeBehaviors = new List<IPropertyChangedBehavior> { new NotifyPropertyBehavior(this, s=> OnPropertyChanged(s) ) };
        }

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propName"></param>
        protected void OnPropertyChanged(string propName)
        {
            if (null != PropertyChanged && !SuspendChangeNotification)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        /// <summary>
        /// Allow turning of change notification
        /// </summary>
        public bool SuspendChangeNotification { get; set; }

Now, depending on your application requirements you may also want to support some of the following features:

  • Validation: You’d like to hook into the Silverlight validation framework to display data entry errors to the end user.
  • Dirty Checking: Maybe you’d like to only enable the Save button when something actually changes on your Model objects.  Maybe you don’t want to bother calling your “Save” WCF service if nothing has actually happened.  Has a given Instance changed or is it sill in it’s original state?
  • Dependent (Reactive or Calculated) properties: Suppose when one property of an instance changes, you need to update another property.  For example, you may have a read-only property which returns the sum of several other properties.  
  • Undo: You may have an Undo requirement, and adding functionality during property change is a good place to implement this. Having excellent multi-level undo support isn’t as hard as you might think!

Given the fact that I want to reuse my INotifyPropertyChanged code, I may wind up with an inheritance hierarchy for each of these features:

click for large version

The new UML modeling features in Visual Studio 2010 Ultimate are awesome!

Take a look at the two notes at the bottom of this class diagram.  We encounter problems when we need to start mixing and matching this functionality.  If I need both a DirtyAware type and a Validating type, I’ll wind up creating a ValidatingDirtyAware base class.  This quickly leads to explosion of the number of classes.  It also means the code I implement for each feature might get copied around, making it hard to maintain.  We shouldn’t need to create a new base class for each new feature we think of. 

Introducing Metro BBQ

While I have several “real” Silverlight applications I work on, I like to use fun samples.  I have also adopted a “No battleship gray” policy lately in order to force myself to work on design and UX more.  Still, this takes time so for this series of articles I will use one of the new Silverlight 4 Application Themes.  This is a Silverlight application dealing with BBQ grills, BBQ-ing, smoking, etc.  As I am using the Metro theme, I am calling this Metro BBQ. 

The part of Metro BBQ we’re interested in is a screen where users can tell us about their ideal BBQ Grill configuration.

metrobbq0

This simple form is missing some functional and design features :

  • We need to be able to handle Validation without relying on a base class
  • We’d like to keep the Submit and Reset buttons disabled until the user makes changes (dirty checking)
  • We need to update the “Metro BBQ Grill Grade” as the user changes values in this form (dependent properties)
  • We’d like to provide an Undo button to provide undo for the user’s input

A Distinct Responsibility

The first responsibility we can break out is to offload the bulk of the Validation Error implementation work from BindableType into it's own class.  ValidationErrorManager does most of the work needed for an INotifyDataErrorInfo implementation.

using System;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using HandWaver.Core;

namespace HandWaver.PresentationModel
{
    /// <summary>
    /// 
    /// </summary>
    public class ValidationErrorManager
    {
        public ValidationErrorManager(INotifyDataErrorInfo client, Action<string> errorsChangedAction)
        {
            Requires.NotNull(client, "Must specify target client");
            Requires.NotNull(errorsChangedAction, "errorsChangedAction: callback must be specified");
            _client = client;            
            ErrorsChanged = errorsChangedAction;
        }

        INotifyDataErrorInfo _client;
        Dictionary<string, List<string>> _errors;
        
        /// <summary>
        /// 
        /// </summary>
        public Action<string> ErrorsChanged { get; set; }

        /// <summary>
        /// Clear errors for the given property and fire ErrorsChanged for that property
        /// </summary>
        /// <param name="propName"></param>
        public void ClearErrors(string propName)
        {
            if (null != _errors && _errors.ContainsKey(propName))
            {
                _errors[propName].Clear();
            }
            ErrorsChanged(propName);
        }

        /// <summary>
        /// Add an error for the given property and fire ErrorsChanged for that property
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="error"></param>
        public void AddError(string propName, string error)
        {
            EnsureErrorContainer(propName);
            _errors[propName].Add(error);
            ErrorsChanged(propName);
        }

        /// <summary>
        /// 
        /// </summary>
        protected void EnsureErrorContainer()
        {
            if (null == _errors) { _errors = new Dictionary<string, List<string>>(); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propName"></param>
        protected void EnsureErrorContainer(string propName)
        {
            EnsureErrorContainer();
            if (!_errors.ContainsKey(propName))
            {
                _errors[propName] = new List<string>();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            if (null == _errors
                || !_errors.ContainsKey(propertyName))
            {
                return null;
            }
            return _errors[propertyName];
        }

        /// <summary>
        /// 
        /// </summary>
        public bool HasErrors
        {
            get
            {
                if (null == _errors) { return false; }
                int propsWithErrCount = _errors.Values.Where(l => l.Count > 0).Count();
                return (propsWithErrCount > 0);
            }
        }

    }
}

With comments ValidationErrorManager is over 100 lines of code, completely worth moving into it’s own class.  The BBQGrill class can use this functionality thusly:

public class BBQGrill : BindableType, INotifyDataErrorInfo
{
    public BBQGrill()
    {
        FireType = FireTypes.Charcoal;
        Smoker = false;
        KettleMaterial = Materials.Aluminum;

        _errMgr = new ValidationErrorManager(this, s => OnErrorsChanged(s));
    }

    ValidationErrorManager _errMgr;

    double _GrillingArea;

    /// <summary>
    /// Grilling area in square inches
    /// </summary>
    public double GrillingArea
    {
        get { return _GrillingArea; }
        set
        {
            if (value < 150.0)
            {
                _errMgr.AddError("GrillingArea", "You need a bigger grill!");
            }
            else
            {
                _errMgr.ClearErrors("GrillingArea");
            }
            Set<double>(ref _GrillingArea, value, "GrillingArea");
        }
    }

We’re still extending BindableType for now, but we’ve made it very easy to add a new feature (property-level validation) to our data types.  At this point we have added this feature without adding a new layer to the inheritance hierarchy.  The class diagram for BBQGrill looks like this now:

validationerrormgrcd

Conclusion

This may not look like much at this point, but we’re taking the first step towards a powerful and flexible way of thinking.  In the next part, we’ll demonstrate a mechanism that’s useful for mixing and matching even more powerful features for data types,starting with change tracking.



Wednesday, June 16, 2010 8:18:05 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Sunday, May 23, 2010

Next week I’ll be speaking about the MVVM design patter in Silverlight (and WPF) in Chicago.

The Model View ViewModel (MVVM) is one of the most quickly and widely adopted design patterns in recent memory.  The models and conventions built into Silverlight and WPF lend themselves to this pattern extremely well. Join Damon Payne as he discuss a history of Presentation Model patterns, MVVM in Silverlight and WPF, and how to achieve Blendability at design time with MVVM.  He'll also spend some time demonstrating how MVVM helps you cope with the asynchronous constructs in Silverlight.

You can register here:

http://chicagosilverlight.eventbrite.com/?ref=eivte&invite=MzU0OTM0L2RhbW9uQGRhbW9ucGF5bmUuY29tLzE%3D%0A&utm_source=eb_email&utm_medium=email&utm_campaign=invite

The Illinois Technology Association
200 S. Wacker Drive, 15th Floor
Chicago, IL 60606

One of my favorite topics in one of my favorite cities, this should be fun.  If anyone from the Metro Milwaukee area would like to come down and perhaps do some Fogo de Chao afterwards please let me know!  Maybe some Chicagoland carnivores will join us?



Sunday, May 23, 2010 10:48:01 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [13]  |  Trackback

This is an article about how I set out to write a Binary Serializer for Silverlight and what I learned along the way.   I have some very large Reference Data entity sets in isolated storage and loading these sets so I can run LINQ queries against them has been extremely painful – I don’t want to have to wait 30 seconds before my feature is usable.  My first analysis lead me to the conclusion that disk IO was my main performance bottleneck and that drastically reducing the file sizes by using Binary Serialization would be just what the doctor ordered.

[Note: if you’ve seen me speak recently, yes I’ve been talking about posting this for quite a while]

Design goals:

  1. Uses familiar DataContract/DataMember/KnownType semantics – use something that my types are already decorated with and that developers are already familiar with.
  2. No fixed buffer size for strings – I have seen some other Silverlight/Binary Serialization schemes that use fixed buffer sizes for strings.  This is undesirable.
  3. FAST – I wanted this to be faster than the built in XML + DataContractSerializer mechanism.
  4. Theoretically Survive assembly rebuilds – Most binary serialization schemes (think ASP.NET SQL server session state) do not tolerate different assembly versions but rather require an exact match.  Since I’m using [DataContract] semantics I hoped to allow whatever properties still match to survive across builds if possible.
  5. Handle lists and complex object graphs
  6. Low disk usage: binary files should be far smaller than equivalent XML files

Building the Serializer

There’s no built in binary serialization for Silverlight or I wouldn’t be writing this article.  There are, however, BinaryWriter and BinaryReader classes.  This saves one from needing to write custom logic for all primitive types and in particular being careful to encode strings with additional length attributes.  This leaves us with fewer problems:

  1. Logic to recursively serialize object graphs
  2. Handling null objects
  3. Handling collection types
  4. Persisting data about what types and what properties of those types were serialized.  This is key for meeting the durability design goal (#4)

The source code will be provided so we’ll only go over some highlights here.  The first thing to do was to build an object with several different property types to test with.  Instances will populate themselves with random values.  While this can throw off apples-to-apples comparisons the differences should be significant enough to ignore this.

[DataContract]
public class RefDataRow
{
    /// <summary>
    /// Set some random values
    /// </summary>
    public RefDataRow()
    {
        Random r = new Random();
        Id = Guid.NewGuid();
        Field0 = r.Next(100000);
        Field1 = r.Next(100000);
        Field2 = r.Next(100000);
        Field3 = r.Next(100000);
        Field4 = r.Next(100000);
        Field5 = r.Next(100000);
        Field6 = r.Next(100000);
        Field7 = r.Next(100000);
        Field8 = r.Next(100000);
        Field9 = r.Next(100000);
        Value = r.NextDouble();
        int dLen = r.Next(50);
        var sb = new StringBuilder();
        for (int i = 0; i < dLen; ++i)
        {
            sb.Append((char)r.Next(26) + 65);
        }
        Description = sb.ToString();
    }

    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public int Field0 { get; set; }
    [DataMember]
    public int Field1 { get; set; }
    [DataMember]
    public int Field2 { get; set; }
    [DataMember]
    public int Field3 { get; set; }
    [DataMember]
    public int Field4 { get; set; }
    [DataMember]
    public int Field5 { get; set; }
    [DataMember]
    public int Field6 { get; set; }
    [DataMember]
    public int Field7 { get; set; }
    [DataMember]
    public int Field8 { get; set; }
    [DataMember]
    public int Field9 { get; set; }

    [DataMember]
    public double Value { get; set; }

    [DataMember]
    public string Description { get; set; }

We add some more code to this class later, but for now this will do.  So far we’ve done nothing but decorate this class with DataContract/DataMember attributes.  There’s a mix of various data types in here, with a lot of integers in the middle.

Test UI

A user interface to run tests and show results will help.  I’ve come up with the following options which can be ran in order if we wish to perform all tests.  The last two buttons can be ignored for now.

binser0

Running Some Tests

My Generate Test Data command creates 300,000 randomly instantiated instances of my RefDataRow class.

I am using some new framework libraries I’m working on here, but in essence there’s a ViewModel with a command bound to each button.  You can see that using the BinarySerializer looks very similar to DataContract serializer:

DataContractSerCmd = new TimedCommand<string>(s =>
{                
    using (var fs = file.CreateFile(XmlDataFileName))
    {
        var dcs = new DataContractSerializer(typeof(List<RefDataRow>));
        dcs.WriteObject(fs, TestData);
    }
    ReportSizes();

}, timer, "Data Contract Serialization");


BinarySerCmd = new TimedCommand<string>(s =>
{
    using (var fs = file.CreateFile(BinDataFileName))
    {
        var bs = new BinarySerializer(typeof(List<RefDataRow>));
        bs.Serialize(TestData, fs);
    }

    ReportSizes();

},timer, "Binary DataContract Serialization");

DataContractDeSerCmd = new TimedCommand<string>(s =>
{
    using (var fs = file.OpenFile(XmlDataFileName, FileMode.Open))
    {
        var dcs = new DataContractSerializer(typeof(List<RefDataRow>));
        var obj = dcs.ReadObject(fs);
    }

}, timer, "DataContract Deserialize");

BinaryDeSerCmd = new TimedCommand<string>(s =>
{
    using (var fs = file.OpenFile(BinDataFileName, FileMode.Open))
    {
        var bs = new BinarySerializer(typeof(List<RefDataRow>));
        var obj = bs.DeSerialize<List<RefDataRow>>(fs);
    }

}, timer, "Binary Deserialize");

So, we have four DelegateCommand<T> style commands wrapped in a low-resolution timer.  Since I’m randomly generating test data each time the results will be slightly different, but I found the run below to represent an average case:

binser1

 

Thinking About the Results

So, I’ve met my design goals, but my performance goals are way off the mark.  What’s going on?

Disk Space Usage

Without paying the angle-bracket tax, and by using binary serialization, the binary file is less than 1/3 the size of the XML file.  The one saving grace I can think of, in terms of my performance goals, is that I’m doing these tests on a Solid State Drive.  With incredibly high sequential write speeds, any performance gains that might come from writing a much smaller file to disk are certainly minimized.

Performance

The out of the box DataContractSerializer is faster, and on deserialization it completely eats my lunch.  What happened?  I have a theory.

What happens when you try to out-do Microsoft engineers?  Sometimes you lose.  Reflection is an awesome and powerful CLR feature and has always been known as a performance no-no.  While I read many times how much faster Reflection (and App Domains) became in the 2.0 runtime it’s still really slow to invoke methods via reflection.  My code is calling all getters and setters here using PropertyInfo.  The built in DataContractSerializer is almost certainly using Reflection Emit to generate “real” classes to get and set values and that is going to be a lot faster than what I’m doing here.  While I’ve written a lot of SRE code in the past couple of years, I want to test my theory before I take the time to do that here.

Another Try

In order to prove that Reflection is my downfall, I create a new .NET interface:

public interface IBinarySerializable
{
    void WritePrimitiveValue(string propName, BinaryWriter bw);
    void ReadPrimitiveValue(string propName, BinaryReader br);
}

The goal here is to have my RefDataRow call setters on itself, looking up a lambda expression in a Dictionary by property name.  Some of the Writer code might look like so…

_actions= new Dictionary<string,Action<BinaryWriter>>();
_actions.Add("Id", bw => bw.Write(Id.ToByteArray()));
_actions.Add("Value", bw => bw.Write(Value));
_actions.Add("Description", bw => bw.Write(Description));
_actions.Add("Field0", bw => bw.Write(Field0));

… with the corresponding reader code looking like this:

_readActions = new Dictionary<string, Action<BinaryReader>>();
_readActions.Add("Id", br => Id = new Guid(br.ReadBytes(16) ) );
_readActions.Add("Value", br => Value = br.ReadDouble());
_readActions.Add("Description", br => Description = br.ReadString());
_readActions.Add("Field0", br => Field0 = br.ReadInt32() );

I am then giving the BinarySerializer a hint, telling it to check for DataContract objects implementing IBinarySerializable and using real method calls. Even though these are Virtual method calls, they’re going to be a lot faster than Reflection.  What’s the result?

binser2

I finally take a strong lead in serialization, and deserialization has increased by 100%, but that aspect is still slower than the built in DataContractSerializer. 

Conclusion

This needs a lot more testing before it’s production ready for complex object graphs but it does work and it’s easy to use.  If I can do some more research and get the read&deserialize speed way down, I’ll go ahead and do the System.Reflection.Emit work necessary to make the speed gains from IBinarySerializable automatic without having to implement this interface.  The source code will be provided as part of a new application framework I’m working on.

Since reading is my issue here, I have to wonder about implementing a XamlWriter in Silverlight, so that I could use XamlReader on the reading end.  XamlReader is extremely fast as it uses a lower level engine than my object models can get at. 

On the flip side, if you are dealing with less than 300,000 complex objects, this may be just what you wanted. Stay tuned for more research on this topic.



Sunday, May 23, 2010 10:36:28 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Thursday, May 06, 2010

Someone had to cancel their talk at the Wisconsin .NET User’s Group so guess what: you’re stuck with me.  Although this is only a few days away the topic is still up for grabs.  Check out the twitter poll:

http://twtpoll.com/olezfc

I’ll either be going over What’s new in Silverlight 4 or Silverlight for Windows Phone 7 development, or maybe a little of both.  Both are really fun topics and I hope to see you there.  I’ll update this post soon with the results of the poll.  You can also leave comments here.

[Update: Looks like Windows Phone 7 Development won the poll!  This will be a fun talk, and don’t worry we can talk about Silverlight 4 during Q and A]



Thursday, May 06, 2010 5:03:14 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |  Trackback

I had a great time at the Chicago Code Camp this year, although I was really only able to dash in, give my talk, and dash out.  What’s more, I was competing with some famous people, so I was pleased that my room was packed and that there were so many thoughtful questions.  One gentleman even told me he drove three hours to this event just to see me.  Wow!

Here are the materials from my presentation:

Slides

Code

Since the topic of run time composition came up more than once, see here for a use case and sample code.

I hope to be invited back next year!

MEF | Silverlight | WPF


Thursday, May 06, 2010 9:49:19 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Thursday, April 22, 2010

[Update: the schedule has been posted]

I’m pleased to announce that the latest version of my “What can I do with MEF?” talk has been accepted for Chicago Code Camp 2 on May first.  While I actually have a release at work that day they’ve been kind enough to let me go towards the end of the day.

In this talk I’ll go over MEF soup-to-nuts with an emphasis on isolation and Silverlight applications.  In staying true to the code camp manifesto there will be only a couple of slides and a lot of code.  I hope to see a lot of familiar faces from the metro-Milwaukee area.

If (for some reason) coming to see me won’t get you into a Chicagoland code camp on a Saturday, there’s also the .NET Rocks! Visual Studio 2010 Road Trip.  Come meet Carl and Richard and do a review of the event in order to get a free special edition .NET Rocks! mug.

MEF | Silverlight | WPF


Thursday, April 22, 2010 7:19:38 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Sunday, April 11, 2010

Last year I had a lot of fun doing a little something called the MIX it Up! tour in the Midwest.  While this year we don’t have quite as much of a formal coalition there are still several of us bringing a recap of MIX 2010 to user groups, code camps, and Days of .Net. My first stop this year will be at the Fox Valley .NET User’s Group meeting this week Wednesday.  While there were a lot of great announcements at MIX, I’ll be focusing on Silverlight 4 and Windows Phone 7 Series.

Why not register and come see me?



Sunday, April 11, 2010 8:35:23 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Monday, March 15, 2010

When building a complex data visualization application in Silverlight, I discovered a disturbing issue when dealing with multiple DataGrids on the same screen.  If the ItemsSource of a particular DataGrid gets updated as the result of a SelectedItem change in another DataGrid the child grid would not display any items.  Worse, adding code to query the ItemsSource of the child grid shows that does have a valid and correct ItemsSource, so why doesn’t it display?  In this article we’ll quickly demonstrate the problem and show a solution.

Sample App

I’ve created a simple example with a ‘parent’ DataGrid containing Orders and a ‘child’ DataGrid meant to display the Order Details of the selected Order.  Throw in a small amount of sample data and we wind up with this:

dbf0

You can see that we have selected the first and only row in the top DataGrid and it has a valid OrderDetails property.  Clicking on the Check Items Source button reveals that something should be shown in the bottom DataGrid:

dbf1

So what’s going on?  Let’s take a look at the XAML and data binding code to see if anything is amiss.

Data Binding Code

I have created a simple ViewModel for this screen, with code as follows:

public class ScreenModel : BindableType
{
    public ScreenModel()
    {
        Orders = new ObservableCollection<Order>();
        Orders.Add(new Order());
    }


    private ObservableCollection<Order> _Orders;

    public ObservableCollection<Order> Orders
    {
        get { return _Orders; }
        set
        {
            _Orders = value;
            OnPropertyChanged("Orders");
        }
    }

    private Order _SelectedOrder;

    public Order SelectedOrder
    {
        get { return _SelectedOrder; }
        set
        {
            _SelectedOrder = value;
            //Doesn't work:
            OnPropertyChanged("SelectedOrder");

BindableType implements the basic INotifyPropertyChanged functionality.  Hopefully you agree that this is extremely vanilla code, and that it’s a bit odd that this does not work.  Maybe something is wrong with the XAML?

            <TextBlock>Master Grid</TextBlock>
            <my:DataGrid x:Name="MasterGrid" MinHeight="100"
                         ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder, Mode=TwoWay}" />
            <TextBlock>Detail Grid</TextBlock>
            <my:DataGrid x:Name="DetailGrid" MinHeight="100" ItemsSource="{Binding SelectedOrder.OrderDetails}" />
            <Button x:Name="ChkBtn" Content="Check Items Source" Click="ChkBtn_Click"></Button>

The XAML is extremely basic as well.  How can you fix this?  Searching through forums it seems that when Silverlight 3 was in BETA changing the bottom DataGrid to have a TwoWay binding to its ItemsSource would fix this but I didn’t have luck with this.

Problem Solution

By groping in the dark and changing a single line of code I was able to get the correct behavior:

dbf2

The only code change is this:

private Order _SelectedOrder;

public Order SelectedOrder
{
    get { return _SelectedOrder; }
    set
    {
        _SelectedOrder = value;
        //Doesn't work:
        //OnPropertyChanged("SelectedOrder");

        //Works:
        Deployment.Current.Dispatcher.BeginInvoke(() => OnPropertyChanged("SelectedOrder"));
    }
}

All I’ve done here is to invoke the Property Change Notification for SelectedOrder back onto a Dispatcher thread.  Why is this necessary?  I can’t say for sure.  My guess is that something down in the bowels of Silverlight is attempting to detect some sort of layout cycle or infinite recursion here.  If you find this situating in your code this should work for you.



Monday, March 15, 2010 3:35:22 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [11]  |  Trackback
 Wednesday, March 03, 2010

It’s great that the built-in controls in Silverlight understand INotifyCollectionChanged, and that ObservableCollection<T> ships with Silverlight.  However, when adding large data sets to ObservableCollection<T> the performance  can be brutal due to the fact that ObservableCollection<T> fires an event with every operation on the collection.  Most of the time this is what you want however some bulk operations like AddRange would be nice.  Let’s take a look at a way to add AddRange() to ObservbleCollection<T> using a technique that vastly increases performance.

Example UserControl

We’ll use a simple Silverlight root visual with a DataGrid on it to demonstrate this technique. Note that we are using a new class called SmartCollection<T> and an Item class with some randomly generated field values.

namespace HandWaver.AG.OCAddRange
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            _collection = new SmartCollection<Item>();
            DasGrid.ItemsSource = _collection;            
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Dispatcher.BeginInvoke(() => BuildItems());
        }

        TimeSpan Time(Action a)
        {
            var start = DateTime.Now;
            a();
            var end = DateTime.Now;
            return end - start;
        }

        SmartCollection<Item> _collection;
        
        private void BuildItems()
        {
            Action slow = () =>
            {
                for (int i = 0; i < 25000; ++i)
                {
                    _collection.Add(new Item());
                }
            };

Using this technique and displaying the time it took to add these items to the data grid gives me this result.  Note that Stopwatch is not present in Silverlight 3.

time0

Nearly 10 seconds to add 25,000 items to the DataGrid even though most of them are not visible.  Let’s see what SmartCollection<T> can do with slightly different code:

Action fast = () =>
{                
    var sourceList = new List<Item>();
    for (int i = 0; i < 25000; ++i)
    {
        sourceList.Add(new Item());
    }
    
    _collection.AddRange(sourceList);                
};

var time =
    //Time(slow);
    Time(fast);

So we’ve built a source collection and used the AddRange implementation.  How does it do?

time1

That’s a pretty significant speed up! Let’s go ahead and look at the code for SmartCollection<T> now.

using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace HandWaver.AG.PresentationModel
{
    public class SmartCollection<T> : ObservableCollection<T>
    {
        public SmartCollection()
            : base()
        {
            _suspendCollectionChangeNotification = false;
        }


        bool _suspendCollectionChangeNotification;

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (!_suspendCollectionChangeNotification)
            {
                base.OnCollectionChanged(e);
            }
        }

        public void SuspendCollectionChangeNotification()
        {
            _suspendCollectionChangeNotification = true;
        }

        public void ResumeCollectionChangeNotification()
        {
            _suspendCollectionChangeNotification = false;
        }


        public void AddRange(IEnumerable<T> items)
        {
            this.SuspendCollectionChangeNotification();
            int index = base.Count;
            try
            {
                foreach (var i in items)
                {
                    base.InsertItem(base.Count, i);
                }
            }
            finally
            {
                this.ResumeCollectionChangeNotification();
                var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
                this.OnCollectionChanged(arg);
            }
        }

    }

}

Note that we could do an AddRange implementation via an extension method but we would not gain the performance improvements since events would still fire for every operation.  By extending ObservableCollection<T> we gain the ability to create our own AddRange method.  The AddRange method sets a flag that temporarily suppresses the collection changed notification for ObservableCollection<T>.   At the end it uses NotifyCollectionChangedAction.Reset to indicate that a significant change was made to the collection.  The UI rebuilds itself in a fraction of the time it took to add the items one by one.  You can use this technique to gain huge performance improvements for your Silverlight 3 apps dealing with large data sets.



Wednesday, March 03, 2010 7:43:44 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [7]  |  Trackback
 Monday, March 01, 2010

I have the privilege for the second year in a row of speaking at the Fox Valley Day of .NET.  Last year the event was a little later in the year (or MIX was earlier?) and I gave the keynote as part of the MIX It Up! tour.  This year I am actually going to MIX so maybe I’ll have to come back.

I will be giving the MEF talk I mentioned not too long ago and while I will have some good Silverlight content this is more about MEF itself.  The guys up there are gracious hosts and I am looking forward to getting up there and sharing one of my favorite new technologies.

If MEF is not your thing, please consider seeing my colleague Christopher Barwick’s talk on Business Intelligence: “BI: from Database to Warehouse  to OLAP to Excel with SQL Server 2008 & Excel 2007” running in the same time slot as my presentation.  Chris has really been stepping up his involvement in the community and he has a lot to share.

For my part I will be watching Dave Bost talk about Silverlight 4 right after my talk.  While I imagine I’ve already consumed this content I can’t get enough Silverlight and Dave is a good presenter so I’ll be sitting in.

Please check out their site and come see us!  They have a lot of swag this year so your chances of coming home with something extremely useful are very good.



Monday, March 01, 2010 8:40:32 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [17]  |  Trackback
 Saturday, February 27, 2010

If you’ve ran into me at a community event lately you may have caught me saying something along the lines of working too many hours.  You no doubt felt bad for me and wished you could do something, anything, to help me in my predicament.  Most of the time your sympathy came in the form of beer, which is greatly appreciated.  However if you really want to contribute to the Save Damon Foundation you should come and work for me!

Ok, bad jokes aside, we are hiring for my team.  We currently have offices in Waukesha (WI), Nashville, and Atlanta, although people have worked remotely from all over the place in the past.

What would you be working on?  A large family of applications with terabytes of data.  Our current process and technology mix looks something like this:

  • Asp.net
  • jQuery
  • WCF
  • All Microsoft application stack
  • Team System
  • Scrum
  • Silverlight 3
  • Silverlight 4
  • MEF
  • Unity
  • PRISM
  • AbunchofotherstuffIcanthinkofrightnow

Leave me a comment or contact me for more details.



Saturday, February 27, 2010 2:13:21 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [12]  |  Trackback
 Sunday, February 14, 2010

I am preparing a talk for the spring code camps and conferences.  Would you go see this talk?

What can I do with MEF?

The Managed Extensibility Framework has left excitement and confusion in its wake as .NET 4 moves towards release. What can I use this for? What should I use this for? Is this another IoC/DI container? What about Unity? What about the System.AddIn namespace? In this talk we will explain the workings of MEF using straightforward examples and then dig deep into the real power of MEF using examples including MEF for Silverlight, MEF for WPF, and MEF for Enterprise services.

Leave a comment, and thanks!



Sunday, February 14, 2010 2:56:56 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [23]  |  Trackback

Starting tomorrow I’ll be attending the 2010 Global MVP Summit in the Seattle area.

After working way too hard since, well, pretty much since July of 2009, I will be taking some me time.  Not everyone would consider this kind of event a break but for me this is as exciting as an exotic vacation.  Of course I can’t talk about anything that’s going on during this week, so to the untrained eye following me on twitter it may appear that I am merely goofing off and drinking beer for five days.  In the past I have always found events like this to be a mix of actual learning value and chance to take one’s self out of the daily grind of deadlines and features to gain perspective and recharge.  While I expect this to be the most learning-heavy event I’ve ever attended I’ve also really never needed to recharge as badly as I do right now.  Between the actual session content, putting faces with virtual names, and the organized social events it looks like sleep is going to be hard to come by.  This means my recently worsening insomnia may prove to be a valuable (well, OK, tolerable) asset.

Work and personal factors have kept me from writing much for the past month (since December, really) so I have three personal technical goals for this week as well.  I hope to have time to hack on some personal items as well.

I’ll be using the twitter tag #mvp10.  Here’s to a great geek holiday.

MVP2010 | Personal | Rant | Silverlight | WPF


Sunday, February 14, 2010 2:24:08 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [10]  |  Trackback
 Saturday, January 30, 2010

I haven’t decided where I’m staying yet, but I will be attending MIX 2010 March 15th – 17th.  Are you going to MIX? Drop me a comment!  I will likely be accompanied by someone from BigHammer but I’m always looking for other people to drink beer learn with!  I’ll obviously be leaning heavily towards the Silverlight sessions.  If MIX 2009 is any indication there should be some great content in March. 

Of course, I am hoping to finally see a completely awesome Windows Mobile 7 announcement.  Please?



Saturday, January 30, 2010 2:53:02 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Monday, December 07, 2009

Silverlight 3 ships with out of browser support which is a particularly useful feature.  Combining OOB with the ability to detect network status can allow your users to consume your Web app without a connection.  Combining both of these features with Isolated Storage and the right design approach can provide a seamless Occasionally Connected application experience using Silverlight 3.

Wine Research

I will need another sample application to demonstrate these concepts, this time I’m going with HandWaver Wine Research:

wineresearch0

When we navigate to [Questions] we’re going to see a list of questions about things like Wine/Food pairing and similar topics; the questions were submitted using another system.  The green orb in the upper right hand corner indicates the app is online and connected.  I have also included a button to toggle the online/offline status which is easier than pulling my plug or disabling my network device in Windows.  I will be using the HandWaver.AG Silverlight Guidance project from previous articles.

Application Goals

We only have a couple of functions for this application.  We need to read questions people send us about wine, answer those questions, and send a message to the question asker.  Because some of these questions may take careful thought and I don’t always have an internet connection, I want the application to seamlessly work no matter my connection state.  I have only two modules, abstracted behind interfaces, for accomplishing this: IQuestionRepository and IResearchMessageSender.  I would like to use a dependency-injection like approach for configuring these modules, but I must be able to react to environmental changes as well.

Initializing MEF

MEF is much more than an IoC container and in this article we will explore one of the more intriguing features.  Recomposition in MEF is easy to turn on, not straightforward to get working, and you must design for it.  This should not be taken as a general introduction to MEF but we can review:

MEF works by satisfying [Import]s with matching [Export]s.   Instances with either Imports or Exports are typically called Parts in MEFspeak.  Parts can either be handed to MEF of discovered from some type of Catalog.

Since we have two different parts which need to change in concert, the idea of a Catalog works well here.  I am using a Presenter combined with ViewModel to drive the question/answer screen.  The presenter utilizes MEF to satisfy module needs as follows.

namespace HandWaver.AG.WorkOfflineDemo.PresentationModel
{
    public class ResearchPresenter : IPartImportsSatisfiedNotification
    {
        public ResearchPresenter(IResearchView v)
        {
            _view = v;
        }

        IResearchView _view;        
        IQuestionRepository _qRepo;

        [Import(AllowRecomposition = true)]
        public IQuestionRepository QuestionRepo
        {
            get { return _qRepo; }
            set
            {
                _qRepo = value;
                if (null != _view.ViewModel)
                {
                    _view.ViewModel.QuestionRepo = _qRepo; 
                }
            }
        }

        [Import]
        public NetworkStatusMonitor NetworkStatusMonitor { get; set; }

        public void OnImportsSatisfied()
        {
            NetworkStatusMonitor.GoOnline += new Action(NetworkStatusMonitor_GoOnline);
            NetworkStatusMonitor.GoOffline += new Action(NetworkStatusMonitor_GoOffline);
        }


        public void InitView()
        {
            var mdl = new WineResearchViewModel();
            QuestionRepo.FindUnanswered((qs) =>
            {
                mdl.UnansweredCount = qs.Count;
                mdl.Questions.Clear();
                mdl.Questions = new ObservableCollection<Question>();
                qs.ForEach((q) => mdl.Questions.Add(q));
            });

            mdl.QuestionRepo = QuestionRepo;

            _view.ViewModel = mdl;            
        }

        public void SaveSelected()
        {
            var q = _view.ViewModel.SelectedQuestion;
            QuestionRepo.MarkAnswered(q, () => _view.ViewModel.Questions.Remove(q));
        }

There are two Import definitions for this class.  One is a NetworkStatusMonitor that tells us when connection status changes using the baked-in Silverlight 3 APIs and uses the bare Import attribute.  The second is more interesting as it also sets AllowRecomposition to be true.  What this tells MEF is that we can re satisfy this import later if the situation arises.  This will help us meet our goals if we design carefully: when online, I’ll use a WCF based implementation of IQuestionRepository, when offline I’ll save messages to Isolated Storage to be forwarded on later.  Because I’m setting the current IQuestionRepository as a property of the ViewModel I can actually see this at work:

OnlineQuestionRepo

OfflineQuestionRepo

In each case, note the area highlighted in red and the green/red orb indicating connection status .  How did we accomplish this?

Groups of Services and Re Composition

The high level static structure for the different pieces needed to make this work is pictured here. 

staticstructure

Some extra work is needed to translate this into something MEF can help with.  MEF provides Catalogs, Imports, Exports, and of course the CompositionContainer however the out of the box components did not quite do what I wanted in a straightforward way so some extensions were required.  Let’s walk through the code needed to let ServiceFactory switch between OnlineServices and OfflineServices.

When first starting, we setup the MEF container based on the current connection status:

public class ServiceFactory
 {
     [Import]
     public NetworkStatusMonitor StatusMonitor { get; set; }

     [Import]
     public CompositionContainer Container { get; set; }

     IServiceGroup _services;

     public void Initialize()
     {
         if (StatusMonitor.IsOnline)
         {
             _services = new OnlineServices();
         }
         else
         {
             _services = new OfflineServices();
         }
         AddAll(_services);

AddAll performs the initial composition based on whichever group of services (Modules) were applicable at the time.  Things get interesting when we need to recompose based on an environment change.

void StatusMonitor_GoOnline()
{
    var oldSvc = _services;
    _services = new OnlineServices();
    RecomposeAll(oldSvc, _services);
}

void StatusMonitor_GoOffline()
{
    var oldSvc = _services;
    _services = new OfflineServices();
    RecomposeAll(oldSvc, _services);
}

The intent should be clear at this point.  We are going to remove the “old services” from the Composition and add the “new services” to the composition.  The rest of the code here is a little bit more verbose than need be in order to make it very clear what’s going on.

void RecomposeAll(IServiceGroup old, IServiceGroup @new)
{
    var exportQR = GetDefaultExport(typeof(IQuestionRepository), old.QuestionRepo, false);
    var exportIRM = GetDefaultExport(typeof(IQuestionRepository), old.ResearchMessageSender, false);

    var addQR = GetDefaultExport(typeof(IQuestionRepository), @new.QuestionRepo, true);
    var addIRM = GetDefaultExport(typeof(IResearchMessageSender), @new.ResearchMessageSender, true);

    var batch = new CompositionBatch();
    batch.RemovePart(new SingleExportPart(exportQR, old.QuestionRepo.Name));
    batch.RemovePart(new SingleExportPart(exportIRM, old.ResearchMessageSender.Name));

    batch.AddPart(new SingleExportPart(addQR, @new.QuestionRepo.Name));
    batch.AddPart(new SingleExportPart(addIRM, @new.ResearchMessageSender.Name));
    
    Container.Compose(batch);
}

In each case, we must provide enough information for the CompositionContainer to:

  1. Find the currently exported Part for a given contract
  2. Create a ComposablePart that maps to each
  3. Create new Exports for the new Parts
  4. Create a ComposablePart for the new Exports
  5. Create a CompositionBatch to do the Add/Remove as an atomic operation

I found that the code below was needed in order to successfully accomplish the aim.

protected Export GetDefaultExport(Type contractType, Type implType, bool add)
{
    string contractName = contractType.ToString();
    var metadata = new Dictionary<string, object>();
    metadata.Add("ExportTypeIdentity", contractName);
    Func<object> valueFinder = null;

    if (add)
    {
        valueFinder = () => Activator.CreateInstance(implType);
    }
    else
    {
        valueFinder = () => (from p in Container.Catalog.Parts where p.Metadata["ExportTypeIdentity"] == contractType select p).FirstOrDefault();
    }
    var export = new Export(contractName, metadata, valueFinder);
    return export;
}

/// <summary>
/// A simple container to work with MEF container constructs
/// </summary>
public class SingleExportPart : ComposablePart
{
    public SingleExportPart(Export e, string implVal)
    {
        _export = e;
        _implVal = implVal;
    }

    Export _export;
    string _implVal;

    public override IEnumerable<ExportDefinition> ExportDefinitions
    {
        get { return new ExportDefinition[] { _export.Definition }; }
    }

    public override IEnumerable<ImportDefinition> ImportDefinitions
    {
        get { return Enumerable.Empty<ImportDefinition>(); }
    }

    public override object GetExportedValue(ExportDefinition definition)
    {
        return _export.Value;
    }

    public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
    {
        
    }
    public override IDictionary<string, object> Metadata
    {
        get
        {
            return new Dictionary<string, object>();
        }
    }

    public override bool Equals(object obj)
    {
        return GetHashCode().Equals(obj.GetHashCode());
    }

    public override int GetHashCode()
    {                
        return ("SingleExportPart" + _implVal).GetHashCode();
    }

}

Without the source for the MEF Preview I would have had a more difficult time with this.  It seems like it should be easier to remove Parts from the composition.  To be fair, this is easier if you are using [ImportMany] instead of [Import].  The final part needed to tie everything together is to add a PartCreationPolicy to the Parts being added & removed from the composition batch:

[Export(typeof(IQuestionRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class QuestionRepository :   IQuestionRepository
{

Without specifying the NonShared CreationPolicy MEF will by default create a single instance of the Part.  When trying to remove the part from the CompositionContainer MEF will throw an exception basically stating that the Part couldn’t be removed because it was still being used elsewhere to satisfy another Import.

Further Research

The example shown here is somewhat incomplete.  Because service calls in SilverLight are always asynchronous, we would probably want some thread safety as we swap Imports in and out.  It would also be appropriate to enhance the interfaces used here to notify Parts as they are being added/removed such that the Offline/Online families of services can do appropriate cleanup.



Monday, December 07, 2009 8:22:31 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Wednesday, November 25, 2009

Before PDC 2009, I had a short wish list for the next version of Silverlight:

  1. Real support for commanding
  2. Some kind of full trust mode similar to Click Once
  3. More complete data binding
  4. Context Menus
  5. Relational (Isolated) Storage

I was pretty excited as The Gu’s keynote played out and I saw my wish list filled one item at a time.  There are some other things I’d like, but these are things that aren’t in WPF either so I don’t really dare hope for them.  These items include things like adding extensibility to the Binding framework: too many aspects are internal/sealed black boxen.

The missing #5 would probably have involved something like a cross platform SQL Compact.  The ability to drop a small relational store into Isoloated Storage (or anywhere on the file system using elevated trust) would open up a whole new avenue for Silverlight applications.  Even without tools like the Entity Framework this could be huge.

I recalled my Return of the Smart Client article where I expressed what I believe are common concerns and frustrations developers have when building Enterprise applications using the usual web development tools.  If you needed further convincing that Silverlight is a real alternative for creating browser delivered rich internet applications you need only review day two of the PDC 2009 keynote.  Silverlight is as big as anything going on at Microsoft.  I can’t wait for MIX 2010!



Wednesday, November 25, 2009 2:36:02 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Thursday, November 05, 2009

In just over a week, on November 14th, I will be speaking at the Chippewa Valley Code Camp taking place way up in Menomonie, WI on the UW-Stout campus.  The topic is Advanced Silverlight Development however the content will be somewhat different than my talk by the same name I gave at MadCamp a little while ago.  Doug and company run a healthy community so I’m looking forward to good conversations.

Details on the code camp can be found at: http://www.chippewavalleycodecamp.com/ 

The schedule of topics can be found here: http://www.chippewavalleycodecamp.com/Schedule/2009Schedule/tabid/74/Default.aspx

The elusive Christopher J. Barwick will be accompanying me and making his return to Public Nerd Life.  I am looking forward to seeing Jason Bock and Chris speak, and I’ll probably check out some of the non-Microsoft or Building Better Software topics in between.  We’ll be up the night before and open to grabbing a drink with any local geeks or speakers who may be available.



Thursday, November 05, 2009 10:18:41 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Wednesday, October 21, 2009

This may be obvious to some, but by calling out to JavaScript within the document hosting your Silverlight application, you can do some powerful things.  Back in the day, we used to sometimes use an href with “mailto” as a poor man’s contact form – hosting solutions with databases and backups weren’t always as cheap and plentiful as they are today.  If you lack a Service Layer to email solution in your environment, you can always try the following:

private void Mailto_Click(object sender, RoutedEventArgs e)
{
    var svc = new Modules.BrowserHostService();
    svc.CallHostFunction("MailTo", "damon@damonpayne.com", "Damon", "Try this Wine!", 
        "Roger Sabon Chateauneuf du Pape 2005", "11F16974-8EE1-4c0c-8AB4-21EF0B7BF087");
}

Where BrowserHostService is part of a demo that I’m working on.  The implementation is very simple:

public class BrowserHostService : IHostService
{
    public void CallHostFunction(string func, params string[] args)
    {
        HtmlPage.Window.CreateInstance(func, args);
    }

This method does everything needed to invoke a JavaScript method on the document containing the Silverlight application.  For invoking the user’s default mail program, you could write a function like what’s shown below.  The only item of note is that the parameter orders must match up.  If you’ve done much Reflection coding you should be used to this.

<script type="text/javascript">
    function MailTo(toEmail, fromUser, subject, suggestTopic, suggestCode) {

        var body = fromUser
        + ' thought you would really enjoy '
        + suggestTopic 
        + ' and there is some info about that on TastingProject.com.   '
        + 'Why not head over to http://localhost:5785/TastingProject.UITestPage.aspx?code='
        + suggestCode 
        + ' and check it out?';

        window.open('mailto:'+ toEmail +'?subject=' + subject+'&body='+body);
    }        
</script>

The preceding C# and JavaScript produces the following result on my machine in Outlook:

AGmailto

Of course you would have to rely on your end user to hit the Send button but this may be a quick and dirty way to get email functionality if you don’t have complete control over your environment.



Wednesday, October 21, 2009 10:24:18 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, October 14, 2009

On Saturday, October 24th I will be speaking at the Madison .NET User’s Group’s annual Code Camp: MadCamp.  You can find general information about MadCamp here. My presentation will be on Advanced Silverlight Topics.  They are going to be giving away a lot of cool stuff, particularly around Windows 7.  The group has combined the code camp event with a Microsoft sponsored Windows 7 launch party so it should be pretty sweet.  I will be tweeting on the subject using #madcamp09 and I hope to see you there



Wednesday, October 14, 2009 7:36:51 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, October 04, 2009

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

What’s in a name?

If you have worked in the Windows Forms designer, ASP.Net ViewState, ASP.Net Control Templates, or ever used a design surface of any kind you are likely familiar with the concepts of a Naming Container and Name Providers.  The first Label I drag onto a form might get a name of “Label0”, then “Label1” and so on.  Many visual technologies will throw an exception if two items inside the same container have the same name.  We will need this idea for AGT very soon, so now is the time to implement it.

Design Goals

The design goals for this next step are fairly simple:

  1. We must have a notion of A Container.  This container has children, this container will use some sort of Name provider to provide a default name for each new Child added to it.
  2. The Type of the children should be inspect able, such that we can supply names like “Foo0”, etc.
  3. We do not need to support a parent-child relationship between Containers (like asp.net does).
  4. We should let the Caller set the name on its child

The only remaining decisions are small but important. 

  1. Is there any value to having an INamingContainer interface or can we use an existing mechanism to expose children?
  2. Is there any value to having an INameable interface for items to be named?

I’m going with “yes”, and “no”, respectively.

INamingContainer

I’m going to create a simple INamingContainer interface which can be implemented by containers (like a design surface) that want their users to have a unique name.  I could make this part of the IDesigner interface but I’d like this to be optional.  I am still in my IDesignableControl-centric world though.

namespace DamonPayne.AGT.Design
{
    /// <summary>
    /// Represents something that has children needing unique names
    /// </summary>
    public interface INamingContainer
    {
        IList<IDesignableControl> Children{get;}
    }
}

This is all we need, we can now enumerate the children and generate a unique name.

INameProvider

The interface for INameProvider is equally simple.

namespace DamonPayne.AGT.Design.Contracts
{
    public interface INameProvider
    {
        string GetUniqueName(INamingContainer container, IDesignableControl newChild);
    }
}

As I mentioned before, I’d like to follow the familiar convention of ControlType[number], so I’m ready to build a default implementation for INameProvider.  This is pretty easy using LINQ.

namespace DamonPayne.AGT.Design.Services
{
    /// <summary>
    /// A name provider that uses the last part of type name plus 
    /// </summary>
    public class TypeScopedNameProvider : INameProvider
    {
        public string GetUniqueName(INamingContainer container, IDesignableControl newChild)
        {
            var type = newChild.GetType();
            string namePart = type.Name;
            int existingCount = (from c in container.Children where c.GetType() == type select c).Count();
            return namePart + existingCount;
        }
    }
}

 

Hooking up DesignSurface

In order to finish up, I only need to hook my implementation up using Unity and call the name provider whenever a new item is added to the DesignSurface.

[Dependency]
public INameProvider NameProvider { get; set; }

 

private void EnsureName(IDesignableControl dc)
{
    if (string.IsNullOrEmpty(dc.DesignTimeName)) 
    {
        string name = NameProvider.GetUniqueName(this, ((DesignSite)dc).HostedContent);
        dc.DesignTimeName = name;
    }  
}

I can drag things out without a name now, and everything looks the way it should with the generated naming.  My glorious purple couches and poorly drawn speakers have names!  Now that I’ve been learning Expression Design I should really take another crack at drawing those speakers.

21ss0

I’ve really got to fix the way that PropertyGrid looks too.

Conclusion

The Service/Module based approach continues to prove itself out as the correct approach for making it easy to add new functionality to the system.  Adding a name provider was a small feature, but the next step won’t be so easy.

You can get the source code from codeplex.  This article represents change set 28479.

The live demo has been updated and can be viewed here.



Sunday, October 04, 2009 8:08:07 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Thursday, September 24, 2009

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

Atrophy

As of AGT[19] I was getting ready to extend Commanding and introduce some other concepts in order to get Undo/Redo support.  Of course, that was many months ago and I’ve let this sit for a while haven’t I?  Since that time a lot has happened.  Silverlight 3 has shipped, there are many more class libraries available, and Silverlight in general is being taken more seriously.  It’s time to continue working on the design surface project.

Ok, fine, I also need AGT for some other projects.  I need to take some time to bring the project up to the state of the art and change direction slightly to move the road map around to be more in line with my own short term needs.  Let’s get started.

If you’re new to this project, you can also check out the Codeplex site.

Silverlight 3 Upgrade

Since I’ve been through a machine repave or two since my last work I had to get Team System set back up for Codeplex.  I didn’t realize this until after I’d opened the solution and done the conversion wizard so I was concerned.  Sure enough, the project upgrade hozed the source control bindings but I was able to get it fixed using the Change Source Control command.  With this done I built and ran with no problems, however trying to change anything revealed that source control was most likely irrevocably ruined so I got latest and went back to work.

Once I got through all this, the Silverlight 3 upgrade went off without a hitch.  I found exactly zero breaking changes in the application so far; this is much nicer than some of the previous Silverlight 2 cycles I had gone through.

While no changes were strictly necessary, I had to think about taking advantage of new Silverlight 3 features.  I quickly cycled through my mental list of new Silverlight 3 features.  I may use things like Behaviors in the future.  One thing I wasn’t thrilled with from Silverlight 2 was the copy/scale/glass approach I had to take for creating my drag & drop “thumbnails” of controls.  I sat down to see how much work it would take to create an IDragTypeCreator implementation using WritableBitmap.  WriteableBitmap still has some bizarre behavior in some situations so unfortunately I had to stick with the old implementation.

Refactoring

Pretty much any time I look at old code, I’m eager to apply whatever I’ve since learned to it.  AGT is no exception.  In order to get my head back into this codebase I needed to come up with a refactoring goal that would cause me to review everything…

Moving to Unity

Now that Unity for Silverlight is in good shape, there’s no reason for me not to use it.  I have now taken a dependency on Unity 1.2.  It was hard to say goodbye to my own IoC container but it needed to be done.  Were there any issues in this upgrade?  Oh, you might say so…

  1. I removed my IService interface and replaced with Unity [Dependency] attributes.  This of course involved touching nearly every file in the project.  Find an IService interface, find the implementing classes and the importing classes; lather, rinse repeat.  This was super-painful.

  2. I decided to keep my own EventAggregator for now.

  3. I am also keeping my lame Region Manager for now

  4. The old Creative Commons declarations were removed since AGT is really MS-PL now.

While this resulted in a massive change set, it felt good.  This was a very eye-opening experience:

Changing your IoC container might mean touching most of the files in your solution!

Design Surface

Once I got the solution back up and running with Unity and Silverlight 3, I started looking at areas where I could clean up the design.  The DesignSurface implementation is only 300 lines right now, but is also likely to grow in the future.  I decided to use a concept from another article to encapsulate drawing on the design surface.

namespace DamonPayne.AGT.Design.Behaviors.Drawing
{
    public abstract class DrawingBehavior
    {
        public DrawingBehavior(Canvas surface)
        {
            Surface = surface;
        }

        /// <summary>
        /// The surface we're drawing on
        /// </summary>
        public Canvas Surface { get; private set; }

        /// <summary>
        /// Mouse click point relative to Surface    
        /// </summary>    
        public Point StartPoint { get; set; }

        public abstract void StartDrawing(Point s);

        /// <summary>    
        /// Give derrived classes a chance to determine if they can or should return a valid     
        /// Shape in their current state    
        /// </summary>    
        /// <param name="g"></param>    
        /// <returns></returns>    
        public virtual bool ShouldStopDrawing(DrawingGestures g)
        {
            return (g == DrawingGestures.MouseLeftButtonUp);
        }

        /// <summary>    
        /// Stop drawing and return the final Shape    
        /// </summary>    
        /// <returns></returns> 
        public abstract Shape StopDrawing();


        /// <summary>    
        /// Pass Mouse events through to the DrawingBehavior    
        /// </summary>    
        /// <param name="e"></param>    
        public abstract void MouseMove(MouseEventArgs e);
    }

}

If you are a long-time follower of this series or you’ve read the road map on Codeplex, you’ll know that I intend to have an extensible behavior mechanism for making things happen on the design surface.  DrawingBehvior is the first step in that direction, and also allows me to take a lot of code out of the design surface and move it to a dedicated construct.

namespace DamonPayne.AGT.Design.Behaviors.Drawing
{
    public class RectangleDrawingBehavior : DrawingBehavior
    {
        public static readonly double SELECT_RECT_STROKE_WIDTH = 3.0;
        public static readonly double SELECT_RECT_RADIUS_X = 0.0;
        public static readonly double SELECT_RECT_RADIUS_Y = 0.0;

        public RectangleDrawingBehavior(Canvas surface) : base(surface) { }

        public Rectangle DrawingRect { get; set; }

        public override Shape StartDrawing(Point s)
        {
            StartPoint = s; 
            DrawingRect = new Rectangle { Width = 5.0, Height = 5.0, };
            ApplyShapeStyle(DrawingRect);
            DrawingRect.SetValue(Canvas.LeftProperty, s.X);
            DrawingRect.SetValue(Canvas.TopProperty, s.Y);
            Surface.Children.Add(DrawingRect);
            return DrawingRect;
        }

        public override Shape StopDrawing()
        {
            var rVal = DrawingRect;
            DrawingRect = null;
            return rVal;
        }

        public override void MouseMove(MouseEventArgs e)
        {
            Point localMousePos = e.GetPosition(Surface);
            if (StartPoint.X < localMousePos.X)
            {
                double width = localMousePos.X - StartPoint.X;
                double height = localMousePos.Y - StartPoint.Y;
                if (width > 0 && height > 0)
                {
                    DrawingRect.Width = width;
                    DrawingRect.Height = height;
                }
            }        
            else // northwest drag        
            {            
                double width = StartPoint.X - localMousePos.X;
                double height = StartPoint.Y - localMousePos.Y;
                if (width > 0 && height > 0)//need this safety here in case a resize rectangle "crosses" itself.           
                {
                    DrawingRect.Width = width;
                    DrawingRect.Height = height;
                    DrawingRect.SetValue(Canvas.LeftProperty, localMousePos.X);
                    DrawingRect.SetValue(Canvas.TopProperty, localMousePos.Y);   
                }       
            }         
        }

        public virtual void ApplyShapeStyle(Shape s)
        {
            DrawingRect.StrokeThickness = SELECT_RECT_STROKE_WIDTH;
            DrawingRect.RadiusX = SELECT_RECT_RADIUS_X;
            DrawingRect.RadiusY = SELECT_RECT_RADIUS_Y;
            DrawingRect.Stroke = new SolidColorBrush(Color.FromArgb(0xFF, 0x15, 0x05, 0xFF));
            DrawingRect.Fill = new SolidColorBrush(Color.FromArgb(0xFF, 0x49, 0x59, 0xFF));
            DrawingRect.Opacity = .50;
            DrawingRect.SetValue(Canvas.ZIndexProperty, 1000);
        }

    }
}

Now the design surface code is a bit smaller.  The design surface is still assuming the selecting shape is a rectangle but I can fix that in the future.  I will probably also make the selection strategy pluggable at some point. 

Conclusion

Since there are no functional changes I have not updated the live demo.  You can get the most recent code from codeplex.  The final change set as of the end of this article is 28467.  The next AGT few articles will bring more interesting features, and yes I will get back to the Undo framework eventually.



Thursday, September 24, 2009 8:00:22 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [13]  |  Trackback
 Monday, September 21, 2009

In my last article on View-based navigation, I mentioned that a useful Navigation History module could be built to work in concert.  This article discusses the design and implementation of that module. In this case my design goals are more straightforward:

  1. Allow the size of the navigation history to be set so as to control memory usage
  2. Allow going forwards and backwards in the history
  3. Allow inspection of the navigation history
  4. Allow aspects of navigation history to be bindable

Navigation History

As with the core navigation concepts I went over previously, there is some basic Navigation History built into the Frame class in Silverlight 3.  Since I’ve extended the core navigation concepts I can also add additional hooks for navigation.  As usual, I will first think of my design goals and craft an interface.

public interface INavigationHistory : INotifyPropertyChanged
{
    /// <summary>
    /// How many Views do we want to keep around?
    /// </summary>
    int HistorySize { get; set; }

    /// <summary>
    /// Go back one View, return the next active view
    /// </summary>
    IView GoBack();


    /// <summary>
    /// Go forward one view, return the next active view
    /// </summary>
    /// <returns></returns>
    IView GoForward();

    /// <summary>
    /// Can we go back based on current position?
    /// </summary>
    bool CanGoBack{get;}

    /// <summary>
    /// Can we go forward based on current position
    /// </summary>
    bool CanGoForward{get;}

    /// <summary>
    /// Called by clients to add a view to history
    /// </summary>
    /// <param name="v"></param>
    void Navigated(IView v);

    event Action<IView> NavigatedToView;

    event Action<IView> HistoryItemRemoved;

    /// <summary>
    /// Get the items in the current history, allow a jump to a specific point
    /// </summary>
    ObservableCollection<IView> CurrentHistory { get; }
    

    /// <summary>
    /// Go to a specific View in the history
    /// </summary>
    /// <param name="historyIndex"></param>
    void GoTo(IView v);

    /// <summary>
    /// Removes the most recent item
    /// </summary>
    /// <returns></returns>
    IView Pop();
}

Including INotifyPropertyChanged here means aspects of the navigation history will be bindable: CanGoBack, CanGoForward, and CurrentHistory.  The other parts of the interface should be self explanatory.

Binding to History

Using the previous Audi Fans demo, I can now make the navigation buttons in the lower right hand corner work.

navhist0

The XAML for creating these buttons is fairly simple, note the {Binding} aspects:

<StackPanel Orientation="Horizontal" Grid.Row="2" Margin="5" HorizontalAlignment="Right">
    <TextBlock Style="{StaticResource TextStyle}" >Navigation: </TextBlock>
    <Button x:Name="GoBack" IsEnabled="{Binding Path=History.CanGoBack}" Click="GoBack_Click">
        <Button.Content>
            <TextBlock Style="{StaticResource TextStyle}" Foreground="Black">&lt; Back</TextBlock>
        </Button.Content>
    </Button>                    
    <ComboBox x:Name="JumpCmb" DataContext="{Binding}" ItemsSource="{Binding Path=HistoryItems}" SelectedItem="{Binding Path=CurrentViewName, Mode=TwoWay}" Margin="10,0,10,0" Width="250" >                        
    </ComboBox>                     
    <Button x:Name="GoForward" IsEnabled="{Binding Path=History.CanGoForward}" Click="GoForward_Click">
        <Button.Content>
            <TextBlock Style="{StaticResource TextStyle}" Foreground="Black">Forward &gt;</TextBlock>
        </Button.Content>
    </Button>
</StackPanel>

In order to satisfy these bindings, the Navigation History implementation has been added to the MainViewModel for the front page.  You will note that I’m not binding directly to the History.CurrentHistory but that some additional fields have been added to MainViewModel:

ObservableCollection<string> _histItems;

/// <summary>
/// Silverlight crashes if you use a Page (IView) as a binding source.  Lame.
/// </summary>
public ObservableCollection<string> HistoryItems
{
    get
    {
        return _histItems;   
    }        
}


string _currentViewName;

public string CurrentViewName
{
    get { return _currentViewName; }
    set
    {                
        if (value != _currentViewName)
        {
            _currentViewName = value;
            OnPropertyChanged("CurrentViewName");

            if (_viewMap.ContainsKey(value))
            {   
                Navigation.GoTo(_viewMap[value]);
            }
        }
    }
}

Pit of failure: Silverlight does not seem to like having a Page object as a binding source except when doing Element binding.  This may be a low level implementation detail that’s just not supported, or it may be because the Pages in question are not visible and this causes some fundamental failure.

So there is a layer that maps IView names back to the INavigationHistory because Silverlight binding doesn’t like binding to a visual element that is not currently visible.

Conclusion

That’s pretty much it for Navigation History, be sure to read the previous article if you’d like to see how it fits in with everything else.  There are other directions you could take this, obviously.  If you are using some kind of Region manager you could scope the navigation to each region.  If you are concerned about memory usage, you could create a scheme to use WeakReferences and provide hooks to save just the ViewModel of an IView so that the IView itself could be reconstructed later. 

The updated source code for the HandWaver.AG guidance projects can be found here.



Monday, September 21, 2009 6:17:04 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [8]  |  Trackback
 Wednesday, September 16, 2009

 

Silverlight 3 ships with a Navigation mechanism.  A control that extends Page can be stored inside the Frame control as content, and Navigation from Page to Page is done by specifying the URI of the target Page.  This mechanism includes navigation history and the ability to go forwards and backwards through said history.  I would, however, like a slightly different programming model around application navigation.

Design goals:

  1. I would like to be able to navigate to a new “screen” from within various places such as Presenters or Commands
  2. I do not want to have to configure each new View I add to the system
  3. I don’t want to have to map Views to URIs to use the Frame
  4. I need to be able to pass parameters to Views as they are navigated to
  5. I need to support having more than one View implementation in the system for a given View interface type
  6. I need an inspect-able navigation history that I can also configure in terms of how much history to keep and so forth
  7. I would like to support animated transitions from View to View, this is Silverlight after all

My sample application will be an “Audi Fan Site” in Silverlight.

Views & ViewModels

I have a Silverlight “guidance” Solution called HandWaver.AG; I will use some concepts from this solution to build the navigation framework and post the entire solution’s source code. 

Pattern: For separation of concerns, I’ll use the Model-View-ViewModel pattern.

I’ll start with a humble IView base interface:

namespace HandWaver.AG.PresenationModel
{
    public interface IView
    {
        string Title { get; }
        void Activate(Action complete);
        void DeActivate(Action complete);
    }
}

Nothing complicated here.  The Activate and Deactivate methods can be called to inform a IView that it is about to be “started” or shut down.  The Action callbacks will facilitate animation later.

Now I need a ViewModel base class that does a little bit of change notification work for me.

namespace HandWaver.AG.PresenationModel
{
    public abstract class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

I will expand on these concepts in a little bit, for now assume that I will have Controls of some kind that implement IView and bind to ViewModels.

Navigation

For the most simple case, I want to be able to just go to an IVIew.  I’ve created an INavigationService interface.  This is the calling convention that I’ll use.

namespace HandWaver.AG.PresenationModel.Navigation
{
    public interface INavigationService
    {
        /// 
        /// Display the first IView implementation we find
        /// 
        /// 
        void NavigateTo< T >() where T:IView;

At this point I can go ahead and create my sample application and my “Home” screen.  I’m going to re-use the Frame and Page concepts from Silverlight.  I have some navigation buttons that are disabled, a ComboBox that will contain my navigation history, and some HyperlinkButtons up at the top to link to the various IViews I will create.

navapp0

The grey area is my navigation Frame.  What I want to do now is Navigate to the Home screen when the app is loaded.  As you can see from the INavigationService code above, I’m just going to do this:

Navigation.NavigateToIHomeView>();

Adding this now displays my Home screen in the navigation frame…

navapp1

… but how did we get there?  The first item is the discovery of IViews.

Discovering Views

In order to meet my design goals, I need to figure out how to find IView implementations automatically without having to create some kind of View Registry.  In my implementation of the INavigationService interface, reflection is the key.  It’s worth going over this code as everything else depends on it:

private void MapViewTypes()
{
    _viewMap = new Dictionary<Type, List<Type>>();
    //Note: we're making a giant assumption here that all Views are in this assembly.  MEF could help?
    
    var types = _viewAssembly.GetTypes();
    var viewType = typeof(IView);
    var pageType = typeof(Page);

    foreach (var t in types)
    {
        if (t.ImplmentsInterface(viewType) && t.IsSubclassOf(pageType))
        {
            var viewImplType = t;
            //if we have a sub-class of IView, use that instead
            var subViewType = (from v in viewImplType.GetInterfaces() where v.InterfaceExtends(viewType) select v).FirstOrDefault();
            if (null != subViewType)
            {
                EnsureList(subViewType);
                _viewMap[subViewType].Add(t);                        
            }
            else
            {
                continue;
            }

        }
    }
}

ImplementsInterface and InterfaceExtends are extension methods I wrote for System.Type.  When this class is created, I’m reflecting the implementing assembly and finding all classes that implement a descended of IView and storing them in a List.  The implementation for the most basic NavigateTo(), then, just looks like this:

public void NavigateTo() where T : IView
{
    var viewType = _viewMap[typeof(T)][0];
    var view = (IView)Activator.CreateInstance(viewType);

    InitializeView(view);
}

In the most basic case, I am just using the first IView type that I find.  As for how the navigation actually happens, I will have to expose a few more implementation details now.

namespace HandWaver.AG.PresenationModel.Navigation.Modules
{
    [Export(typeof(INavigationService))]
    public class URIFrameNavigationService : INavigationService
    {
        public URIFrameNavigationService(string rootNamespace, Assembly va)
        {
            _viewAssembly = va;
            MapViewTypes();
        }

        static Action nullCallback = () => { };

        [Import(AllowRecomposition=true)]
        public Frame TargetFrame { get; set; }

Note that, for now, I am using MEF as an IoC container.  URIFrameNavigationService uses the Export attribute to tell MEF that it implments the INavigationService contract.  When I construct the instance I pass in some basic information and the class uses MEF to import an instance of the Frame class from Silverlight navigation.  So for the most basic kind of navigation, I’m instantiating a new instance of a Page implementing an IView interface and directly setting the Content of the Frame rather than giving it a URI.

Transitions

Next, I’m going to build the “Models” screen of my Audi fan application; in this case we’re talking about Audi cars and not the usual “Model”.  We can go ahead and look at the implementation for InitializeView() now:

void InitializeView(IView v)
{
    Action go = () => 
    {
        TargetFrame.Content = v;
        v.Activate(URIFrameNavigationService.nullCallback);
        History.Navigated(v);
    };

    if (null != TargetFrame.Content && TargetFrame.Content is IView)
    {
        ((IView)TargetFrame.Content).DeActivate(go);
    }
    else
    {
        go();
    }
}

If the Frame is currently displaying an IView, call it’s deactivate method.  When that method is done, the go action is called which activates the new IView.  We also do something with a History object here which I’ll get to in a little bit.  By using callbacks with Activate and Deactivate we’re able to incorporate animations into the navigation mix.  Here is the Activate implementation for Models.xaml:

public void Activate(Action complete)
{
    var trans = (Storyboard)App.Current.Resources["ExpandIn"];
    trans.Stop();
    Storyboard.SetTarget(trans, LayoutRoot);
    trans.Begin();
    EventHandler sbDone = null;
    sbDone = (o, e) =>
    {
        complete();
        trans.Completed -= sbDone;
    };
    trans.Completed += sbDone;
}

I pull a common animation out of resources and call the giving Action when the animation is complete, being sure to unhook event handlers.  In my case, I’d like to use the same Enter and Exit transitions everywhere in the application and I don’t want to keep copy & pasting this code.  I can move this code to a helper class and invoke it this way everywhere else:

public void Activate(Action complete)
{
    Transition.Animate("ExpandIn", LayoutRoot, complete);
}

public void DeActivate(Action complete)
{
    Transition.Animate("ShrinkOut", LayoutRoot, complete);
}

We could, in future refactoring, create some kind of IViewTransitionManager module that automatically does transitions everywhere, and save the Activate and Deactivate methods for other application specific uses.

You can now run the sample application (link at the end of the article) to see the transitions working from the Home screen to the Models screen.

navapp2

 

Passing Parameters

The View Details Hyperlink buttons, above, should navigate to a special screen showing even more  information about the chosen vehicle model.  When this screen loads, then, the chosen Model will need to be available.  We could set some global User State variable specifying what model of Audi was chosen but it would be nicer to navigate to the chosen IView and directly pass parameters to it.  What’s more, it would be ideal if this parameter passing could be strongly typed such that we can determine the types of parameters using Intellisense.

We have already seen how the INavigationService implementation locates and instantiates an IView implementation.  Adding another overload the the interface implementation meets all stated design goals.

/// <summary>
/// Navigate to a View, passing parameters to said view using the supplied Action
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="paramSetter"></param>
void NavigateTo<T>(Action<T> paramSetter) where T : IView;

It may not be apparent at first look what this allows us to do, but consider the current example of choosing a vehicle model from a list.  The ViewModel of the screen we are coming from knows what the selected VehicleModel is.  Using the excellent Generics implementation in C# we can navigate to an IView and easily see what parameters we might be able to pass to it via Intellisense.

protected void ViewDetailsClick(object s, RoutedEventArgs e)
{
    VehicleModel mdl = null;
    var lnk = (HyperlinkButton)s;
    mdl = (VehicleModel)lnk.Tag;
    Navigation.NavigateTo< IModelDetailView>((v) => v.TargetModel = mdl);
}

I am cheating a little bit here, at least it feels like cheating.  My list of vehicle models is implemented as a listbox and I am using (abusing?) the Tag property that Controls expose in Silverlight:

<HyperlinkButton Grid.Column="5" Style="{StaticResource MenuLinkStyle}" Content="View Details" VerticalAlignment="Center" Click="ViewDetailsClick" Tag="{Binding}">                                        
</< span>HyperlinkButton>

Since we can use Bindings for Tag, the actual data type for  each VehicleModel is stored with the Hyperlinkbutton and we can pull it in the Click event handler.  The real power of what we are now able to do is demonstrated when we see the intellisense available when navigating to IModelDetailView and exploring the overloads available:

navapp4

navapp3

Since we have a strongly-typed mechanism, we can create a lambda expression that takes the IView instance and we can inspect the list of public properties available on IModelDetailView.  In my opinion this makes for a perfectly discoverable mechanism for passing parameters directly to an IView.  If you need further help, you could create a class something like RequiredViewParameter;  having such a type in the Intellisene dropdown might help clients determine which properties must be set in order for a View to function.  Obviously, we an set as many properties as we want using the lambda expression.

 

Choosing Between Multiple IView Implementations

Often, there may be only one implementation for a given IView interface at run time and compile time.  For example, there may likely be only a RealFooView (the real production visual) and a MockFooView (used only for unit testing related logic) for a given IFooView interface.  In some cases, though, multiple implementations for a given IView interface may be present in the real run-time system.  For example, there may be “basic” and “expert” views of the same data and these views my be different enough that it doesn’t make sense to use DataTemplates and configuration properties to set up the View.  We’ve already seen that we are keeping a list of IView in the INavigationService, we can now add another overload to this interface to elegantly handle situations where more than one IView implementation is available.

You may have noticed in the various screenshots that there is a CheckBox on the UI with accompanying text “I am a Fanboy”.  The goal here is that if users check this item, we will show them a more rich set of data when they choose View Details.  By passing a sort of Predicate to the INavigationService, we can easily accomplish this goal:

navapp5

For now, the Fanboy checkbox sets a global variable we can access from within Models.xaml.cs.  I’ll use this value from within this Navigate code to create a selector function.

protected void ViewDetailsClick(object s, RoutedEventArgs e)
{
    bool isUserFanboy = App.UserIsFanboy;
    VehicleModel mdl = null;
    var lnk = (HyperlinkButton)s;
    mdl = (VehicleModel)lnk.Tag;
    Navigation.NavigateTo<IModelDetailView>((v) => v.TargetModel = mdl,
        (v) => v.IsFanboyView == isUserFanboy);
}

I can now create two implementations of IModelDetailView, a basic page and the “fanboy” page displaying more detailed information.  Based on this CheckBox option, Navigation will send the user to a completely different IView.

Regular View:

navapp6

Fanboy View, with extra data:

 navapp7

In this case, the Fanboy view could probably have been done without an extra view implementation, but you can likely see how this would be extremely useful.

What Explodes?

As more than one friend has pointed out to me, when building classes to fit into a framework mechanism there can often be an Explosion of classes, enums, interfaces, delegates etc.  What explodes under this approach, or under MVVM in general?

For every Screen in your application you could potentially have a new IView interface, a new ViewModel, and possibly a new Presenter or Commands.

Preventing View Explosion

If a particular View does not need extra methods, you can prevent View explosion by using Generics.  Generics are the answer to everything.  If your View needs a ViewModel and nothing else special, use something like this:

public interface IView : IView where T : ViewModel
{
    T ViewModel { get; set; }
}

This allows you to implement Views, gain the navigation benefits, but not create a new IView interface.

namespace HandWaver.AG.NavDemo.Screens
{
    public partial class SampleView : Page, IView<MainViewModel>
    {

 

Preventing ViewModel Explosion

Given the previous example, you may be worried about your ViewModel classes exploding, one per IView interface.  I may have mentioned before that generics are the answer to everything, sometimes lambda expressions are part of the answer but the answer definitely always involves generics.  You might, for example, need a ViewModel that does nothing but encapsulate an already existing Data Type from your domain.  There’s a generic type for that:

/// 
/// If the ViewModel can be satisfied by a pre-existing data type, 
/// just use that
/// 
/// 
public class ViewModel : ViewModel where T : INotifyPropertyChanged 
{
    public ViewModel(T source)
    {
        Payload = source;
    }

    private T _Payload;

    public T Payload
    {
        get { return _Payload; }
        set
        {
            _Payload = value;
            OnPropertyChanged("Payload");
        }
    }
}

Combined with the previous example, you could get away without another IView or ViewModel implementation:

public partial class SampleView : Page, IView< ViewModel<VehicleModel> >

If VehicleModel satisfies all of your needs for a View, you don’t need to create any new classes or interfaces.

I’ll talk about Presenters and Commands in a future article.

Conclusion

While the current trends in MVVM may downplay the role of a View interface, IView can be a powerful part of your programming toolbox.  Since the View is an actor in this pattern, it makes sense to be able to treat Views a certain way.  In this article you’ve seen how you can build a flexible Navigation framework around Views and even support handy features like strongly typed parameter passing and selecting from multiple implementations when present.  You can do all this without manually creating any kind of mapping.

HandWaver.AG: I mentioned elsewhere I would be publishing the source for all of my Silverlight Guidance demos.  You can find it here, though it may be updated by future articles and look slightly differently than what you read here.

You can also run the Demo Application.

Future Tasks

There are a few things I had to leave out as this article was getting too long.  The first is the promised Navigation History module.  This is 99.9% done but must have its own article. 

Second, and not very relevant here, is the Photo Gallery page.  This will serve up a cool demo when I get to it.

Finally, If you read the source code closely you may have noticed my comments about the reflection-based IView discovery mechanism.   The mechanism makes the assumption that all IViews you can navigate to are in the initial startup assembly.  In most applications that might be a safe assumption, but MEF is working on changing that with dynamic XAP loading.  Once I’ve had a chance to cozy up with MEF more I’ll post an updated solution.  Also related to MEF, storing the composition catalog on the App class is probably not a best practice.



Wednesday, September 16, 2009 7:28:20 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Monday, September 07, 2009

It is sometimes useful or necessary to represent complex data types as name/value pairs, or Dictionaries if you like.  While the Data Binding implementation in Silverlight 3 is admirable, there is not a built in way to do this cleanly.  Doing some Internet searches, you may find some approaches like this, from http://silverlight.net/forums/t/51864.aspx :

public class DictionaryItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var dict = value as Dictionary<string, string>;
        if (dict != null)
        {
            return dict[parameter as string];
        }
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Note that in all cases you will find the “ConvertBack” strategy missing.  Since the original source object is not passed to ConvertBack, two-way binding to  name/value pairs is an elusive goal.  The binding mechanisms in Silverlight 3 are not very extensible.  I cannot create my own binding type or provide a new implementation for Property Paths.  The Silverlight 3 Data Binding framework wants real Properties to bind to.  If we must have two way bindings to name/value pairs, we are left with pursuing more extreme measures.

Dynamic Type Generation

I have written in the past about the complete Reflection Emit implementation in Silverlight.  It seemed to me that given a means of representing the names and types of the items we want to pull out of a dictionary as Properties, we could dynamically generate a bindable version of a class represented as a dictionary.  We’ll start with creating a very simple model for Objects with data represented as name/value pairs.

namespace HandWaver.AG.EVModel
{
    public class Attribute
    {
        public Attribute(int id, string name, Type t)
        {
            Id = id;
            Name = name;
            AttributeValueType = t;
        }

        public int Id { get; set; }

        public string Name { get; set; }

        public Type AttributeValueType { get; set; }
    }
}

This attribute, not to be confused with the .NET Attribute, represents a sort of metadata for a single property on an object.  We give it a name (which ultimately must be friendly to becoming a Property name) and a run time type.  A single Attribute might be the "Name” property of some object with a type of string.  We may not ultimately want to bind to every single name/value pair available to us.  A means of representing a certain sub-Model is therefore very helpful.  A List of Attribute will suffice for this demo.

namespace HandWaver.AG.EVModel
{
    public class AttributeViewModel : System.Collections.Generic.List<Attribute>
    {
        public AttributeViewModel() : this( Guid.NewGuid() )
        {

        }

        public AttributeViewModel(Guid g)
        {
            UniqueId = g;
        }

        public Guid UniqueId { get; set; }
    }
}

The AttributeViewModel class exposes a UniqueId property of type Guid, which will be useful later.

Reflection Emit code, while incredibly powerful, can also be tedious, difficult, and error prone.  Once practice that I follow is to have as little dynamic code as possible, calling out to pre built classes and only doing what must be done in dynamically created types.  In order to be friendly to binding and provide a base to extend we’ll use a base class that does most of what we want to do already:

namespace HandWaver.AG.EVModel
{
    public class EntityWithAttributes : INotifyPropertyChanged
    {
        public EntityWithAttributes()
        {
            _values = new Dictionary<string, object>();
        }

        Dictionary<string, object> _values;

        public object this[string key]
        {
            get
            {
                return _values[key];
            }
            set
            {
                _values[key] = value;
                OnPropertyChanged(key);
            }
        }


        protected void OnPropertyChanged(string propName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

Next, we’ll build a class to create the dynamic types.  I’m currently calling this a Bindable Entity-Value Builder.  It’s worth going over the Reflection Emit code in some detail.  The public interface for BindableEVBuilder is a single method:

public static Type GetDecoratorType(AttributeViewModel prototype)
{
    EnsureDynamicAssembly();
    EnsureModelTypes();
    if (!_modelTypes.ContainsKey(prototype.UniqueId))
    {
        BuildDecoratorType(prototype);
    }

    return _modelTypes[prototype.UniqueId];        
}

We can look at the first several methods later.  The bulk of our work is done in BuildDecoratorType.

static void BuildDecoratorType(AttributeViewModel prototype)
{
    var typeBuilder = _moduleBuilder.DefineType("HandWaver.AG.EVModel.TypeDecorator.DictWrapper"+ GetName(prototype.UniqueId),
        TypeAttributes.Public | TypeAttributes.Serializable | TypeAttributes.Class, 
        typeof(EntityWithAttributes));

    foreach (var attribute in prototype)
    {
        BuildProperty(typeBuilder, attribute);
    }
    _modelTypes.Add(prototype.UniqueId, typeBuilder.CreateType());
}

So far there’s nothing clever here, just creating a dynamic class that extends EntityWithAttributes and making sure that we only generate one dynamic type for each AttributeViewModel by coming up with a naming scheme.  Properties are somewhat special in the .NET CLR.  In order to create a property using IL, the property itself must be defined as well as a getter and setter method.

static void BuildProperty(TypeBuilder typeBuilder, Attribute attr)
{
    var baseType = typeof(EntityWithAttributes);
    var propertyBuilder =
        typeBuilder.DefineProperty(attr.Name, PropertyAttributes.None, attr.AttributeValueType, null);
    // The property set and property get methods require a special
    // set of attributes.
    MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

    var getterBuilder = BuildGetter(typeBuilder, attr, baseType, getSetAttr);

    var setterBuilder = BuildSetter(typeBuilder, attr, baseType, getSetAttr);

    propertyBuilder.SetGetMethod(getterBuilder);
    propertyBuilder.SetSetMethod(setterBuilder);
}

The getter is more interesting than the setter in this situation.

private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, Attribute attr, Type baseType, MethodAttributes getSetAttr)
{
    var getterBuilder = typeBuilder.DefineMethod("get_" + attr.Name, getSetAttr, attr.AttributeValueType, Type.EmptyTypes);
    var getterILGen = getterBuilder.GetILGenerator();

    var baseGetterInfo = baseType.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
        null, new Type[] { typeof(string) }, null);

    LocalBuilder localValBuilder = getterILGen.DeclareLocal(attr.AttributeValueType);
    // Preparing labels
    Label label20 = getterILGen.DefineLabel();
    // Writing body
    getterILGen.Emit(OpCodes.Nop);
    getterILGen.Emit(OpCodes.Ldarg_0);
    getterILGen.Emit(OpCodes.Ldstr, attr.Name);
    getterILGen.Emit(OpCodes.Call, baseGetterInfo);
    if (attr.AttributeValueType.IsValueType)
    {
        getterILGen.Emit(OpCodes.Unbox_Any, attr.AttributeValueType);
    }
    else
    {
        getterILGen.Emit(OpCodes.Castclass, attr.AttributeValueType);
    }
    getterILGen.Emit(OpCodes.Stloc_0);
    getterILGen.Emit(OpCodes.Br_S, label20);
    getterILGen.MarkLabel(label20);
    getterILGen.Emit(OpCodes.Ldloc_0);
    getterILGen.Emit(OpCodes.Ret);
    return getterBuilder;
}

It is a convention that property methods are named as get_ and set_ plus the actual public property name.  What’s interesting here is the code we must generate to call the dictionary methods on the base class.  You will note that the method name is “get_Item”, which I only discovered by using Reflector.  I made the decision that I wanted I wanted the generated Properties to be strongly typed rather than return Object.  After emitting the OpCodes for calling the base class’s dictionary get method, we cast the Object to the type specified by the Attribute used to build this Property.  When I was first testing this I had properties of type string and double; the string was working and the double was not.  The Castclass opcode produces odd results when working with value types, so I found the correct Opcode for non-reference types which is naturally OpCodes.Unbox.

If you want to become more familiar with what the CLR is really doing, try writing Reflection Emit versions of some common tasks.

The setter code is straightforward as far as these things go.

private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, Attribute attr, Type baseType, MethodAttributes getSetAttr)
{
    var setterBuilder = typeBuilder.DefineMethod("set_" + attr.Name, getSetAttr, null, new Type[] { attr.AttributeValueType });
    var setterILGen = setterBuilder.GetILGenerator();
    var baseSetterInfo = baseType.GetMethod("set_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(object) }, null);
    setterBuilder.SetReturnType(typeof(void));
    setterBuilder.SetParameters(attr.AttributeValueType);
    // Parameter name=value
    ParameterBuilder value = setterBuilder.DefineParameter(1, ParameterAttributes.None, "value");
    ILGenerator gen = setterBuilder.GetILGenerator();
    // Writing body
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldstr, attr.Name);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Call, baseSetterInfo);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ret);
    return setterBuilder;
}

 

Proving it Out

With a first draft of the reflection code written, I wanted to write a simple UI to smoke test three obvious Data Binding scenarios:

  1. Basic binding to a dynamically generated property
  2. Invoking code manually to update a property to make sure update notifications are firing
  3. Change a value using a Control of some kind to make sure TwoWay binding is updating the instance 

These goals can be met with a simple and unglamorous user interface:

<Grid x:Name="LayoutRoot" Width="450" HorizontalAlignment="Left">
      <StackPanel>
          <TextBlock FontSize="30" FontFamily="Verdana">Things that I have time for:</TextBlock>
          <TextBlock Text="{Binding Code}"></TextBlock>
          <TextBox Text="{Binding Wine, Mode=TwoWay}"></TextBox>
          <Border CornerRadius="5" BorderBrush="Red" BorderThickness="2">
              <Border.Child>
                  <TextBlock Text="{Binding Wine}"></TextBlock>
              </Border.Child>
          </Border>
          <Button x:Name="ChangeBtn" Click="ChangeBtn_Click" Content="Change"></Button>
          <StackPanel Orientation="Horizontal">
              <TextBlock>Money(double)</TextBlock>
              <TextBlock Text="{Binding Money}"></TextBlock>
          </StackPanel>
      </StackPanel>
</Grid>

Yes, I’ve identified Code, Wine, and Money (and little else) as things that I have time for.  You’ve got to write what you know.  In the Loaded event handler for this basic Control, we can see the Entity Value classes in action.  The first step is to create a simple model to generate a bindable Type from.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    var model = new AttributeViewModel();
    model.Add(new HandWaver.AG.EVModel.Attribute(0, "Wine", typeof(string)));
    model.Add(new HandWaver.AG.EVModel.Attribute(1, "Code", typeof(string)));
    model.Add(new HandWaver.AG.EVModel.Attribute(2, "Money", typeof(double)));

First we’ll try one object with three attributes.  Creating an instance of the dynamic type looks like this:

var t = BindableEVBuilder.GetDecoratorType(model); _entity = (EntityWithAttributes)Activator.CreateInstance(t);

var t =  BindableEVBuilder.GetDecoratorType(model);
_entity = (EntityWithAttributes)Activator.CreateInstance(t);
_entity["Wine"] = "Chateauneuf du Pape";
_entity["Code"] = "Silverlight";
_entity["Money"] = 5555785.0;
LayoutRoot.DataContext = _entity;

Time for the moment of truth.  Running the code and interacting with the UI yields the following:

UI0

Everything works.  The one time binding “Silverlight” works, I typed Malbec in the TextBox and it updated the object, the Property changed evens fired, and clicking the Change button changed the bottom text to 1000000.  My dynamic property system works.

Going a Little Further

The basic binding situations work, but what about a slightly more complex scenario?  Let’s go ahead and test multiple dynamic types, multiple instances, and IValueConverters.

void BindGrid()
{
    var carModel = new AttributeViewModel();
    var year = new HandWaver.AG.EVModel.Attribute(0, "Year", typeof(int));
    var make = new HandWaver.AG.EVModel.Attribute(1, "Make", typeof(string));
    var model = new HandWaver.AG.EVModel.Attribute(2, "Model", typeof(string));
    var series = new HandWaver.AG.EVModel.Attribute(3, "Series", typeof(string));
    var msrp = new HandWaver.AG.EVModel.Attribute(4, "MSRP", typeof(double));
    carModel.AddRange( new List<HandWaver.AG.EVModel.Attribute>() {year,make,model,series, msrp} );

    var carType = BindableEVBuilder.GetDecoratorType(carModel);

    var wrx = (EntityWithAttributes)Activator.CreateInstance(carType);
    var s4 = (EntityWithAttributes)Activator.CreateInstance(carType);
    var s4Prestige = (EntityWithAttributes)Activator.CreateInstance(carType);

    year.SetValue(wrx, 2002);
    make.SetValue(wrx, "Subaru");
    model.SetValue(wrx, "Impreza");
    series.SetValue(wrx, "WRX");
    msrp.SetValue(wrx, 26000.00);

    year.SetValue(s4, 2010);
    make.SetValue(s4, "Audi");
    model.SetValue(s4, "S4");
    series.SetValue(s4, "Premium Plus");
    msrp.SetValue(s4, 47900.00);

    year.SetValue(s4Prestige, 2010);
    make.SetValue(s4Prestige, "Audi");
    model.SetValue(s4Prestige, "S4");
    series.SetValue(s4Prestige, "Prestige");
    msrp.SetValue(s4Prestige, 53000.00);

    var carList = new List<EntityWithAttributes>() {wrx, s4, s4Prestige};
    CarGrid.ItemsSource = carList;
}

You can see that there’s a bit of code being written to create the instances and set the values, but we could obviously write more code around that to automate more of the tedious work.  I will also note that AutoGenerateColumns=”True” did not work on the DataGrid: I leave that as an exercise for another day.  The resulting display is exactly what you’d expect:

UI1

 

A little polish

When first testing this code, I could see that it worked but inspecting the dynamically generated EntityWithAttributes descendant was producing odd results in the Visual Studio debugger.  Even trying to add a watch for the base class would show an error like “Unable to evaluate expression”.  Silly rabbit, I had forgotten to include debug information in my Reflection Emit code.  This turned out to be a lot easier than it sounds.  Let’s take a look at the EnsureDynamicAssembly method that I skipped earlier.

        /// <summary>
        /// Debugger info found at http://blogs.msdn.com/jmstall/archive/2005/02/03/366429.aspx
        /// </summary>
        static void EnsureDynamicAssembly()
        {
            if (null == _dynamicName)
            {
                var appDomain = AppDomain.CurrentDomain;
                _dynamicName = new AssemblyName("HandWaver.AG.EVModel.TypeDecorator");

                _aBuilder = appDomain.DefineDynamicAssembly(_dynamicName, AssemblyBuilderAccess.Run);
#if DEBUG
                var debugAttrType = typeof(DebuggableAttribute);
                var debugAttrCtor = debugAttrType.GetConstructor(new Type[] { typeof(DebuggableAttribute.DebuggingModes) });

                var customAttributeBuilder =
                    new CustomAttributeBuilder(debugAttrCtor,
                        new object[] { DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default });
                _aBuilder.SetCustomAttribute(customAttributeBuilder);
#endif
#if DEBUG
                _moduleBuilder = _aBuilder.DefineDynamicModule(_dynamicName + ".dll", true);
#else
                _moduleBuilder = _aBuilder.DefineDynamicModule(_dynamicName + ".dll");
#endif
            }
        }

I don’t usually use preprocessor directives but this is exactly what they’re for.  The stuff in between the #if DEBUG statements includes some symbol information in the dynamic assembly.  There’s more work I could have done to associate the code to a Document for source step-through, but at least going this far gets my Local/Watch windows in VS working:

WatchWindow

You can see that I can evaluate properties on the base class and even Reflect the dynamic type to see that it exposes “real” properties like any other Type.

Conclusion

There is a saying that every problem in computer science can be solved with one more level of indirection.  When using the Full .NET Framework it often seems they’ve thought of everything, even providing an effective layer of indirection for Reflection in the form of TypeDescriptor and friends.  Lacking TypeDescriptor in Silverlight, it’s up to us in the community to come up with solutions while we simultaneously pester Microsoft to add more extensibility to the Data Binding frameworks.



Monday, September 07, 2009 12:59:55 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [8]  |  Trackback
 Saturday, August 15, 2009

I have been happily using the Unity container for over a year now.  I had previously written my own IoC framework in Silverlight and used some other off the shelf and custom home-grown containers.  I have recently been getting started using MEF in Silverlight as part of some up and coming Patterns articles.

When you scratch the surface of MEF, I’ll warn you that you may be Underwhelmed.  What, exactly, is this?  The Ultimate IoC container?  What is this doing for me that Unity wasn’t already doing for me?  In fact, you can implement the IUnityContainer interface with MEF so what’s the big deal?

Is the big deal…

  • That MEF is the Visual Studio 2010 extensibility mechanism?  No. Microsoft has claimed that they needed MEF for internal efforts like this.  I believe they are earnest, that this is not just a public “Look at us eating our own dogfood” showing.  Still, Microsoft needed the Entity Relation model beneath the Entity Framework and we saw how well that was received by the community.
  • That MEF works in Silverlight?  No.  Other IoC containers were ported to Silverlight 2 long before Unity or MEF.  We’ve been able to use the same container for Silverlight/WPF/ASP.net/Winforms/Console apps for a long time.
  • That MEF has cool features like Catalogues, Dynamic Recomposition, Lazy<T> ? No.  These features are cool and I plan on exploring them further and talking about them to anyone who will listen.

No, the big deal is that MEF is shipping with .NET 4.

So what, you say? Let me explain.  A lot of you probably listen to .NET Rocks.  I do, I enjoy it.  The hosts are funny, the guests are bright, and I get bite-sized updates on what is going on out there in areas of the development universe that are important to me but not so important as to be in my overloaded RSS list.  .NET rocks succeeds for various reasons and to hone in on one of the big whys let’s take a look at one possible way to group developers:

  1. Brand new developers. 
  2. Developers who are lazy clock-punchers who don’t care about their craft whatsoever.  Enough said about these folks.
  3. Developers who know enough to know there are better ways.  They struggle within the bureaucracy.  They’ve heard testing is good, but why?  They feel they probably need abstractions, but where?  Some don’t quite have the motivation or the skills the find the answers themselves; some have never been afforded the opportunity to break out.
  4. Developers who think they have all of the answers but are full of $(‘.crap’).
  5. Developers who mostly “get it”, who have the motivation and skills to find the answers, refine their craft, and make things better.

Based on my own experience Group #3 makes up an awful large percentage of the developer world.  A lot of the folks in Group #5 start here until something snaps within them and they decide to stop playing WoW and get serious about figuring out what it takes to get better.   These #3 folks want to learn but the lure of American Idol keeps them from catching up on those blogs.  They want to try a new pattern but the Architecture Police have decreed that they must never do X and always do Z.  Maybe the political war required to get a new library into the solution to try a different way kills any initiative.  Maybe their employer never springs for training.  More often than not, the high turnover in IT isolates developers from the long term perspective that would otherwise be a great teacher.  Which abstractions worked and which turned out to be nightmares or at best simply unnecessary?  Who knows.  We don’t go back and ask because we’re afraid of the answers, and to be fair we all hate each others’ code anyway.

The .NET rocks show and things like it is a little beacon of light, a reminder that some people are really doing things right out there, and staying on the cutting edge, and delivering a lot of value.   But, Damon, how does this relate back to MEF?  Because Group #3 is looking for guidance.  How many guests have debated which Data Access technology Microsoft “wants us to use” ?  Do they want me to use LINQ to SQL or Entity Framework?  Yes, the good folks behind door #3 who are in the .NET space overwhelming look to Microsoft for that guidance.  When Microsoft showed creating SQL DataAdapters inside button click event handlers that’s what they did.  When Microsoft said Stored Procedures are the way that’s what they built.  When Microsoft said SOA was good we learned SOAP.  When Microsoft endorsed jQuery we learned jQuery. 

By putting MEF in the .NET framework the community of well-meaning folks who want to do better are going to see a new message, some new guidance.  “Microsoft wants me to think of my applications differently.”  Microsoft is telling me that my apps should be composed out of modules, perhaps modules hidden behind interfaces.  I should worry about the physical location of a module or its implementation later and program against abstractions.  When I write a class I should think about what the responsibility of this class is and what its dependencies are.  Microsoft is telling me to get onboard with loose coupling.  I don’t need to petition my overlords to bring in a new framework because the only thing between me and MEF is:

using System.ComponentModel.Composition;

Microsoft has always been a Platform company.  I like my Zune, the Xbox is cool, I’m a PC, and maybe the Microsoft Stores will be cool.  But let’s be real: what Microsoft does better than anything else is to build something that other people use to build something.  By including MEF in the core toolbox Microsoft is getting behind a lot of solid development ideas and removing another barrier between the Average Corporate Developer and Better.

 



Saturday, August 15, 2009 7:30:59 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [12]  |  Trackback
 Tuesday, July 14, 2009

One natural way to create some visual effects would seem to be animating the clipping path of a visual element.  Sitting down in either Visual Studio or Blend 3 will immediately show there’s not a straightforward way to do this.  There have been some other means of doing this posted online involving  creating a Storyboard and DoubleAnimation entirely in a code-behind.  Since one of our goals when using Silverlight and Blend should be a clean designer/developer separation,  I didn’t care for this approach. 

We’re going to build a “wipe” effect using animation and clipping paths and the only code behind will be the Storyboard trigger.

Setup

I’m first going to create a Silverlight 3 Navigation application and throw in my own styles.  I add a Page called AnimateClip.xaml and throw in a DataGrid containing some sample wine data and a button I can push to test the “wipe” effect.  The application looks like this when I navigate to the new page:

ss0

 

DataGrid XAML

I’m going to apply the Wipe effect to the DataGrid.  Here’s the initial XAML defining my grid.  As you’ve already seen above I created some Sample Wine data to populate the DataGrid.

<data:DataGrid x:Name="WineGrid" AutoGenerateColumns="True" IsReadOnly="True" CanUserResizeColumns="True" Grid.Row="1" Width="500" Height="200" RenderTransformOrigin="0,.5">
    <data:DataGrid.Clip>
        <RectangleGeometry Rect="0,0,500,200">
            <RectangleGeometry.Transform>
                <ScaleTransform x:Name="WipeScale" ScaleX="1" ScaleY="1"/>
            </RectangleGeometry.Transform>
        </RectangleGeometry>
    </data:DataGrid.Clip>
</data:DataGrid>

Note that I’ve given the DataGrid a clipping geometry that exactly matches the bounds the DataGrid has anyway.  The RectangleGeometry gets its dimensions from a Rect property.  The struct Rect has a Width property, but Rect.Width does not appear to be backed by a DependecyProperty, so we can’t simply animate the Width of a Rect.  Without creating new Rect structs and manually animating in C#, some trial and error was needed in order to find a workable XAML-only solution.  The solution is to name the ScaleTransform and animate its Properties.

I’m going to create the Storyboard manually in XAML, we’ll see why when moving to Blend 3 in a minute.

<Storyboard x:Name="WipeGrid">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="WipeScale"
                                   Storyboard.TargetProperty="ScaleX">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

So, this is fairly simple once we figure out how to effectively animate the bounds of the DataGrid.  Ordinarily this Storyboard would produce a uniform “shrink in” effect.  The default RenderTransformOrigin is of course the very center of a UIElement.  Because the RenderTransformOrigin of the DataGrid is set to 0, .5 we get the desired “wipe” effect as we are calculating from the left side rather than the center.

When I press the Wipe Out button the code-behind starts the Storyboard.  The wipe effect now works as desired.

Polish

As I enjoyed how the wipe effect looked, I thought perhaps I’d use some of the new built in easing functions to make it even better.  In the recently released Expression Blend 3 + SketchFlow, this is what I see when attempting to edit my Storyboard:

ss1

Despite the fact that the Storyboard works,  the clipping path is missing something needed for Blend to be able to show it for timeline editing.  I assume this is because it’s technically not a visual element in the tree, but just a humble RectangleGeometry used to modify a visual element.  I really don’t feel like learning the ins and outs of all of the easing functions and their parameters, so for now I had to create a bogus storyboard and copy it to this Page.  The Quadratic Ease (InOut) produced the effect I was going for.

<Storyboard x:Name="WipeGrid">
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="WipeScale"
                                   Storyboard.TargetProperty="ScaleX">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="1">
            <EasingDoubleKeyFrame.EasingFunction>
                <QuarticEase EasingMode="EaseInOut"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
        <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0">
            <EasingDoubleKeyFrame.EasingFunction>
                <QuarticEase EasingMode="EaseInOut"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

So, while that would not be bad as far as hand-crafting XAML it would be better if I didn’t have to build fake Storyboards to create my visual effects.  With a more complex Storyboard mistakes might be made in the manual copy & conversion process.  You now have an alternate way to animate clipping paths in Silverlight using XAML.



Tuesday, July 14, 2009 7:46:00 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, May 05, 2009

The Fox Valley .NET user’s group has posted a hand-out for the two sessions I’m doing this Saturday.  Cool!

http://fvnug.org/dnn/LinkClick.aspx?fileticket=ZGv%2fajN89eU%3d&tabid=36

{Edit: Here's the schedule of events too. http://fvnug.org/dnn/DayOfNet/Schedule/tabid/62/Default.aspx. I hope to see familiar faces there tomorrow.}



Tuesday, May 05, 2009 2:35:13 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, May 01, 2009

The next two stops on the Mix it Up! tour are quickly approaching.  My talk to the Wisconsin .NET Users Group (metro Milwaukee area) has been moved up to this coming Tuesday, May 5th. You can find details on how to register and where to go here.

The following night, Wednesday, May 6th, I will be in Madison.  You can find details on this talk here.

I hope to see you there!



Friday, May 01, 2009 8:00:22 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, April 10, 2009

Last night I did my first stop on the Mix it Up! tour: the Indy NDA group in Indianapolis, IN.  These guys have got it made.  They have an absolutely fantastic group of people, a great location, and a TON of swag at every meeting.  I was pleased to be bringing some t-shirts and copies of Expression Studio only to show up abd find 20 books, a webcam, and various other nice prices already on the swag table.

These guys are hard-core too:  Their attendance is 120 people and up for every event, and they hold various SIG groups right after the main user group meeting.  After I did the Mix it up content they invited me into their Architecture SIG meeting to discuss the MVC pattern and we ended up talking about map reduce and the Parallel Extensions for the .net framework.  Thanks, guys, for having me, and maybe I’ll be back again.



Friday, April 10, 2009 2:59:59 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, March 31, 2009

The What:

A whole lot of goodness was announced at MIX09.  Many of us were unable to attend.  Larry Clarkin and Dave Bost of Microsoft decided to locate some dedicated Web/RIA geeks in the Midwest region to bring an ultra-condensed version of the MIX09 announcements to user groups, conferences, and code camps.  While the level of detail won’t equal what you would have gotten by going to MIX09, we can hopefully get you excited about what’s coming up and answer some of your questions. 

The Who:

Corey Miller

Anthony Handley

Damon Payne

Josh Holmes

Larry Clarkin

The When:

  • 4/1 CD2UG (Corey Miller / Anthony Handley) – The first stop, I’ll be heading down to Chicago tomorrow to provide moral support and take notes on how things flow.
  • 4/9 Indianapolis, IN (Damon Payne)
  • 4/15 CNUG (Corey Miller/ Anthony Handley/ Josh Holmes)
  • 4/30 Lake County (Anthony Handley)
  • 5/6 Madison, WI (Damon Payne)
  • 5/8 RIAPalooza (Anthony Handley)
  • 5/9 Fox Valley Day of .NET (Damon Payne/ Larry Clarkin) – This is a day long conference, and our presentation will serve as the keynote for this event!
  • 5/12 Milwaukee (Damon Payne)
  • 5/12 Ft Wayne, IN (Corey Miller)
  • 5/14 Chippewa Falls, WI (Damon Payne) – hopefully I can be forgiven for looking forward to this date most of all.  Doug has promised to book the Leine Lodge for this presentation.  Beer + Silverlight = shangri-la
  • 5/16 Indy Code Camp (Josh Holmes)
  • 5/19 South Bend, IN (Corey Miller)
  • 5/26 Rockford, Il (Josh Holmes)
  • 5/30 Chicago Code Camp (Josh Holmes – possibly others?)

See you there!



Tuesday, March 31, 2009 9:11:15 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 19, 2009

Obviously a lot of big things were announced at MIX 2009 yesterday.  I maintain my position that I would like to see Silverlight go towards more parity with WPF than ASP.Net, but there’s plenty to be happy about here.  I am most interested in the low-level details on the Bitmap APIs, perspective 3D, and the new tool bundled in with Blend 3. 

MIX it UP!

If, like me, you missed MIX because you couldn’t spare the time or expense, we have a consolation prize to offer you.  Larry Clarkin has organized a cabal of Silverlight-loving speakers in the Midwest with the mission to bring the presentations from MIX2009 to your local User Group, Code Camp, or other community event.  The “MIX it up” tour may already be planning on visiting your user group in MN, WI, IL, or IN.  I myself will be talking at about four stops on the MIX it Up! tour.  Look for more details and dates soon.



Thursday, March 19, 2009 11:15:52 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, March 13, 2009

I’ve been blogging since before it was called “blogging”, I started around 1998 on a Geocities site.  I like browsers.  The internet is awesome.  Web pages are great.  Web pages have literally changed the world.  Still, call me old fashioned, but I like Smart Clients.  Whether you call them “Smart Clients”, “Fat Clients”, or “Client Server Applications” – they can often still offer the best user experience and the most rewarding developer experience out there.

In the ‘90s and ‘00s there were various reasons why companies were going online.  A lot of it was hype, to be a part of this big movement that was going on.  Some businesses found that some of their worst application deployment nightmares could be avoided by using the browser as a delivery mechanism.  Some businesses found that a standards-compliant web page could (sort of, with a lot of pain) be used to reach a wider audience such as Mac or Unix users.  Some businesses found that having things like database connections behind the firewall was a Good Thing. 

ReturnoftheSmartClient

Not all is well on the Web

Despite outrageous success, not everything is great in the land of browser based applications.  Is the in-browser markup-based application the best choice or is it being carried forward purely by momentum?  If someone at your business announces that “We’re going to build a new Widget Widgetizer for our Agents”, is it an unspoken assumption that it will be delivered via HTML?  Is it cheaper and easier to build, debug, deploy, and maintain a browser based app than a Smart Client application?  I wonder.

Doing layouts for web sites is still painful.  HTML was not precisely meant to be a ubiquitous content-positioning mechanism.  The post-<table/> world can be infuriating because CSS is still painful.  Successfully doing CSS-only layouts seems to require Herculean efforts.  Sure, people do it all the time, but these skills seem to be very hard won.  The average CSS expert I talk to can rattle off an astounding list of browser specific gotchas and most will confess that testing is extremely time consuming.  Firefox seems to mostly get it right, but that doesn’t matter if Safari and IE don’t.    Hilariously, IE8 will get most things right but this is a curse in disguise: all of the server-side “if browser is IE” code out there will potentially now break under IE8.  Even the dynamic menus generated by ASP.Net (a Microsoft product) were broken in the first IE8 beta, and appear to be broken still in the first release candidate.  Maybe it’s just the way my mind works, but I’d rather troubleshoot a threading issue than try to figure out which CSS attributes function differently on which dom objects in which browser and scream about min-height:xx; being ignored.

HTML 5 promises to save us with real layout primitives, but if we can’t even get rid of IE6 after 7 years, what kind of adoption timetable can we expect with a whole new paradigm?

Doing browser-based development using Ajax and JavaScript is still painful.  If you don’t believe me  I have a challenge for you: turn on your browsers JavaScript error notification so you get a popup every time a script error occurs.  How long can you stand to surf like that?  I personally last about 25 seconds.  Why do people tolerate this?  Name an important application commonly used outside the browser where this level of scary flakiness would be tolerated?  Would iTunes be as popular if I occasionally had to “refresh” the application because a error occurred when I hit Play?  If Live Messenger randomly threw error codes up?  If Excel occasionally just wouldn’t save my spreadsheet?

The Internet, which in its current incarnation was designed around stateless ideas, is being used to deliver applications which need to represent state to the user.  It does this by delivering documents with complex layouts written in languages that were not meant to allow for complex layouts, supplemented by scripting languages pushed to the breaking point.

Not everyone likes strongly typed languages, true.  Not everyone likes writing unit tests, with code coverage tools, refactoring tools, static analysis, and so forth.  A lot of people do, and a lot of those people find the typical JavaScript experience to be a bit primitive by comparison.

iPhone as indicator?

When the iPhone was first announced, the plan was that development for the device would be accomplished via JavaScript in the browser with special extensions to access the iPhone hardware.  Ultimately, though, development is done as a native client in Objective C or Mono static compile.

I had, for years, secretly held the theory that Microsoft avoidance was the primary factor that had propelled browser-based development in the direction it was going.  Building native apps for the various LINUX flavors isn’t all that fun.  Java Swing is absolutely terrible.  Visual Basic, C++, or .NET on Windows? Pretty good!  Personally, if I wanted to build an application that could reach over 90% of PCs in America, and have a great developer experience while doing so, I’d say that’s a pretty good bet.  A lot of people disagree with me.

With the iPhone having gone native with Cocoa Touch, and Google re-imagining ActiveX via “Nclient” I wonder if we are seeing the beginning of a trend.

Silverlight to the Rescue?

I was meeting with a potential client who seemed to be somewhat familiar with some of my on-line antics.  The client asked “So, why are you so into Silverlight?”.  There are really two answers to this question.  One of course is that my Nerd Brian gleefully laps up the serotonin released as a side effect of my diet of C# 3, VS2008, LINQ, Lambdas, vector graphics, and so forth.  This is not an answer that lends itself well to building a proposition of the business value of your expertise.  So what came out of my mouth was something like this:

If we take a look at the application designs and user experiences we are seeing in the current “web 2.0” world we can make some observations.  We are seeing an amazing level of creativity and high production quality of graphics and animation.  We are seeing instant interaction, multiple simultaneous asynchronous actions happening by exchanging small messages with the server instead of whole-screen refreshes.  We have Google maps and several “Outlook in the browser” variants and a host of other accomplishments I wouldn’t have believed without proof.  The technologies allowing us to do this are all in one way or another hacks, round-about techniques, unintended extensions, or fragile houses made of cards.  They are steps towards real Smart Client technologies.  They are all band-aids and medications lavished on a ruined Soldier who was never meant to fight this war.  I prefer to take the most direct route and use a tool perfectly suited to the types of experiences we want to create.  Silverlight is the direct route from A to B.

This is the point where people who have worked day in/day out with JavaScript, Ajax, and CSS for ten years will tell me that they can do anything that Flash can do, and that it’s not that hard, and browser plug-ins suck, etc.  I have a hard time understanding this world view, and I suspect that a great many other people lacking that 10 years of scars might as well.

What’s your point?

At any rate, my goal here is not to start a war.  Rather, let’s pretend your business is actually having a real discussion about whether an application should be browser based or not.  What does the landscape look like today?  I’ve already made some comments about the developer story, but what about:

Data, Firewalls, and you

I was serving as technical reviewer for a book called “Apache SOAP”, before SOAP became somewhat synonymous with “Web Services”.  It seemed clear that Http-based message exchange could potentially solve one of the issues associated with the client-server era.  The firewall is already open for HTTP traffic, so why not send data instead of markup?  There are a number of options today for exchanging data through the firewall: WCF, Remoting, REST-full services, ASMX services.  Not only does this negate one of the old benefits of markup-based applications, but a lot of things are going the direction of distributed computing, grid computing, or mashups, or crowd-sourcing.  The success of Folding @Home, for example, shows what a distributed auto-updating smart client can do.

Performance

I often hear the words “Performance doesn’t matter”, mostly from people for whom performance is a black eye on their world view.  Performance does matter.  I currently have 250 movies in my Netflix queue and simply scrolling the page up and down in IE maxes out the CPU.  Pulling up damonpayne.com in Firefox and resizing the browser window maxes out the CPU.  Yet in Silverlight I can play full-screen HD video and the CPU is around 25%.  Animations? Visual Tools? Large data sets?  What about callbacks to a client without the client needing to constantly poll?  I suppose network performance doesn’t matter either?  In case you haven’t noticed, CPUs aren’t getting faster at the same rate they have been.  Performance matters, and the compiled language camp has a big advantage here.

Deployment

Through various techniques, it seems to me that the deploy-and-update issue has mostly been solved.  Java had Java Web Start.  There is a slew of aftermarket “Application Updaters” for various platforms.  .Net had the Application Updater Block and now Click Once and XBAP.  A rich Silverlight (or Flash) app can still be delivered through the browser but keep the advantages of a Smart Client platform.  PERL has CPAN.  Vista has Windows Update.  In this late hour, what kind of deployment advantage can we say a web application has over a smart client?

Cross-Platform web apps

I feel the need to reiterate this point as it’s a personal favorite: How cross platform are markup based applications?  How much time is spent debugging CSS and JavaScript in different browsers on different operating systems?  Chances are it’s a lot of time that could have been spent elsewhere.  Sun tried to make Java the universal smart client platform but they forgot to to make AWT and Swing not suck; SWT seems to have gotten to the party too late. 

Standards (HTML, CSS, ECMA script), no matter how well specified, will somehow manage to be interpreted differently by different vendors.  The nice thing about a plug-in model (Silverlight 2, Flash) is that they tend to come from a single vendor for every browser and operating system.  Most of the code will be shared with a bare-minimum bootstrap rebuilt for the specific platform.  There are some exceptions, such as Moonlight. In the case of Moonlight, Miguel, of whom I am a huge fan, actually has access to Microsoft’s internal specification and test suites.  A spec PLUS a suite of tests that shows you if you are rendering correctly is a good thing.  I have much more confidence that Microsoft and Novell can get the Silverlight experience to be 99.9% universal than I do that IE/Firefox/Opera/Safari/Chrome will come close to converging.

Presentation

As I mentioned to my client, it seems a shame to me to spend a lot of time and money producing high quality assets and then Hacking them up for a web site.  Worse, if someone has their browser scaled for accessibility reasons, raster images aren’t going to look so hot.  Vector graphics presented via a smart client technology has a clear advantage here.  A Style or Control Template in Silverlight does precisely what it’s supposed to do, no frustrating cross-browser debugging.  No need to build raster images for Gradients when Gradients are primitives within Silverlight.  No need to debug layouts in every browser when Silverlight has a comprehensive yet extendable layout system.  No need to worry about “web safe” colors, and you need not be shy about embedding fonts.  I’ve seen some impressive JavaScript-based animation, but Expression Blend makes it so easy and there’s that pesky “performance” thing to think of too.

Smart client software can give you great control over the presentation of your brand.

Conclusion

After 10 years of the browser delivered/markup based application getting all the attention I think the Smart Client is making a comeback.  Smart Client technology has gotten better.  Having massive storage and gadgets like GPS and accelerometers in our phones has brought back to the mind the advantages of access to the local system.  Ideas like the Apple Store help close the usability gap between installing software and just hitting a webpage.  Standards have emerged letting Smart Clients take advantage of the web.

Part of what I’ve said here can be chalked up to personal preference.  If you like dynamic languages and don’t write unit tests, or you hate Microsoft, or you are anti plug-in, you may disagree. I would appreciate any rational feedback pointing out where you think I’m wrong.

Note: As I was doing proofreading for this article before publication, Scott Hanselman posted some thoughts about Quake Live.  At one point he states “There's no reason for QuakeLive to be shoe-horned into a browser plugin”.  I couldn’t agree more.



Friday, March 13, 2009 2:14:11 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Tuesday, February 24, 2009

I just learned that my Image Hotspot Designer article was mentioned on TCS Weekly #005:

http://channel9.msdn.com/shows/TCSWeekly/ep005/

The article is mentioned briefly around 2:30 in the video.  Now the entire world knows about my love of wine and my messy (if well supplied) office.



Tuesday, February 24, 2009 10:22:10 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, February 23, 2009

I have taken first place in the most recent Silverlight: Write and Win contest on SilverlightShow.Net for my article on creating a Designer for Image Hotspots in Silverlight.  Thank you judges!



Monday, February 23, 2009 9:53:31 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, February 06, 2009
 Friday, January 23, 2009

I had to comment on the latest Hanselminutes podcast.  I would absolutely love to do an article in the same style as AGT, but building up a Document instead.  I have not written a document/text editor from scratch so I would be starting from zero knowledge, but it sure sounds like fun.

There's at least 10 more AGT articles to go, so don't hold your breath.



Friday, January 23, 2009 1:42:25 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, January 14, 2009

I wanted to write up some thoughts based on attending yesterday's MSDN Dev Con in Chicago.  Since I knew this was coming, I purposely did not do much reading about the technologies announced at PDC.  This was my first exposure to these items. 

Keynote: Ron Jacobs's keynote was of course the typical "Lots of cool stuff you can't have yet".  I'm interested in Windows 7, I'm one of the folks who actually really likes Vista.  While I had hear about VS 2010 being rewritten in WPF this was the first time I had actually seen it, in this case showing some kind of comment view provider.  This will be huge.

A lap around Oslo: I've been following Dan Rigsby's blog for a while and I got a chance to meet him before the show. 

Here's what I learned from his talk: Oslo is the capital of Norway, and Microsoft wants to buy it.

Just kidding, Dan!

Now that I have a super-basic understanding of Oslo I have a couple of concerns.  The first regards Dan's comment about creating a DSL that some kind of business user could use to create functionality.  It is my firm belief that this won't happen in the next ten years, not because it's technically infeasible, but because Business Analysts, Managers, and the like do not want direct responsibility for systems.  They like a nice, thick neck to get their hands around if something goes wrong and by that I mean us Technical folks can always be blamed for any shortcomings in the implementation of any Grand Vision.  Maybe I'm pessimistic, but I cannot imagine this direct accountability happening any time soon.

My second concern is a smaller technical issue.  Dan showed an example of a DSL specific to WCF services.  This seemed well enough for expressing WCF specific ideas (endpoints, URL patterns, etc.) but I wonder if there is or will be a mechanism for using generic code from another CLR language.  For example, I may have a business logic or data access component already written in C#, and my WCF service is just a thin layer on top of it to expose it as a service.  Enquiring minds want to know.

Business Apps in Silverlight: I heard before the conference that this talk would involve a brief look at some new features in Silverlight 3 so I had to check it out.

The future stuff was billed as ideas taken from the Artist Formerly Known as Alexandria.  Overall, I'm sorry to say I was disappointed in what I saw and here's why:

  • A demo was shown whereby a server side business operation exposed via WPF was automatically linked to a generated Silverlight class in the client.  This was nice, since I have nothing but issues with the automatically generated endpoint configuration for WCF <--> Silverlight.
  • Data Source: MSFT is bringing the notion of an ASP.Net Data Source into Silverlight/XAML.  All in all, I wasn't sure I liked the magical nature of this, but it's optional.
  • Navigation: my ears perked up when the speakers said they were going to bring a notion of Navigation into Silverlight.  My problem with what was shown was that it was NOT NavigationWindow and Page, but something new: Frame and so forth.  Why not use WPF concepts?  I have heard many times that the intention is to bring more parity between Silverlight and WPF.  The ability to deep-link to the application using the URL query string is good at least.
  • An ASP.Net-ish Login control was shown as well as some declarative security you can place on functionality.  It wasn't clear if this would ONLY work with ASP.Net Membership and Role providers on the server, or if the client side mechanism would be extensible via a provider model as well.

Overall, I'd like to see Silverlight going in more of a WPF direction than an ASP.Net direction and I'm worried about what I'm seeing.  The vibe I got from this talk was that Silverlight is going to "Work real well with ASP.Net" or "Something you can do instead of Web Pages, if you want".  There was no mention of bringing Commanding or other awesome (fundamental) WPF features into Silverlight.  I hope that I'm wrong.

F#: I first looked at F# something like two years ago and got frustrated and dropped it.  Having done some functional style stuff in C# and LINQ of late, I've been meaning to pick it up.  Aaron Erickson gets big points from me for using the term "Hand Waving".  His presentation, however, I felt wasn't basic enough for an intro to F#.  If the goal was to tell the audience why they might want to look into F# then mission accomplished.  If his goal was to show us how to do some basic things, not so much.  What do the |> or -> operators mean in F#, things like that.  By this time of the day, my 100% lack of sleep the night before was seriously weighing on me, so these things may have actually been explained.

I do plan on getting the book he described as the Most Basic learning F# book he showed, if I can remember what it was called.

After the conference several of us went to Fogo de Chao where I had a tremendous amount of excellent meats with bell peppers and some Malbec.



Wednesday, January 14, 2009 11:51:59 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Monday, January 12, 2009

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

 

Gestures, Commands, Transactions, and Undo

There's so many sexy things coming up, it was difficult to postpone them further.  There's two more important categories of plumbing that needed to be built.  We'll cover the design of one category and its pieces in this article followed by two dedicated implementation articles.  The other functionality, Document Types and Serialization, will be next.

I would like to be able to use the keyboard for some gestures on the design surface: for example the arrow keys should move the selection and I should be able to delete items from the surface with the delete or use CTRL-Z for undo.

Additionally, I would like to support some kind of undo.  The plumbing needed for undo will allow for other cool interactions later...

Implementing Undo using Transactions and Commands

Undo can be an incredibly hairy thing.  In my opinion, going back to a working system and suddenly trying to add undo can be a very good test of how well designed the system is.  Those following along should take careful note of the degree to which the changes we see are additive (feels like we're just adding another feature) versus things that are refactoring (changing code that was already working to support this new feature).

Now to reveal a personal bias: I seldom find a use for "Redo" as it is sometimes implemented.  For example in MS-Word you can apply some formatting to text.  You can then use "redo" to apply the same formatting to some other text.  I'm really only interested in in redo as an Undo-for-Undo. So Move something, undo, no wait I really wanted to do that, redo.

This is something that's important to get right so let's list the primary goals:

  1. The changes made to items on the surface are easily captured to be undone or redone.  This should be true for both surface interactions and property editing.
  2. The number of undo levels should not be artificially limited. In some cases an undo won't "work" because the state of the system has changed -  we don't want Units of Undo to have to know about other Units of Undo.
  3. There should be a centralized means of manipulating, creating, or querying currently known Units of Undo.
  4. Interactions should be able to be grouped together where it makes sense to do so.  For example, moving an IDesignableControl around on the surface may in fact be 200 small changes, but we really only care about the starting position and the final position when the move interaction is over.

Item #4 in particular is important to me.  Visual Studio sometimes has some odd behaviors where what is perceived as a single Gesture by the user actually requires several Undo operations to erase.  For example, I might paste some code in or use a shortcut Chord and the first undo changes the formatting of the created code and the second undo actually gets rid of the code.

 

Gestures and Commands

Would it be best to try to mimic the Commanding system from WPF, even partially?  I debated this point long and hard: is it worth some research, some effort, some potential confusion due to subtle differences?

I played around on several different occasions and I did come up with some things that I liked better than the other "Commanding in Silverlight" code I've seen out there but for now I didn't want to use it.  Specifically, the ability to map keyboard gestures to a command from XAML was problematic.  Still, Silverlight does contain the ICommand interface, and I tend to like the Command Pattern for encapsulating units of work, so by this time I had most of a design in mind:

UndoTransactions

Controls that need to affect the change of something living on the Design Surface will go through some kind of Change Service.  This is similar, but not exactly like, how Visual Studio does this today.  Using this design I will allow interested parties to be privy to (but not cancel) all the changes going on in case this is needed for some custom logic.  The IDesignerChangeService will also be the gateway to creating and undoing transactions.  This bears some disucssion.

The IDesignerTransactionService and IDesignerChangeService are both public and overridable or replaceable, but they work in tandem.  In any circumstance I can immediately think of, client code will want to deal strictly with the change service.  The process will look like this:

CommandChangeProcess

 

Note that it will be up to the Client who is initiating the change to determine if gestures need to be grouped together into a transaction or not.  No one else would know!  We'll explore more about how we accomplish this in the forthcoming implementation article.  The stimuli shown here are not going to map directly to real method calls, but this gives you a good idea of what's going to happen as objects are edited.

I do like the way WPF creates some ways of specifying commonly used UI commands.  I can also see how the ability to customize the mapping from gestures to specific ICommand implementations would be useful.  Towards these goals, I am going to build some infrastructure modeled after what's in WPF.

DesignerInputBinding

These classes all exist in WPF.  My goal here is that I will mimic the WPF functionality as closely as possible and provide a public interface to InputBindingCollection.  With this, an application that wishes to customize the keyboard behavior of the design surface can simply replace the default ICommand for a gesture.

 

AGContrib

Over the course of the AGT project and several other efforts, I've written (or at least started) some useful things for Silverlight that may have usefulness beyond the AGT effort.  I'm going to be moving these things into a new project: DamonPayne.AGContrib.  For the sake of my time, this will be a part of the AGT solution for now, until I get time to move it into it's own CodePlex project.

The first denizen of the AGContrib project will be the Input Binding framework as shown above.

I should most likely move some other handy code from the DamonPayne.AG.IoC project into AGContrib as well.  When I get some more time in the coming weeks, I will also be evaluating Unity 1.2 for Silverlight, possibly deprecating my own custom container in favor of it.

 

Conclusion and Next Steps

It's been a while since I've done an AGT article that's entirely design without code.  I'm trying to keep the articles reasonably bite-sized.  I've checked in the most recent design documents, CodePlex change set 10098.  In the next article I'll implement the all the undo functionality.



Monday, January 12, 2009 2:35:07 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, December 11, 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

Yes, I changed the name of the article.  Here at the 19th step, I decided to ditch the old tagline.

Visual and Code Refactoring

After letting Property Editing sit for too long, I have a list of questions to answer and things I've been meaning to fix or refactor.  I publish these Refactoring articles as part of the Reality Blogging commitment but also in case some of the random code is useful to anyone.  Let's get started.

 

Visual Lasso

I don't know why I had a corner radius on the Visual Lasso, I was probably suffering from some Web2.0itus which I am now over.  I change this and changed the colors:

Vision18

The lasso was also crashable by drawing diagonally until the lasso size would involve negative numbers.  This is fixed.

 

Some Questions

I have gotten some questions from interested parties, which I'll answer here.

  • How about a better constructor for LogMessage?
    • Sure.  More constructors provided
  • Where is IDragDropmanager.RemoveDropTarget?
    • Missing.  I can be very focused trying to get these articles out sometimes.  This has been added.
  • In Page.xaml.cs in the example application, why are you resolving IRegionManager when the Page is the IRegionManager implementation?
    • Force of habit, and I was unsure how I was going to leave that.  And there's nothing wrong with talking to yourself using a Contract...
  • Why are the names inconsistent between IPageView and RootPresenter ?
    • Shameful inconsistency.  IPageView has been renamed to IRootView.
  • Why are the names of the Region Controls in Page.xaml not the same as the Regions themselves?
    • More inconsistency.  They are all named XXXRegion now.  As I looked at this I also wondered why I was using Canvas and not a ContentPresenter, possibly something to do with how much ContentPresenter was chaning in Beta2-->RC0.  At any rate, the regions have good names and I've switched to ContentPresenter.
  • In IRegionManager, what is the difference between RootVisual and TopLevelContainer?  Why do you need both of them?
    • RootVisual is meant so that IRegionManager could provide something to System.Windows.Application.RootVisual, and matches its type of UIElement.  I could probably rename this to ApplicationRootVisual since that is the only reason to have this.  TopLevelContainer is used as the main Playground of services that need to do something visually, like DragDropManager.  This isn't as bad as it first seems, for example the built-in "Popup" class must do something very similar.  Comments have been added.

The Border on DesignSite

When is five minus five not zero?  When you have applied a RenderTransform to a FrameworkElement.  For the border on DesignSite, I would move the control by the border thickness (5px) when I would hide or show the border.  Once you have applied a RenderTransform, though, changing a Canvas.XXXProperty by 5px no longer means 5px.  I fixed this by using a transparent and normal border rather than by changing the border thickness from 0 to 5 to show selection.

 

~Creative Commons

Since I had to choose from a list of licenses on Codeplex (I chose MS-PL) I am removing the creative commons snippets from the code as I see them.

 

Clipping

The Canvas does not clip its children.  This means you can drag controls all over the place outside of the Design surface which is obviously undesirable.

Refactor: I extracted a MouseMoveSelection method and made it virtual so the logic was not totally hidden inside an EventHandler.  This method ultimately calls MoveSelection

I needed to make a decision here, to either create a class that extends Canvas or Panel and does perform some form of clipping on its Children, or to simply implement boundaries within the DesignSurface class.  For now I went with enforcing the bounds in code and snapping the selection back to a valid value.

Floating Properties

I went back and finished implementing my original intention for the PropertyGrid, namely that it would float around with the selection and not be statically docked to the right of the surface.

Refactor: I had a TODO item to un-hack the relationship between IDesigner and DesignSite.  Since IDesigner defines a Surface property of type Canvas this was mostly already done.  DesignSite.DesignParent is now of type IDesigner.

Refactor: Some methods, like GetSelectionBounds(), seemed to belong more on IDesigner than DesignSite after the above change was made.

What I sought to do next was pretty vanilla: if IDesignEditorService.Visual currently had a parent, I would remove it from its parent and add it to IDesigner.Surface or IRegionManager.TopLevelContainer in a location close to the right bounds of the current selection.  Control.Parent and VisualTreeHelper.GetParent both have a return type of DependencyObject.  It seems there is no generic way to remove a FrameworkElement from its visual parent.

DamonPayne.AG.IoC.ControlExtensions  now has a RemoveFromParent() extension method.

The PropertyGrid now floats around to be close to the current selection.

Vision19

 

Bigger Surface

In order to set things up for some future articles, I have change the default size of the DesignSurface and the sample application.  Yes, this means it won't fit on a 1024x768 screen, which apparently some people still have even in this late hour.  We'll fix this in a future article but for now I wanted more room to play.

 

A better Toolbox

The way gestures worked in the toolbox had been bothering me, and I thought I could make it better looking anyway, a little more like Visio perhaps.  I nearly introduced an external dependency on the Silverlight Toolkit, but wanted to see how to do this myself instead.  Given the amount of space I'm working with, I needed to add some more furniture.  I have created an area rug, which I feel really ties the room together.

I created a Control extending Panel, WrapLayoutPanel, to property postion them items in my new and improved toolbox.  The MSDN documentation for MeasureOverride and ArrangeOverride was simple, but surprisingly helpful.

Here's the new and improved toolbox:

Vision20

 

  Conclusion

I've finally gotten around to fixing a number of things that have been bothering me, and I think the application looks a little better too.  I am having a hard time deciding what to do next, and I'm afraid the next two installments are not going to be very sexy, but will be very necessary to tie all the concepts together.

The changes in this article are published as version 0.7.18, Codeplex change set 8710.  While things are still very rough, I did go ahead and make this the default public release on Codeplex.

The live demo has been updated at http://www.damonpayne.com/agt



Thursday, December 11, 2008 6:26:42 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, December 02, 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

 

Finishing Property Editing

In the last article, I mentioned the need for some more complex test cases to use to help determine when Property Editing was done.  I could see that too much logic was living in EditServiceHelper and that the editing of properties for multiple items at once was going to become very difficult.  It has been far longer than I intended since the last article (user group presentations, etc.) and there's a lot of cool stuff we can get to after we get Property Editing working the rest of the way so let's get to it.

A Better Example

A much better example was required right off the bat.  This required some more up front thought and setup.  Each Property potentially has a Display Visual and an Edit Visual, and in some cases these may be the same instance.  I need to test some reasonable scenarios:

  1. If multiple items are selected that happen to have a Property with the same name and of the same type and with the same Editors we can edit this Property for all selected items with a single Edit Visual even if the items are not of the same Type.  Only the lowest common denominator of Properties should display.
  2. Basic edit scenarios: an Enum Property is editable in a ComboBox, a string Property is Editable in a TextBox, and so forth.
  3. In some cases the Display Visual will be a custom IDesignablePropertyVisualizer Type which should support some kind of Binding so it auto-updates when edited.
  4. In some cases the Edit Visual will be a custom IDesignablePropertyEditor Type which should support binding as well as setting the value to the actual object on loss of focus or Enter key.
  5. Refactor: It seems clear at this point that since I'm heavily using Bindings, IValueConverter will come in handy, so this has been added to DesignablePropertyDescriptor.

In order to test all of this I'll make the following changes:

  • Couch and Chair will get a Softness property of type Double which I'll edit with a custom slider.  This should work for single or multi selection. (Tests #1, #4)
  • Softness will be displayed with a custom "volume style" Visualizer (Tests #3)
  • The Color property of Chair & Couch will get a Converter that displays a Color name instead of the hex value as shown in the previous article. (Tests #1, #2, #5)

Beyond this, we'll simply need to think about the various "basic" edit scenarios to support, like editing boolean values with a radio button or checkbox.  I've added "Editors" and "Visualizers" namespaces to the DamonPayne.HTLayout project and created the requisite implementations.

 

PropertyGridModel

The various private fields I had been adding to PropertyGrid for keeping track of various states and such clearly needed to be moved to a single class which I now call PropertyGridModel.  All of the methods are virtual to allow enterprising developers to easily tweak behavior.  The public interface for PropertyGridModel :

/// <summary>
/// What are we editing?
/// </summary>
public virtual List<IDesignableControl> Selection { get; set; }
/// <summary>
/// Properties common across the entire Selection
/// </summary>
/// <param name="props"></param>
public virtual void SetProperties(List<DesignablePropertyDescriptor> props)

 

/// <summary>
/// Only 1 property is editing at a time
/// </summary>
public FrameworkElement CurrentEditElement { get; set; }
public virtual void SetDisplayElement(DesignablePropertyDescriptor d, FrameworkElement fe)...
public virtual FrameworkElement GetDisplayElement(DesignablePropertyDescriptor d)...
public virtual List<FrameworkElement> GetAllDisplayElements()...
public virtual void SetEditElement(DesignablePropertyDescriptor d, FrameworkElement fe) ...
public virtual FrameworkElement GetEditElement(DesignablePropertyDescriptor d)...
public virtual DesignablePropertyDescriptor GetDescriptorForEditElement(FrameworkElement fe)...
public virtual DesignablePropertyDescriptor GetDescriptorForDisplayElement(FrameworkElement fe)...
/// <summary>
/// Remove everything from the Model
/// </summary>
public virtual void Reset()...

 

EditServiceHelper

I continued by overhauling the EditServiceHelper class.  I realized my notion of the TypeDescriptor-like functionality of this class was not fully baked, but sitting down to look at it the second time a much more cohesive purpose coalesced.  I also made sure to pull in the concepts of IDesignablePropertyEditor and IDesignablePropertyVisualizer right away.  I'm only going to show the Display path and no the Edit path here for brevity, they are very similar.

In a static constructor we set up some defaults, this will be expanding as I go.

AddDefaultDisplayor(typeof(string), typeof(TextBlock));
AddDefaultDisplayor(typeof(Color), typeof(TextBlock));
AddDefaultEditor(typeof(string), typeof(TextBox));
AddDefaultEditor(typeof(Color), typeof(ComboBox));
AddBasicBindableDisplayType(typeof(TextBlock), TextBlock.TextProperty);
AddBasicBindableEditType(typeof(TextBox), TextBox.TextProperty);
AddBasicBindableEditType(typeof(ComboBox), ComboBox.SelectedItemProperty);

These are all public static methods in case someone needs to expand this faster than I get to it.

Client code (PropertyGrid) will call GetDisplayInstance to get some type of Visual that can be used to represent the value of a single property

public static FrameworkElement GetDisplayInstance(IDesignableControl instance, DesignablePropertyDescriptor desc)
{
    //if null we try to find a default, otherwise see if we can do a binding anyway using the override
    if (null == desc.DisplayType || _basicBindableDisplayTypes.ContainsKey(desc.DisplayType))
    {
        Type displayType = _displayors[desc.PropertyInfo.PropertyType];
        FrameworkElement displayInstance = null;
        if (null != displayType)
        {
            displayInstance = (FrameworkElement)Activator.CreateInstance(displayType);
            SetupDisplayInstanceBinding(instance, desc, displayInstance);
        }
        return displayInstance;
    }
    else if (desc.DisplayType.ImplementsInterface(typeof(IDesignablePropertyVisualizer)))
    {
        var visualizer = (IDesignablePropertyVisualizer)Activator.CreateInstance(desc.DisplayType);
        visualizer.Initialize(instance, desc);
        return visualizer.Visual;
    }
    return null;
}

First check defaults, then check special overrides, then ultimately give up.  ImplementsInterface is an extension method in the DamonPayne.AGT.IoC project.  SetupDisplayInstanceBinding handles the rest of the work which is fairly simple with the help of Silverlight Binding.

protected static void SetupDisplayInstanceBinding(IDesignableControl instance, 
    DesignablePropertyDescriptor desc, FrameworkElement display)
{
    if (_basicBindableDisplayTypes.ContainsKey(display.GetType()))
    {                
        var dProp = _basicBindableDisplayTypes[display.GetType()];
        Binding b = new Binding(desc.PropertyInfo.Name);
        b.Converter = desc.Converter;
        b.Mode = BindingMode.TwoWay;
        b.Source = instance;
        display.SetBinding(dProp, b);
    }
}

How might a custom Property Visualizer work?  Take a look at part of the code behind for displaying the "softness" property of Couches & Chairs:

public double Softness
{
    get { return (double)GetValue(SoftnessProperty); }
    set { SetValue(SoftnessProperty, value); }
}

// Using a DependencyProperty as the backing store for Softness.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty SoftnessProperty =
    DependencyProperty.Register("Softness", typeof(double), typeof(SoftnessVisualizer), new PropertyMetadata(SoftnessChanged));

public static void SoftnessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    if(obj is SoftnessVisualizer)
    {
        ((SoftnessVisualizer)obj).ScaleSoftness();
    }
}

protected void ScaleSoftness()
{
    if (Softness > 0.0)
    {
        VisualScale.ScaleX = Softness;
        VisualTranslate.X = 10 * Softness;
    }
    else
    {
        VisualScale.ScaleX = 1.0;
        VisualTranslate.X = 0.0;
    }
}

/// <summary>
/// We know we're just for softness!
/// </summary>
/// <param name="instance"></param>
/// <param name="desc"></param>
public void Initialize(DamonPayne.AGT.Design.IDesignableControl instance, DamonPayne.AGT.Design.Types.DesignablePropertyDescriptor desc)
{
    if (null != instance)
    {
        Binding b = new Binding("Softness");
        b.Converter = desc.Converter;
        b.Mode = BindingMode.TwoWay;
        b.Source = instance;
        SetBinding(SoftnessVisualizer.SoftnessProperty, b);
    }
}

The visual representation of softness here is a triangle that I am stretching based on Softness but that's not important.  In Initialize we set up a binding to the particular Property we care about which handles keeping in sync with the underlying object.  Using a changed callback for the Softness DependencyProperty allows us to tweak the UI if an editor changes this value in some fashion.

You can see here that I am making a heavy dependence on System.Windows.Data.Binding concepts which in turn means DependencyProperties.  This is OK as far as I'm concerned.  The Binding in Silverlight/WPF is the Data Binding you've always wished you had.  It is awesome.  It is likely going to be used frequently and you should learn it, love it, learn where it breaks down, and return to loving it.

 

The PropertyGrid logic is now fairly simple and just swaps an Edit Visual out for a Display Visual when the Display Visual is clicked on.

 

The ColorConverter I wrote may be helpful as well...

public class ColorConverter : IValueConverter
{

    static ColorConverter()
    {
        _colorNameToColor = new Dictionary<string, Color>();
        _colorToName = new Dictionary<Color, string>();
        Type cType = typeof(System.Windows.Media.Colors);
        PropertyInfo[] colors = cType.GetProperties(BindingFlags.Public | BindingFlags.Static);
        foreach (var propInfo in colors)
        {
            var c = (Color)propInfo.GetValue(null,null);
            _colorNameToColor.Add(propInfo.Name, c);
            _colorToName.Add(c, propInfo.Name);
        }
    }

    private static Dictionary<string, Color> _colorNameToColor;
    private static Dictionary<Color, string> _colorToName;


    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is Color)
        {
            Color src = (Color)value;
            if (targetType == typeof(string))
            {
                if (_colorToName.ContainsKey(src))
                {
                    return _colorToName[src];
                }
                else
                {
                    return src.ToString();
                }
            }
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is string)
        {
            string src = value.ToString();
            if (targetType == typeof(Color))
            {
                if (_colorNameToColor.ContainsKey(src))
                {
                    return _colorNameToColor[src];
                }
            }
        }

        return null;
    }
}

How does it look?

I needed to create some better visuals than the placeholders I had at the end of the previous article.  The default ControlTemplate for PropertyGrid now looks like this with a Chair selected.  Click on any image for the larger version.

Vision14

Note my special visualizer (I am no artist) for Softness.  When I select Softness, I get a custom Editor as well:

 Vision15

With a chair, a couch, and a Dummy Button selected, the only Property they have in common is Name, so only name shows up in the grid:

Vision16

Couches and Chairs have lots in common, so I could edit the Color of them all at once:

Vision17

 

Unit Tests

Jeff Wilcox released a full Silverlight 2 version of his Silverlight Unit Test Framework, so the AGT Solution has been updated to use this.  I have yet to find new Templates, so the Templates still say Beta and the default test project has to be tweaked slightly.  This is extremely handy for things like IValueConverters, and I will be greatly increasing test coverage in the future.

Conclusion

So, we've got Property Editing working now and this is starting to look like a real Design Time Environment.

The source code is checked into Codeplex as downloadable Change Set 8353 here.  I have also created a 0.7.17.2 "Planning" source drop on Codeplex.

 

The Live Demo has been updated at http://www.damonpayne.com/agt/

 

In the next article, we will be going through some refactoring items and some questions that I've been meaning to get to.



Tuesday, December 02, 2008 3:57:51 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, November 21, 2008

Regular readers may observe that my interest in Silverlight is primarily in making business applications and Visual Tools. I am not an artist and I don't stream music or movies.  No, I'd just like to see Silverlight replace HTML, CSS, and Javascript.  There are a few issues with this plan right now (other than wish thinking) - binding ought to be closer to what WPF has.  There are some handy controls missing. 

There's no built in Validation strategy. 

Obviously nothing stops me from doing validation in a completely manual fashion but it's unlike Microsoft to leave holes like this...

Strategy

We have Extension Methods, but not Extension Properties or Extension Events, so simply adding Validating event handlers isn't an option.  What's more, the typical Windows Forms strategy requires an Extender Provider framework - perhaps when I get further on the AGT Project I'll revisit this, but for now something quicker to implement would be better.

For some reason the ASP.Net model was intriguing to me given the current state of Silverlight.

  1. The Validators have a visual representation and function essentially as a Control.
  2. The Validators "point to" the Control they will validate, so they are completely outside the rest of the functionality on the Page so to speak.
  3. Making sure to trigger Validation via code like if(Page.IsValid) { /* do something*/} is a fair enough way to write code.

So, could we create something like this, but in a reasonably Silverlight-y way? Read on.

Test Form and three sample validation scenarios

I have created a very simple Wine Order Form:

Form0

I have some simple data requirements for this order form.

  • For Name, I'd like something similar to the Asp.Net RequiredFieldValidator
  • For Email, I'd like a RegexValidator
  • For Bottles of Wine I'd like a RangeValidator

Design Goals

So, how would I like this to work and what should the programming experience be?

  • I'd like some sort of Control in the Visual Tree representing the Validation state of an associated Control; it should be easy to Template
  • I do not want to require any interface implementation, base classes, or any changes to the Visual Tree besides the addition of the Validation visuals. (ie no <v:ValidationArea>... your XAML here </v:ValidationArea>, although this is a common enough model in Silverlight and WPF)
  • Calling an arbitrary # of Validators on any given Control or Panel should require as little code as possible
  • Adding new Validator types should be as easy as possible.
  • It should feel like the Validators have saved programming time and created consistency versus pure manual validation.

 

Implementation

How did I do on these goals?  Let's see.

Validator Control Class

As I stated, I've taken the approach of creating a Control which will live in the Silverlight Visual Tree.   Any inheritance involving XAML & User Controls is painful or impossible, but I wanted to see about directly inheriting from Control and using a ControlTemplate instead of working at any kind of visual inheritance.  I'm happy with the results.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DamonPayne.AGContrib.Validation
{
    [TemplatePart(Name = "LayoutRoot", Type = typeof(Panel))]
    public class Validator : Control
    {
        public Validator()
        {
            DefaultStyleKey = typeof(Validator);
            Loaded += new RoutedEventHandler(Validator_Loaded);
        }

        private Panel _layoutRoot;

        void Validator_Loaded(object sender, RoutedEventArgs e)
        {
            CheckValidState(this);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _layoutRoot = (Panel)GetTemplateChild("LayoutRoot");
        }

        /// <summary>
        /// A single Framework Element to display when Valid, perhaps an empty TextBlock?
        /// </summary>
        public FrameworkElement ValidContent
        {
            get { return (FrameworkElement)GetValue(ValidContentProperty); }
            set { SetValue(ValidContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ValidContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValidContentProperty =
            DependencyProperty.Register("ValidContent", typeof(FrameworkElement), typeof(Validator), new PropertyMetadata(new TextBlock()));



        public FrameworkElement InValidContent
        {
            get { return (FrameworkElement)GetValue(InValidContentProperty); }
            set { SetValue(InValidContentProperty, value); }
        }

        //Inheritance, does NOT like sharing visual elements, very sneaky
        public static readonly DependencyProperty InValidContentProperty =
            DependencyProperty.Register("InValidContent", typeof(FrameworkElement), 
            typeof(Validator), 
            new PropertyMetadata(new TextBlock { Text="*", Foreground=new SolidColorBrush(Colors.Red) }));



        /// <summary>
        /// Tool tip content, possibly explaining more information about why the value is invalid
        /// </summary>
        public FrameworkElement InvalidToolTip
        {
            get { return (FrameworkElement)GetValue(InvalidToolTipProperty); }
            set { SetValue(InvalidToolTipProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InvalidToolTip.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InvalidToolTipProperty =
            DependencyProperty.Register("InvalidToolTip", typeof(FrameworkElement), typeof(Validator), new PropertyMetadata(null));


        /// <summary>
        /// Is the target Control currently valid
        /// </summary>
        public bool IsValid
        {
            get { return (bool)GetValue(IsValidProperty); }
            set { SetValue(IsValidProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsValid.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsValidProperty =
            DependencyProperty.Register("IsValid", typeof(bool), typeof(Validator), new PropertyMetadata(true, IsValidChanged));


        public static void IsValidChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is Validator)
            {
                var v = (Validator)obj;
                CheckValidState(v);
            }
        }

        /// <summary>
        /// show/hide the valid/invalid FrameworkElement
        /// </summary>
        /// <param name="v"></param>
        private static void CheckValidState(Validator v)
        {
            if (null != v.ValidContent && null != v.InValidContent)
            {
                if (v.IsValid)
                {
                    v.ValidContent.Visibility = Visibility.Visible;
                    v.InValidContent.Visibility = Visibility.Collapsed;
                }
                else
                {
                    v.ValidContent.Visibility = Visibility.Collapsed;
                    v.InValidContent.Visibility = Visibility.Visible;
                }
            }
        }

        /// <summary>
        /// The name of the Control within the visual tree we should try to validate
        /// </summary>
        public string ControlToValidate
        {
            get { return (string)GetValue(ControlToValidateProperty); }
            set { SetValue(ControlToValidateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ControlToValidate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ControlToValidateProperty =
            DependencyProperty.Register("ControlToValidate", typeof(string), typeof(Validator), new PropertyMetadata(null));


        /// <summary>
        /// When overridden in a derrived class, checks for a valid value
        /// in <paramref name="ctv"/>
        /// </summary>
        /// <param name="ctv"></param>
        /// <returns></returns>
        public virtual bool Validate(FrameworkElement ctv)
        {
            return true;
        }

        /// <summary>
        /// Helper method for derived classes
        /// </summary>
        /// <param name="fe"></param>
        /// <returns></returns>
        protected virtual string GetInputValue(FrameworkElement fe)
        {
            string val = string.Empty;
            if (fe is TextBox)
            {
                val = ((TextBox)fe).Text;
            } //else if ComboBox, etc.
            return val;
        }
    }
}

We define three dependency properties for showing content, presumably using a ContentPresenter in the default template for Validator.  ValidContent, InValidContent, and InvalidToolTip.  We leave the Validate function empty, and the work of showing or hiding the correct content happens in CheckValidState.

Default Validator ControlTemplate

We target Validator with this ControlTemplate:

<ControlTemplate TargetType="local:Validator">
    <Canvas x:Name="LayoutRoot">
        <ContentPresenter x:Name="ValidPresenter" Content="{TemplateBinding ValidContent}"></ContentPresenter>
        <ContentPresenter x:Name="InValidPresenter" Content="{TemplateBinding InValidContent}">
            <ToolTipService.ToolTip>
                <ContentPresenter x:Name="InvalidToolTipPresenter" Content="{TemplateBinding InvalidToolTip}"></ContentPresenter>
            </ToolTipService.ToolTip>
        </ContentPresenter>
    </Canvas>
</ControlTemplate>

This should be easy to understand.   Since this is an early experiment I haven't created any Visual States yet.

 

Validator Implementations

Surprisingly, creating classes that extend Validator worked out fairly well.  We can review the main pitfall in the ValidContentProperty Dependency Property registration.  Note the default value inside the PropertyMetadata constructor.  I left this in to make a point - only one instance of this TextBlock will be created, and a single Control cannot appear multiple times in the Visual Tree.  Be sure to provide values in each instance or you will get hard to grok errors.  For the sake of brevity, we can just review the implementation for RegexValidator:

using System.Text.RegularExpressions;
using System.Windows;

namespace DamonPayne.AGContrib.Validation
{
    public class RegexValidator  : Validator
    {
        public RegexValidator()
        {
            DefaultStyleKey = typeof(Validator);
        }

        public string RegularExpression
        {
            get { return (string)GetValue(RegularExpressionProperty); }
            set { SetValue(RegularExpressionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RegularExpression.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RegularExpressionProperty =
            DependencyProperty.Register("RegularExpression", typeof(string), typeof(RegexValidator), new PropertyMetadata(string.Empty));

        public override bool Validate(FrameworkElement ctv)
        {
            if (!string.IsNullOrEmpty(RegularExpression))
            {
                Regex r = new Regex(RegularExpression);
                string text = GetInputValue(ctv);

                return r.IsMatch(text);
            }
            return true;
        }
    }
}

There are a couple of things to note here.  Because this class is simply adding some logic we use DefaultStyleKey = typeof(Validator); in the constructor.  We could conceivably want different Validators to have utterly different visual representations, but in those cases we can create a ControlTemplate for those instances only.  Surprisingly this works just fine.  We've defined a RegularExpression property we can set from XAML and overridden Validate to do the Regex check.

Adding the Validators in XAML

Given all of this, it's easy to add the Validators onto my order form:

<!-- Name Row-->
<TextBlock Grid.Row="1">Name:</TextBlock>
<TextBox x:Name="NameTxt" Grid.Row="1" Grid.Column="1" Height="25" VerticalAlignment="Top"></TextBox>
<v:RequiredFieldValidator x:Name="NameReq" ControlToValidate="NameTxt" Grid.Column="2" IsValid="True">                
</v:RequiredFieldValidator>            

Obviously, the value of ControlToValidate must be a valid Name somewhere in the visual tree.  Because we allow the display of anything for valid vs. invalid Controls, and we can set the initial valid state my order form now looks like this when first loaded, calling out the fact that Wine Bottles needs a real value:

Form1

Firing the Validators

Given the primitive state of the example application so far, a single extension method for type FrameworkElement will cause all of the validators contained within that FrameworkElement to do their thing.  The extension code is worth taking a quick look at.

namespace DamonPayne.AGContrib.Validation
{
    public static class FrameworkElementExtensions
    {
        /// <summary>
        /// Search the control's Visual Tree for Validators, and run them!
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static bool IsValid(this FrameworkElement c)
        {
            bool valid = true;
            var lv = FindAllValidators(c);

            foreach (var v in lv)
            {
                var ctv = (Control)c.FindName(v.ControlToValidate);
                if (null != ctv)
                {
                    if (!v.Validate(ctv))
                    {
                        valid = false;
                        v.IsValid = false;
                    }
                    else
                    {
                        v.IsValid = true;
                    }
                }
            }

            return valid;
        }

        public static List<Validator> FindAllValidators(FrameworkElement fe)
        {
            return FindAllValidators(fe, new List<Validator>());
        }

        public static List<Validator> FindAllValidators(FrameworkElement fe, List<Validator> lst)
        {
            int count = VisualTreeHelper.GetChildrenCount(fe);
            for (int i = 0; i < count; ++i)
            {
                DependencyObject d = VisualTreeHelper.GetChild(fe, i);
                if (d is Validator)
                {
                    lst.Add((Validator)d);
                }
                if (d is FrameworkElement)
                {
                    FindAllValidators((FrameworkElement)d, lst);
                }
            }
            return lst;
        }

    }    
}

You may note that only certain elements such as Canvas define a "Children" property.  VisualTreeHelper is the only way I know of to walk the Visual Tree in a generic fashion.  This allows us to look for Validators within the target FrameworkElement no matter how nested.  I will revisit my choice of FrameworkElement as the entry point.  With this Extension Method, validating my form when I attempt to submit is easy, "Form" being the name of my top level Grid.

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (Form.IsValid())
    {
        //Do something meaninful
    }
}

So, if we push the Submit button without doing anything, we see this ...

Form2

... but providing valid values ...

Form3

 

Conclusion

So, what do you think?  Is this worth a little more work?  I would probably add some kind of Validation Summary, some Visual States to Validator, potentially support Validation Groups, and add a way to dynamically fire the Validators when a Control loses focus.

Send me a note with your comments.

Source code:

DamonPayne.AGContrib.zip (44.75 KB)


Friday, November 21, 2008 11:03:43 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, November 19, 2008

The talk went very well last night and I got very positive feedback.  Thanks to John from Microsoft for helping with the Projector system, which mysteriously shut down at special pre-programmed times twice during the night; this was a complex topic and the audience was excellent.

Now that I'm done preparing for this, you can expect the next AGT article to happen soon.

Here are the slides and code from the talk:

AGSIGControlTeplatePreso.zip (1.03 MB)


Wednesday, November 19, 2008 12:06:31 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, November 18, 2008

http://www.wi-ineta.org/DesktopDefault.aspx?tabid=192

Hopefully I will see some of you there at 6pm in the Microsoft offices.  I think I'll be visiting the bar at Thunder Bay Grill next door after the show...



Tuesday, November 18, 2008 11:27:16 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, November 10, 2008

I will be speaking next week at the Silverlight Special Interest Group, a subset of the Wisconsin .NET user's group.  The topic is Control Templating, Styling, and the Visual State Manager.  For more details check out:

http://www.wi-ineta.org/DesktopDefault.aspx?tabid=192

 

 



Monday, November 10, 2008 10:46:45 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, November 06, 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

Yes, I've switched to Live Writer full time now.  The VSPaste plug-in is excellent.

Editing Properties

The theory is baked and the anticipation is high, it’s time to edit Properties inside the designer. I add the DesignablePropertyDescriptor type to the solution and add the GetDesignProperties method to the IDesignableControl interface. With this added I need to go about providing implementations. This project is going to start and end with the purple Couch.

public Color FillColor { get; set; }

        public List<DesignablePropertyDescriptor> GetDesignProperties()
        {
            return new List<DesignablePropertyDescriptor>();
        }

I need to implement this for the FillColor property.

public List<DesignablePropertyDescriptor> GetDesignProperties()
      {
          List<DesignablePropertyDescriptor> props = new List<DesignablePropertyDescriptor>();
          
          DesignablePropertyDescriptor fillColor = new DesignablePropertyDescriptor();
          fillColor.PropertyInfo = GetType().GetProperty("FillColor");
          fillColor.DisplayName = "Color";
          fillColor.DisplayType = typeof(TextBlock);
          fillColor.Editable = true;
          fillColor.EditorType = typeof(ComboBox); 
          fillColor.SupportsStandardValues = true;
          fillColor.StandardValues = new List<object>( new object[] {Colors.Purple, Colors.Red, Colors.Gray, Colors.Brown, Colors.Gray});
            return props;            
        }

I’m already questioning the decision not to use attributes, that’s a lot of code for one property. Still, I’ll follow this through for now. Later I’ll dig up some WinForms designer code for comparison. Now I need to provide my default implementation for IDesignableControlInspector.

    public interface IDesignableControlInspector : IService
    {
        List<DesignablePropertyDescriptor> Inspect(IDesignableControl idt);
    }
    public class DefaultInspector : IDesignableControlInspector
    {
        public System.Collections.Generic.List<DesignablePropertyDescriptor> Inspect(IDesignableControl idt)
        {
            if (null != idt)
            {
                return idt.GetDesignProperties();
            }
            return null;
        }
    }

Refactor: At this point, I can see that my IService interface is broken. I’m forcing myself to implement Startup() and Shutdown() methods that I have yet to use in any meaningful way. Removing Startup from the IService definition gives a single compiler error from ServiceManager. The methods are gone until I need them. Of course, this leaves IService as a simple “marker” interface with no methods. Not terribly evil, but there are better ways to supply what is essentially metadata in .NET.

Now I create the IDesignEditorService interface:

public interface IDesignEditorService:IService
{
    Control Visual { get; }
    void Edit(IList<IDesignableControl> targets);
}

For now, I will leave IDesignablePropertyEditor alone.  I need to create a PropertyGrid control and wire up the code needed to use it.  The specifics of making the templatable PropertyGrid will be in the next article.  The PropertyGrid must implement IDesignEditorService.

public Control Visual
{
    get
    {
        return this;
    }
}

public void Edit(IList<IDesignableControl> targets)
{
    //TODO: something
}

Now, in order to begin doing our work of displaying and editing properties, we need to get whatever IDesignableControlInspector has been registered.

public PropertyGrid()
{
    DefaultStyleKey = typeof(PropertyGrid);
    Inspector = ServiceManager.Resolve<IDesignableControlInspector>();
}

IDesignableControlInspector Inspector { get; set; }

And now we can begin doing some work inside Edit. 

public virtual void Edit(IList<IDesignableControl> targets)
{
    if (null != targets && targets.Count > 0 && null != targets[0])
    {
        if (targets.Count > 1)
        {
            EditMultiple(targets);
        }
        else
        {
            EditSingle(targets[0]);
        }
    }
    else
    {
        ClearProperties();
    }
}

I've decided it really would be nice to handle editing multiple items with the same Properties, so we'll do that too.  In order to keep methods small and readable and provide some extra support for alternative implementations of IDesignEditorService I have created EditServiceHelper to assist with the various reflection and control instantiation operations.  In order to more easily see how well this is working, I start by wiring up Selection events with the PropertyGrid.  This means that my DesignSurface control now needs to get an IDesignEditorService injected and we need to handle selection events.

private ISelectionService _selectionSvc;

public ISelectionService SelectionSvc 
{
    get
    {
        return _selectionSvc;
    }
    set
    {
        if (null != _selectionSvc)
        {
            _selectionSvc.SelectionChanged -= new EventHandler(SelectionSvc_SelectionChanged);
        }
        _selectionSvc = value;
        _selectionSvc.SelectionChanged += new EventHandler(SelectionSvc_SelectionChanged);
    }
}
void SelectionSvc_SelectionChanged(object sender, EventArgs e)
{
    List<IDesignableControl> idtList = new List<IDesignableControl>();
    foreach (var idt in SelectionSvc.GetSelection())
    {
        if (idt is DesignSite)
        {
            idtList.Add(((DesignSite)idt).HostedContent);
        }
        else
        {
            idtList.Add(idt);
        }
    }
    EditSvc.Edit(idtList);    
}      

Ok, nothing crazy so far, then things got interesting.  How to generically have some kind of binding between "some visual representation of a property" and "some property on an IDesignableControl" ?  I went back and added another property to my Couch, namely the "DesignTimeName", in order to illustrate this better.  This is where things stand now inside Couch.GetDesignProperties()

public List<DesignablePropertyDescriptor> GetDesignProperties()
{
    List<DesignablePropertyDescriptor> props = new List<DesignablePropertyDescriptor>();

    DesignablePropertyDescriptor name = new DesignablePropertyDescriptor();
    name.PropertyInfo = GetType().GetProperty("DesignTimeName");
    name.DisplayName = "Name";
    name.Editable = true;
                
    DesignablePropertyDescriptor fillColor = new DesignablePropertyDescriptor();
    fillColor.PropertyInfo = GetType().GetProperty("FillColor");
    fillColor.DisplayName = "Color";
    fillColor.DisplayType = typeof(TextBlock);
    fillColor.Editable = true;
    fillColor.EditorType = typeof(ComboBox); 
    fillColor.SupportsStandardValues = true;
    fillColor.StandardValues = new List<object>( new object[] {Colors.Purple, Colors.Red, Colors.Gray, Colors.Brown, Colors.Gray});

    props.Add(name);
    props.Add(fillColor);

    return props;            
}

Inside PropertyGrid.EditSingle, we build up our Grid rows & columns using the EditServiceHelper I mentioned earlier.

protected virtual void EditSingle(IDesignableControl idt)
{
    List<DesignablePropertyDescriptor> props = Inspector.Inspect(idt);
    var propCount = 0;
    foreach (var descriptor in props)
    {
        _propertyPart.RowDefinitions.Add(new RowDefinition());
        TextBlock tb = new TextBlock();
        tb.Text = descriptor.DisplayName;                
        tb.SetValue(Grid.RowProperty, propCount);
        tb.SetValue(Grid.ColumnProperty, 0);
        //
        FrameworkElement displayElement = EditServiceHelper.GetBoundDisplayElement(descriptor, idt);
        displayElement.SetValue(Grid.RowProperty, propCount);
        displayElement.SetValue(Grid.ColumnProperty, 1);
        displayElement.GotFocus += new RoutedEventHandler(displayElement_GotFocus);
        //
        _propertyPart.Children.Add(tb);
        _propertyPart.Children.Add(displayElement);
        ++propCount;
    }
}

That's not too bad so far.  Here's a screenshot with my so far, so lame PropertyGrid template.

Vision12

I'm going to skip over the implementations inside EditServiceHelper for now, it's in the code if you're curious.  Suffice to say it contains some Property to Control mappings for some basic supported types.  For example, a Property of Type string will be both displayed and edited using a TextBox element and a Two Way Binding. When we get into more complex Visualization and Edit scenarios in a later article.

The next thing we need to do is handle a couple of simple edit scenarios when the display element gets focus, as shown above in EditSingle.

void displayElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    VerifyEditVisualization(sender);   
}

void displayElement_GotFocus(object sender, RoutedEventArgs e)
{
    VerifyEditVisualization(sender);            
}

private void VerifyEditVisualization(object sender)
{
    if (null != _targets && _targets.Count > 0)
    {
        FrameworkElement element = (FrameworkElement)sender;
        Type t = sender.GetType();
        DesignablePropertyDescriptor descriptor = _descriptorMap[element];
        Type editType = EditServiceHelper.GetEditElementType(descriptor);
        if (!t.Equals(editType))
        {
            FrameworkElement editElement = EditServiceHelper.GetBoundEditElement(descriptor, editType, _targets[0]);
            editElement.SetValue(Grid.RowProperty, element.GetValue<int>(Grid.RowProperty) );
            editElement.SetValue(Grid.ColumnProperty, 1);
            _propertyPart.Children.Add(editElement);
            _editElement = editElement;
        }
        //else do nothing, the Editor is the same as the Displayor
    }
}

One thing you can see immediately is that my colors are listed using their Hex #s, so I need to introduce the value converters into the mix.  I also need to fill in a better look and feel for the PropertyGrid, handle Multiple IDC Edit,  and invent a scenario where I can test out the IDesignablePropertyEditor idioms.  This article is already getting a bit long, so I will do some more cleanup and stop here for now.

Making Property Registration Easier

I'm still thinking that writing code for this property edit scheme this will be easier than the current WinForms equivalent, but I realized immediately that it could be better.  I have created a couple of new methods in EditServiceHelper:

/// <summary>
/// Returns common props from IDesignableControl : DesignTimeName, 
/// </summary>
/// <returns></returns>
public static List<DesignablePropertyDescriptor> GetDefaultDescriptors()
 
/// <summary>
/// Return default <code>DesignablePropertyDescriptor</code> objects for each Property name
/// </summary>
/// <param name="names"></param>
/// <returns></returns>
public static List<DesignablePropertyDescriptor> GetDescriptors(IEnumerable<string> names, IDesignableControl idc)

The default can be merged with any custom list a control author wishes to return.

 

Updating the Existing Controls

Refactor: The Couch, Chair, Center channel speaker, and Tower speaker controls were all refactored to use the new convienience methods to return their own properties.  The descriptions no longer say "red chair" or "purple" couch, since we can actually edit the properties of items on the surface now!

Here's what the application looks like now:

Vision13

Yes, we left Property Editing half done in order to keep each article shorter.

 

Conclusion

If you're following the source code, there were several tiny refactorings made that weren't mentioned here.  Speaking of code, as I previously blogged, the project is in CodePlex now at http://www.codeplex.com/agt. The current source code for this article, then, is the 0.7.16.1 Release.  Tune in next time to see Property Editing reach a more finished state.

The live demo has been updated at http://www.damonpayne.com/agt



Thursday, November 06, 2008 5:20:51 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

The various source drops were taking up a lot of space on my server and I promised to open source this tool, and now it's done.  The AGT project has moved to Codeplex.

http://www.codeplex.com/agt

I have not published an actual Release yet, but from this point forward I'll be tagging a release at the end of each article.  Parts [16] and [17] are in progress now.



Thursday, November 06, 2008 10:14:17 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, October 30, 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

Choosing an Approach to Editing Properties

One of the last top level pieces to begin fleshing out is Property Editing.  I don’t want to see “Red Couch” in the toolbox anymore: I want to see “Couch” and to change its color at runtime by editing his properties.  Having had a lot of experience writing things like this so as to fit neatly within the framework Microsoft gives us, I felt very familiar with the benefits and drawbacks of these approaches.  I had to ask myself some questions:

1.       Is the PropertyGrid really the best way to go about changing attributes of objects on a design surface?

2.       Is a Visual Studio style grid the best route?  Maybe a smaller gird that appears right next to a Component (ala Visio) is better?  Maybe there should be more interaction with the Component itself?

3.       How important is it to support the editing of a Property for many Components at once?

4.       How important is Undo?  Redo?

5.       What is the best contract between IDesignableControl and “the design time environment” for determining which properties are editable and how they should be edited?

When writing custom controls for the Visual Studio designer, it always bothered me that the Contract between my Controls and the Designer Time Environment was simply some attributes on public properties.  Now that I’m in a position to change it for my own world, I’m having trouble thinking of something better.  My ideas on what is the most “correct” in terms of Object- and Component- oriented semantics is not the only thing at play here.  As a component creator, what do I want my software development experience to like?  Is placing eight attributes on a property the easiest and most sensible way?  If I diverge from what’s present in the Visual Studio design time environment, how much extra work am I creating?

What design-time support is present in Silverlight anyway?

Designer Support native to Silverlight

What namespaces, classes, interfaces, and attributes we are accustomed to from “full framework” design time support have made it into Silverlight?

System.Web.UI.Design.SilverlightControls

This namespace only contains things related to a Silverlight plug-in control on ASPX pages.  There’s nothing useful for us here.

System.ComponentModel

Many of the familiar friends are here:

·         CategoryAttribute – Place this on a property indicating what category the property belongs to in a categorized PropertyGrid.

·         DefaultValueAttribute – specify the default value for a property

·         DescriptionAttribute- a Description for a property or event

·         DesignerProperties class.  This one is interesting.  At the time I started this article, RTW was not quite out and the documentation on MSDN didn’t really say anything about what this actually did.  The description now says “The DesignerProperties class provides attached properties that can be used to query the state of a control when it is running in a designer. Designer tools will set values for properties on objects that are running in the designer.” Ultimately, though, this is an attached property for “IsInDesignMode”, which I don’t know if I’ll need or not.

·         EditorBrowsableAttribute – In what situations should this property be shown by designers or intellisense

·         TypeConverter – TypeConverter is missing some things from the full framework. The MSDN documentation still says it supports “standard values” but those methods (to return a standard values collection) are missing in Silverlight.

·         TypeConverterAttribute –present and accounted for!

Missing in action:

·         System.ComponentModel.TypeDescriptor – In the full framework, TypeDescriptor is extremely important to the design-time environment.  TypeDescriptor is used to augment Reflection to specify what attributes a Component has. 

o   TypeDescriptor tells us what kind of Editor is used to modify an instance of a Component

o   TypeDescriptor tells us what kind of Designer a particular component uses

o   TypeDescriptor is used to create design-time instances of a type

·         TypeDescriptor is extensible via IExtenderProvider, ITypeDescriptorFilterService, ICustomTypeDescriptor, all missing.

·         System.ComponentModel.Editor

·         System.Drawing.UITypeEditor

In some cases I’ve already made design decisions that negate the need for some of these missing things.  Other’s I’ll have to introduce or replace.

Design Goals

Property editing is complex.  Now that I see the lay of the land, I can state some design goals and some approaches to achieving them.

1.       The items on the design surface (IDesignableControl) need to be able to be inspected for a list of Properties that are displayable and editable.  It should be painless for Control authors to do this.

2.       When a property is found to be displayable, we must be able to determine what type of visual Control should be used to display the property value.

3.       When a property is found to be editable, we must be able to determine:

a.       The type of the property

b.      The category of the property

c.       What type of visual Control should be used to edit the property value

4.       We should be able to display all of this in a PropertyGrid-esque fashion.

In order to reach these goals, I decide on the following:

1.       I’m going to try replacing the attribute based Component-Designer contract mechanism with new and modified interfaces.

2.       I’m going to make the Component inspection mechanism pluggable, in case someone really wants to fall back on Reflection.

3.       I’m going to come up with some better named interfaces and types to use to modify property values.

4.       I’m going to build some helpers/extension methods to make this painless for IDesignableControl authors.

5.       I’m going to build a PropertyGrid from scratch.

Working on these ideas, I have come up with this:

Since no code was written as part of this article, the most updated code is still AGT[14].

In the next article we’ll look into implementing this approach.



Thursday, October 30, 2008 1:40:30 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 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
 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

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;

                        }

                        else

                        {

                            Log.Log(new DamonPayne.AG.IoC.Types.LogMessage

                            {

                                Level = DamonPayne.AG.IoC.Types.LogLevels.Debug,

                                Message = r.ToString() + " does not contain " + corners[i]

                            });

                        }

                    }

                }

            }

 

            SelectionSvc.Select(selection);

        }

This feels dirty, like I’m still thinking like a Winforms developer, but I’m testing so far with controls that are not Transformed or anything hokey, so I’m disappointed that the VisualTreeHelper method misbehaved on even the simplest case.  This code works exactly as I’d hope it to.  I’m ready to set the lasso aside and move on.

The live demo has been updated!

Source code: DamonPayne.AGT[13].zip (1.47 MB)



Thursday, October 09, 2008 7:49:30 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 08, 2008

I have had a couple of requests to post the entire source code for my Silverlight 2 Image carousel.  I plan on updating the code for RC0, cleaning it up, and making it not specific to Images, and posting the code here.  Stay tuned.



Wednesday, October 08, 2008 10:09:50 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, October 07, 2008

This is a roundup for the AGT project: what have we done so far?

If you’re just joining me, I always put the following text at the beginning of each article in the series I’m currently working on:

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.

What I'm working on here is a framwork I can use to build something that looks and behaves like a Visual Studio Shell or a Visio, but runs inside the browser using Silverlight.  I of course won’t claim to have duplicated the functionality in those tools, which is massive.  Rather, I’m going for the “80/20” rule; I’m hoping that I can achieve huge amounts of functionality for very little work with an implementation that is understandable yet flexible.  I plan on putting this on Codeplex once I get to a decent v1 milestone. 

Summary of the ground covered so far:

·         AGT[0] – Vision statement

·         AGT[1] – Initial thoughts on Service/plug-in/add-in/module strategy and Composite UI management

·         AGT[2] – Dependency injection, Presenters, Event Aggregation

·         AGT[3] – Creating the initial designer metaphors

·         AGT[4] – Toolbox service, ViewModel, Data binding with INotifyCollectionChanged

·         AGT[5] – Generating dynamic proxies for Service providers using Reflection Emit

·         AGT[6] – Creating a design for drag/drop interactions in Silverlight.

·         AGT[7] – Implementing drag ‘n drop from the Toolbox to the Design surface

·         AGT[8] – Code refactoring and introduction of Unit Tests for Silverlight

·         AGT[9] – Visual refactoring, new Toolbox items

·         AGT[10] – Creating a Selection service, publish live demo

·         AGT[11] – Updating the codebase for Silverlight 2 RC0

·         AGT[12] – Resizing items on the designer surface

This is Reality Blogging, I really am writing the code as I go.  Some of the embarrassing refactoring should serve as proof of this.  Still, it’s completely fair to think about a roadmap and potential problems.  I do have an idea of where all this is going and what might constitute a reasonable v1 release.  Here are some additional things I'd like to support.

1.       Multi-select items by drawing a rectangle “lasso” on the surface

2.       Moving  the selected stuff around on the surface with the mouse and keyboard

3.       Property Editing contract: When an IDesignableControl is selected, how will I determine what properties show up in my “property grid” and how they look?

4.       Property editing grid: with the above decided, a PropertyGrid will have to be built from scratch!

5.       Supporting Silverlight 2’s Full Screen mode

6.       Support zoom in/zoom out at the document level

7.       Change the look of the designer using some kind of Settings service.

8.       Design Surface Behavior concepts:  I have this idea that I might provide flexibility as to how things look and behave on the Designer Surface using a “behavior chain”, which I envision will function as a set of mini-Services hosted by the Design Surface itself. I thought of some things I might do to prove the design works:

a.       Alignment snap-lines ala Visual Studio?

b.      Rotate “adorner” to allow things to be rotated?

c.       Measure adorner to selected items show a “size”?

d.      Refactor multi-select to be a behavior of this type?

9.       Serialization/IDesignableControl<T>: In order to be useful, my “drawings” will need to be saved and re-loaded into the designer!  I need to serialize the scene graph into some kind of "document".

10.   Hierarchy of service containers via some notion of Context.  Stay tuned for explanation on this one…

11.   “Stencil” sets: how to provide drop-in flexibility for “what kind of document is this?”

12.   Dedicated refactoring articles as I go.

If I am correct and these things can mostly be done in one article each, I might be up to something like AGT[30] before I’m ready to put this on Codeplex as an v1 release. I'm excited about what's to come, but I only have so much time for research so I will take each step as I can.



Tuesday, October 07, 2008 12:33:30 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, October 06, 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

Resizing

As I look at the project so far, it seems somehow more natural to want to move things before selecting or resizing, but the notion of Selection had to come first, which has a dependency on my Site metaphor.  Since the selection border is already in place, why not move on to resizing next?

While contemplating resizing, rotation (which I plan on adding eventually), and selection I had a dilemma.  I felt there were two main paths:

1.       Draw adorners.  This means the IDesignableControl lives on the surface mostly as itself, and when an interesting event happens (like selection) we have the surface draw a border with the familiar “grab knobs” at the corners. 

2.       Container/Site: I first thought of creating a Grid to hold the IDesignableControl within it, since this would provide the table structure needed to draw “knobs” as well as the familiar selection border.

I wrote the code for the Grid first while working in my third office (http://www.ale-house.com/alehouse/index.php?option=com_content&task=view&id=23&Itemid=57 ) without the source for the project, which lives on the workstation in my second office (http://www.flickr.com/photos/damonrpayne/528817886/ ) and ultimately didn’t’ like it, but I did learn enough to see that a Silverlight Border would make resizing incredibly easy.  I have a vague notion of an IDesignSurfaceBehavior I can use for true Adorner flexibility later, but for selection and resizing using a UserControl with a Border was just so simple I had to start by going down this path.

DesignSite

The UserControl serving as border container is the DesignSite.  It has a Width and Height of Auto and the Border has a Width and Height of Auto as well, to make resizing a piece of cake.  The XAML couldn’t be easier.

        <Border x:Name="SiteBorder" BorderThickness="0" BorderBrush="{StaticResource SelectionBorderBrush}" CornerRadius="7" Cursor="Hand" MouseLeftButtonDown="SiteBorder_MouseLeftButtonDown" MouseLeftButtonUp="SiteBorder_MouseLeftButtonUp" MouseMove="SiteBorder_MouseMove">

            <Border.Child>

                <TextBlock>Default Content</TextBlock>

            </Border.Child>

        </Border>

The code is fairly straightforward too.  For most things we are using a pattern of delegation to the contained instance to make future designer interactions easier.  In order to properly calculate resizing, I needed to be able to get at the actual design surface itself, so I created an IDesigner interface which is implemented by DesignSurface.

    public interface IDesigner : IService

    {

  UserControl VisualRoot { get; }

  Canvas Surface { get; }

    }

I have a feeling I’ll be adding to that later.  Here is the mouse code in DesignSite:

        private void SiteBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {           

            _resizing = true;

            SiteBorder.CaptureMouse();

        }

 

        private void SiteBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

        {

            _resizing = false;

            SiteBorder.ReleaseMouseCapture();

        }

 

        private void SiteBorder_MouseMove(object sender, MouseEventArgs e)

        {

            if (_resizing)

            {

                Point resizePoint = e.GetPosition(_designer.VisualRoot);

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

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

                double newWidth = left + resizePoint.X;

                double newHeight = top + resizePoint.Y;

                Control c = (Control)SiteBorder.Child;

                if (newWidth > 5 && newHeight > 5)

                {

                    c.Width = newWidth;

                    c.Height = newHeight;

                }               

            }

        }

And now, when I run it, everything goes to hell.  Handling resizing seems to have become my first serious problem.  In order to study the issue, I created a DummyButton IDesignableControl, which exposes even more problems. 

A Silverlight control with size set to Auto (take up its whole container) returns NaN for Width and Height,  {0,0} for RenderSize, 0.0 for ActualWidth and ActualHeight, {0,0} for DesiredSize, 0.0 for MinWidth and MinHeight.  This makes it hard to measure or do any calculations on until it is actually placed in a container.  Calling Control.Measure does not seem to fix the issue.  I was unable to create my Drag representation of the DummyButton until I set its containing UserControl to have an actual size.  Attempting to select the button on the surface reveals another issue: the button is swallowing the click event before my code can react to it.  I’d like to handle this in a generic fashion.

I set about making a “glass” idea: placing an almost entirely transparent control in front of the real content in the Z order.  Getting this to cover the entirety of the real content led me back to the same lack of ActualWidth and friends that started this mess. 

Reality Blogging, cue RC0

OK, everything above this heading was written pre-RC0 and I was clearly floundering, I call time out.  I need to restate and reform my goals:

·         My DummyButton test shows that I need to refine IDesignableControl and how things ought to resize on the DesignSurface.  I am so far setting width/height of the IDesignableControl.Visual when dragging the Border to resize.  This is only appropriate in some cases. For a Button, sure, I’d want to set its bounds independent of its FontSize or other properties.  For the furniture and speakers, I’d actually want to apply a Transform since a huge red square with a chair in the corner or a small red square showing part of a chair is obviously not my intent.

·         Following from the above, IDesignableControl.Scalable and IDesignableControl.Transformable need to be re-thought.  I need a property stating if the IDC{IDesignableControl} is to be resized by changing Width & Height, if not, Transformable should be checked.  Both these properties being false means we should not resize the IDC at all.

·         There’s too much code in DesignSite.  Event wire up related to sizing is possibly fine, but we should keep things in whatever IDesignTypeCreator implementation wherever possible.

Now I feel like I’ve got a better plan, even if the Measuring issue is still waiting.  First I refactor IDC.Scalable and include an appropriate comment.

        /// <summary>

        /// Return true if this IDesignableControl should be resized by changing Width & Height

        /// </summary>

        bool IsBoundsResizable { get; }

Next, IDC.Transformable:

        /// <summary>

        /// Can a ScaleTransform etc. be used to resize this control?

        /// </summary>

        bool IsTransformable { get; }

Next I setup a test. The DummyButton will be IsBoundsResizable=true, and the Red Chair will be IsBoundsResizable=fasle/IsTransformable=true.

Debugging Events

The next step I take is to set up some code to debug the various mouse position, sizing, and layout events for the Border in DesignSite.  I tend to do this by instrumenting the code rather than using the debugger.  What good is a MouseLeftButtonDown event on a visual control when the debugger will receive the MouseLeftButtonUp event?  I have to use the ILogService and my message window to see when/how some things are happening.

Layout Cycle Detected

If you subscribe to a LayoutUpdated event, it’s best not to touch the GUI in any way from within this event handler.  During my testing, I would use the ILogService to send a message stating that my DesignSite had its layout updated.  This, in turn, fires the LayoutUpdated event which is counterintuitive.  One might think that LayoutUpdated means “Layout of the current control” but in fact means a layout or property change of any item in the visual tree of the Silverlight plug-in.  Obviously touching the visual tree from here will melt circuits.

Resizing

With a more clear idea of precisely what I was trying to do, I went back to working on my resizing code. When my Border has mouse capture, I can choose how to resize its content:

        private void SiteBorder_MouseMove(object sender, MouseEventArgs e)

        {

            if (_resizing)

            {

                if (HostedContent.IsBoundsResizable)

                {

                    ResizeContent(e);

                }

                else

                {

                    TransformContent(e);

                }               

            }

        }

I have to admit it took quite a while to get the ResizeContent method right.  At the end of the day, this was just facing reality and doing what worked rather than refusing to accept how”Auto”, “Stretch”, and “Fill” did not live up to expectations.  I have to set five different Controls’ sizes manually to make my bounds resizing work:

        private void ResizeContent(MouseEventArgs e)

        {

            Point resizePoint = e.GetPosition(DesignParent);

            //Determine where the DesignSite is on parent

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

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

            double newWidth = resizePoint.X - left;

            double newHeight = resizePoint.Y - top;

 

            //Take border into account

            double innerWidth = newWidth - (2 * UNIFORM_BORDER_THICKNESS);

            double innerHeight = newHeight - (2 * UNIFORM_BORDER_THICKNESS);

 

            if (newWidth > 5 && newHeight > 5)

            {

                _content.Visual.Width = innerWidth;

                _content.Visual.Height = innerHeight;

                Glass.Width = innerWidth;

                Glass.Height = innerHeight;

                LayoutRoot.Width = newWidth;

                LayoutRoot.Height = newHeight;

               

                ContentCanvas.Width = innerWidth;

                ContentCanvas.Height = innerHeight;

 

                SiteBorder.Width = newWidth;

                SiteBorder.Height = newHeight;

 

            }

        }

Having accepted this reality, my “transform” resizing worked the first time:

        private void TransformContent(MouseEventArgs e)

        {

            Point resizePoint = e.GetPosition(DesignParent);

            //Determine where the DesignSite is on parent

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

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

            double newWidth = resizePoint.X - left;

            double newHeight = resizePoint.Y - top;

            //We need to attain a render size equal to our new border inner bounds,

            //so we'll calculate what scale transform would get us there

            double innerWidth = newWidth - (2 * UNIFORM_BORDER_THICKNESS);

            double innerHeight = newHeight - (2 * UNIFORM_BORDER_THICKNESS);

 

            SiteBorder.Width = newWidth;

            SiteBorder.Height = newHeight;

            //

            Glass.Width = innerWidth;

            Glass.Height = innerHeight;

            //

            ContentCanvas.Width = innerWidth;

            ContentCanvas.Height = innerHeight;

            //

            ScaleTransform st = new ScaleTransform();

            st.ScaleX = innerWidth / HostedContent.Visual.ActualWidth;

            st.ScaleY = innerHeight / HostedContent.Visual.ActualHeight;

            HostedContent.Visual.RenderTransform = st;

        }

Now, I can finally have large buttons, small couches, and huge speakers on my design surface:

… All perfectly scaled according to their needs by naturally grabbing and dragging the border.  Hooray for vector graphics.  Note to self: every single flaw in artwork is readily apparent when scaled far larger than I drew it.

Conclusion

This article was a bit of a mess since Silverlight 2 RC0 happened right in the middle of it and my vision was not re-adjusted early enough when I started realizing there were issues.  There are also still cases where I am obviously thinking like a WinForms developer.  I kept thinking to myself how easy this would be if I could do work by overriding an OnPaint virtual method.  Still, I finally got where I wanted to be and learned a few more things about Silverlight in the process.  Since I’ve already given thought to one more bit of sauce before I write code to move things around on the surface I will visit the “rectangular lasso” next.

Source code: DamonPayne.AGT[12].zip (1.3 MB)



Monday, October 06, 2008 10:32:00 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 01, 2008

I don’t have any ideas yet, but Netflix is one of my favorite services.   This is completely awesome.  I hope they have the APIs enabled for cross-domain stuff so I could perhaps do some Silverlight mojo without having to Proxy the calls.

http://developer.netflix.com/page

Actually, in the process of writing this email, I did get some ideas for uses of the Netflix API.  I'm not sharing yet!



Wednesday, October 01, 2008 1:52:56 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, September 27, 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

RC0

As promised, my next task was to make any changes needed to prepare the app so far for working with the RC0 Silverlight release.

Object Tag

The first thing I noticed is that when I tried to run the application I was presented with “Get Silverlight”.  I figured the version # had changed, but it was actually the type attribute of the object tag:

    <object data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%">

          <param name="source" value="ClientBin/DamonPayne.HTLayout.xap"/>

          <param name="onerror" value="onSilverlightError" />

          <param name="background" value="white" />

          <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">

            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>

          </a>

      </object>   

Note the type is now “application/x-silverlight-2”.

XAML weirdness

The next error I noticed was a seemingly uncatchable error that percolated all the way up to the onSilverlightError JavaScript function.

“Can not set ItemTemplate and DisplayMemberPath simultaneously.”

Looking at the XAML, this makes sense.

        <ListBox x:Name="_itemLst" Grid.Row="1" DisplayMemberPath="Name"  ItemsSource="{Binding ToolboxItems}" SelectedItem="{Binding Path=SelectedToolboxItem, Mode=TwoWay}">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <StackPanel Orientation="Horizontal" >

                        <TextBlock Text="{Binding Name}"

This makes sense, it just didn’t cause an error before.  I’m setting DisplayMemberPath in XAML, which is then not used since I’ve provided the entire ItemTemplate.  It would be useful if the error told me which XAML file the error is in, but this is a small project so far and future Silverlight developers will be starting out with Silverlight 2 Release.

HitTest missed

At this point I thought I’d better do a Clean Solution just to be safe, and sure enough this turned up another issue.  At some point, my DesignSurface.HitTest method (required by IDropTarget) disappeared.  The Clean Solution found this out for me.  Looking back through my code snapshots taken at the end of each article, it’s been gone for quite some time.  This behavior from VS2008/Silverlight Tools is just not ok.  This was my last breaking change though.  UIElement.HitTest is gone, replaced with a method in VisualTreeHelper:

        public IEnumerable<UIElement> HitTest(Point p)

        {

            return VisualTreeHelper.FindElementsInHostCoordinates(p, LayoutRoot);

   }

Moving On

I find now that my Selection code is absolutely broken, but that was under repair anyway.

Source code: Because the code was in a state of flux pending the selection/sizing updates, the next source code will be at the end of AGT[12].



Saturday, September 27, 2008 12:31:33 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, September 26, 2008

You have probably seen that Silverlight RC 0 is now available: http://weblogs.asp.net/scottgu/archive/2008/09/25/silverlight-2-release-candidate-now-available.aspx

I was in the middle of writing AGT[11] and got sick, requiring a 16 hour sleeping spree, and I'm still not over it. AGT[11] also represented some frustrating material, so it was going slow.  Figuring out the Silverlight Layout-->Measure-->Clip-->Render process well enough to acheive my next goal is turning out to be challenging.  I'll preface it with some recommended reading.  At any rate, the material for AGT[11] will now become AGT[12].  The articles and the live demo are immediately switching over to use Silverlight RC0 before I accumulate any more breaking changes or write code to Measure behavior that changes.  Some of the solutions I was pursuing just became private/internal/sealed as of yesterday.

Recommended reading:

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

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

http://blogs.msdn.com/silverlight_sdk/archive/2008/05/06/more-on-layout-and-the-layoutinformation-apis.aspx

http://silverlight.net/forums/t/20520.aspx

http://nerddawg.blogspot.com/2008/04/silverlight-2-layout-and-rendering.html

http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.layoutinformation_members(VS.95).aspx

 



Friday, September 26, 2008 3:56:32 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, September 22, 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

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)



Monday, September 22, 2008 9:28:30 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, September 21, 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

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)



Sunday, September 21, 2008 7:25:35 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

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)



Sunday, September 21, 2008 6:32:39 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, September 20, 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-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)



Saturday, September 20, 2008 1:06:22 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 18, 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-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)



Thursday, September 18, 2008 7:43:00 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

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



Thursday, September 18, 2008 5:42:43 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

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].



Thursday, September 18, 2008 3:28:26 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

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)



Thursday, September 18, 2008 3:26:29 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, September 16, 2008

You simply know you're doing good, important, awesome work when you are able to get this error at runtime:

An exception of type 'System.Security.VerificationException' occurred in DamonPayne.IoC.TempProxy but was not handled in user code

Additional information: Operation could destabilize the runtime.

The Type name "DamonPayne.IoC.TempProxy" is wrong, and doesn't exist, which just adds to the fun. This will factor into the AGT series of articles, but I took a brief break to share this badge of honor.  In the age of awesome search engines, a developer can often learn how to quickly do a search for their particular error type or error message or get lucky based on some aspect of their exception stack trace.  Sometimes, very rarely (rarely unless you are Dan) you will have an error so specific that no one has blogged about it, or documented it, or asked about it in newsgroups.  The first reaction to this situation is typically panic or disgust; "crap I'm in uncharted territory" or "I can't believe no one else has tried this".  Sometimes these feelings are followed by Pride.  In a time of rising water level, more and more abstractions between the programmer and the metal, there is a certain appeal to doing something that was clearly not intended by the developers of the frameworks you are consuming.  At the very least, you now know that you are down in the bowels of The Machine, far below the safety nets installed to save Mort from himself.



Monday, September 15, 2008 11:06:11 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, September 15, 2008

If you’re just jumping in, you may want to start from the beginning: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Beginning the Designer

In case you’re not pouring over the source code with interest and reverence, let’s take a quick snapshot of the structure that is emerging:

There’s very little in the IoC or HTLayout projects so far, but it seems very clean up to this point.  I have also done nearly nothing related to my core problem of a Design Time environment for Silverlight: the Design project stands empty.  It seems sensible enough to work from left to right for the time being, so let’s begin with the Toolbox. 

Designable

The next step is to create an abstraction for what we are going to be working with.  In the VS2008 design time environment the coin of the realm is the System.ComponentModel.Component.  In many ways I didn’t like this since one is nearly always casting Component to Control.  Creating an interface that gives us a grammar for something which is “designable” and also has a visual representation will be a better place to start.

namespace DamonPayne.AGT.Design

{

    public interface IDesignableControl

    {

        /// <summary>

        /// Visual representation of this IDesignableControl

        /// </summary>

        Type VisualType { get; }

 

        /// <summary>

        ///

        /// </summary>

        bool Scalable { get; }

 

        /// <summary>

        /// Should the designer allow transformations such as rotation to be applied?

        /// </summary>

        bool Transformable { get; }

 

        string Name { get; set; }

    }

}

That’s all I’ve got for now.  Clearly I’ve thought this through…

Toolbox Service

A toolbox is a thing that displays a list of things you can drag out into another thing.  It is a thing as well as a meta-thing.  Beginning in Visual Studio 2005 we had the notion of a categorized toolbox.  I like that, so the next step is to decide on the starting point for a Toolbox service.  Keeping with the Microsoft design for now, I create a ToolboxItem.

namespace DamonPayne.AGT.Design.Types

{

    public class ToolboxItem

    {

        public ToolboxItem():this("Unknown", "No description",null)

        {

        }

 

        public ToolboxItem(string name, string desc, Type type)

        {

            Name = name;

            Description = desc;

            Type = type;

        }

 

 

        public string Name { get; set; }

        public string Description { get; set; }

        public Type Type { get; set; }

    }

}

The IToolboxService is straightforward.

namespace DamonPayne.AGT.Design.Contracts

{

    public interface IToolboxService : IService

    {

        void AddItem(ToolboxItem item);

        void AddItem(ToolboxItem item, string category);

        ToolboxItem SelectedItem { get; }

        void RemoveItem(ToolboxItem item);

    }

}

That will serve for now.  Before I get into creating some funky purple couches and speakers in Expression Blend, I need to make sure my framework supports my next intended action.

    public partial class ToolboxView : UserControl, IView, IToolboxService

It makes a lot of sense for the visual toolbox UserControl to implement the IToolboxService interface.  Who better?  Now I need to take a look at my framework already.  Things that I need to create as top level objects go through the ControlBuilder.  Things that need to provide service implementations to other components are created when their Type is registered with the ServiceManager.  I’m trying to have it both ways and I can’t without doing more work.  Since I envision the same situation for the designer surface and property grid later on this deserves some thought.

1.       I could create a non-visual toolbox class that knows about the ToolboxView and have this class implement the IToolboxService instead.  The ToolboxView would have to provide public methods for all the appropriate operations.

2.       In addition to #1, I could change ToolboxView to have a “Visual” property that could be set at a later time.

3.       I could change ServiceManager such that the ToolboxView (while implementing IToolboxService) is actually instantiated up front just like other service providers and somehow requested.

4.       I could strengthen the loving relationship between ComponentBuilder and ServiceManager.

Recall the public interface to ComponentBuilder:

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

I could fairly easily detect here that I’ve been asked to create something which is potentially both a service provider and service consumer and pass it off to ServiceManager after fully building it.  Unless I receive one metric ton of hatemail, I will take this approach for now.  ComponentBuilder can call this entirely new ServiceManager.ManageInstance method:

        public static void ManageInstance(object provider)

        {

            Type baseServiceType = typeof(IService);

            Type t = provider.GetType();

            Type[] interfaces = t.GetInterfaces();

            foreach (Type iface in interfaces)

            {

                if (iface.InterfaceExtends(baseServiceType))

                {

                    _services.Add(iface, provider);

                }

            }

        }

The main issue that I can foresee is getting into trouble with multiple instances of an IService being provided by different Types.  If this situation arises, some form of discriminator will need to be introduced.  For now, though, this is a good stopping point.  In the next article we can begin the visual implementation of the ToolboxView.

Source code: DamonPayne.AGT[3].zip (672.51 KB)



Monday, September 15, 2008 7:30:55 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Some news aggregators (Google reader) like to just ignore articles from DasBlog if they get too long, or perhaps it doens't like the special formatting HTML I get when copy/pasting out of Word.  At any rate, if you are following the AGT series, please check out

AGT[1] http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT1.aspx (Composite UI and modules)

and

AGT[2] http://www.damonpayne.com/2008/09/15/RunTimeIsDesignTimeForAGT2.aspx (Service wire-up, presenters, and Events)

I will keep my eye on Google Reader and be sure to double post.  I think I've been singled out for this behavior for my Break Up Google article.



Monday, September 15, 2008 8:42:22 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, September 14, 2008

If you are just joining, you might want to start from the beginning: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Service Wire-up, Presenters, and Events

As I wrapped up the last article I found that I needed to determine a mechanism for providing IService implementations to Components that need them, and that it would be nice to have this happen in some automatic repeatable fashion.  As for providing IService implementations, there are four ways (four that come to mind right now anyway) to do this, one of which I have already shown:

1.       Explicitly: ISuck is = ServiceManager.Resolve<ISuck>();

2.       Implicit magic: public ISuck MySuck{get;set;} //set by some magic

3.       Via constructor: public MyServicedComponent(ISuck defaultSuck){…} //possibly also set by magic

4.       Via a Serviced Component interface: perhaps something like this, coupled with a registration mechanism:

    public interface IServicedComponent

    {

        IService[] GetDependencies();

        void SetDependency(Type serviceType, IService impl);

    }

In terms of discoverability of what is going on, #4 wins.  Back in my original design goals I stated that I wanted things to feel like Serviced Components.  An interface like this, which we could build on, would establish an unambiguous set of expectations and behaviors.  When it gets down to the other design consideration of “How do I want to code?”, though, I’m not very in love with #4.  #1 is safe but puts any future lifecycle management we may want to do out of reach most likely.  To support #2 or #3, I am going to need control over when most important objects are created.  It seems that going with #3 would make the various Reflection code that I have and expect I’ll have later a bit more messy.  As much as it pains me to do so, I’m going to go with #2 and try to make sure I take care of some design goals:

·         Make the source of injected objects as discoverable as seems reasonable, so that there is as little distrustful magic as possible.

·         In terms of the cleanest OO practices one doesn’t want to allow objects to exist in a state where they are invalid.  Providing a default constructor when this would render an object unusable is not desirable. 

Since I’ve been rereading some Fundamental Material lately, the answer has already jumped out at me.

Builder Pattern

The Builder Pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.

Using a builder pattern we can separate the construction of a serviced component from the creation of its dependent parts.  This also provides a clean “entry point” into the creation of top level objects and gives client code a window into when and how dependency injection is happening without exposing any unnecessary plumbing.  For truly old-school OO folks: I realize I am reaching a bit here as what I’m about to show differs in structure but not intent from the canonical Builder examples.

Because generics are the answer to nearly everything, and because part of the point of this series of articles is to show code evolving, I type the following code into VS2008 and stare at it so that thoughts can congeal.

namespace DamonPayne.AGT.IoC

{

    public class ComponentBuilder

    {

 

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

        {

            T instance = new T();

 

            return instance;

        }

I thought about this for a while.  Suppose my first use of this code was as follows:

MessageConsoleView view = ComponentBuilder.Create<MessageConsoleView>();

This will accomplish several goals for me.   If MessageConsoleView needs an IService implementation I can detect that need and provide it here.  If MessageConsoleView needs a “Presenter” of some kind I could detect and provide that too.  In the first article I stated that I wanted as little code behind/presenter code as possible.  As it turns out, I had been corrupted by WPF.  WPF contains a nice notion of a Command infrastructure whereby visual components can be bound to fire commands such as from a button’s click event.  This often results in hardly any code behind or presenter needed.  Silverlight doesn’t have this, nor a Navigation service, nor control Validation plumbing, so there’s probably some more work to do later.

I could take this notion one layer “forward”, and come up with a way to construct the IView implementations too, such that the entire composite UI was constructed in magical fashion. For now I’ll stick with imperative creation of the IViews.  The following code now works:

        private void UserControl_Loaded(object sender, RoutedEventArgs e)

        {

            IRegionManager mgr = ServiceManager.Resolve<IRegionManager>();

            MessageConsoleView view = ComponentBuilder.Create<MessageConsoleView>();

            mgr.AddView(view, "MessageConsole");

        }

The next step is to determine what the contract looks is for a serviced component to say “I need an IService implementation”.  Since we have ruled out the constructor method, we’re left with properties or fields.  Now to rely solely on reflection or include attributes too?  For now, we’ll just let the magic roll and state that: Any Public properties of type IService or some Presenter type will just be set for you within ControlBuilder.  MessageConsoleView needs an ILogService.

public ILogService LogService { get; set; }

We’ll assume we can use the LogService property as soon as the Constructor returns, like so:

        private void UserControl_Loaded(object sender, RoutedEventArgs e)

        {

            LogService.Debug("Message console started");

        }

Next it’s up to the ComponentBuilder to make the magic happen.  As soon as we generically create an instance of whatever type was given to ComponentBuilder we call an method to find out if it needs any IService types:

        internal static void ResolveServiceDependencies(object component)

        {

            PropertyInfo[] publicProps = component.GetType().GetProperties();//TODO: cache per Type to make this faster

            foreach (PropertyInfo pi in publicProps)

            {

                Type propType = pi.PropertyType;

                if (propType.IsInterface && propType.InterfaceExtends(typeof(IService)))

                {

                    object svc = ServiceManager.Resolve(propType);

                    pi.SetValue(component, svc, null);

                    ResolveServiceDependencies(svc);//TODO: Smartest way to do this?

                }

            }

        }

“InterfaceExtends” is a new extension method for Type “Type” from the new TypeExtensions static class in the IoC project.  Determining if one Interface extends another interface is rather annoying so it’s encapsulated there.  Other than that this method is straightforward: look for any public properties on component that are of a Type that extends IService.  Resolve that type using the ServiceManager and recursively check to see if that service depends on any other services.  I also had to refactor ServiceManager to have a Resolve(Type) method alongside the generic Resolve<Type>() method.  This takes care of IService injection for now, on to Presenters.

I could call Presenters a special kind of Service and be done now.  I won’t do this for several reasons.  First, I’ve never looked back on code and wished I could swap out a different Presenter implementation.  While running automated testing, why would I want to swap out a Presenter?  If I’m not testing the logic/process, and I’m mocking out any heavy components like data access, what am I really testing then?  Second, I tend to like doing Model-View-Presenter a certain way which doesn’t fit into some of the design choices I made for the IService world and I don’t want to redo my IService world for this reason at least.  I like to have a Presenter talk to a certain specific View interface, while the View knows the concrete type of the Presenter.  View ßà Presenter communication is then a combination of method calls and data binding. 

I could have an IPresenter interface and use that to type public properties and have the same logic as I have above.  Since every Presenter will be highly specialized I would end up just having IPresenter as a “marker” interface and since this is .NET there are better ways to do this.  I could force a Presenter to extend a base class, but in this case I don’t like that either.  It’s a common practice to have an application specific Presenter base class to use for code sharing.  I’ll mark a Presenter with a public Property with a certain attribute type on it.  I can always change my mind later.  After creating an empty IMessageView and MessagePresenter, MessageConsoleView now has another Property:

        [Presenter()]

        public MessagePresenter Presenter { get; set; }

And my presenter constructor looks like:

    public class MessagePresenter

    {

        public MessagePresenter(IMessageView view)

        {

            View = view;

        }

Now I can go back to ComponentBuilder and add another method:

        internal static void BuildPresenters(object component)

        {

            PropertyInfo[] publicProps = component.GetType().GetProperties();//TODO: cache per Type to make this faster

            foreach (PropertyInfo pi in publicProps)

            {

                object[] attrs = pi.GetCustomAttributes(typeof(PresenterAttribute), true);

                if (null != attrs && attrs.Length > 0)

                {

                    //We rely on the convention that the Presenter has a public constructor with a view as argument,

                    //where said view is implemented by "component"

                    ConstructorInfo ci = pi.PropertyType.GetConstructor( new Type[] { component.GetType() } );

                    object presenter = ci.Invoke(new object[] { component });

                    pi.SetValue(component, presenter, null);

                    ResolveServiceDependencies(presenter);

                }

            }

        }

At this point, by the time my IView loaded event fires, I’ve already got valid state through the friendly ComponentBuilder.  The “parts” of my Builder pattern are the IServices and Presenters.  If I were a tiny bit less lazy I could hide the algorithms for IService and Presenter resolution behind an interface or Strategy and allow different algorithms to be plugged in for resolution. 

 

Component Communication using Events

“Every problem in computer science can be solved by one more level of indirection.”

In Composite Application Guidance for WPF (http://msdn.microsoft.com/en-us/library/cc707857.aspx) they introduce the idea of an Event Aggregator.  An Event Aggregator is sort of a message hub.  In normal .Net event driven code, you have a component that has a hard reference to another component that can raise an event.  The event is signified as a special delegate type and VS2008 is nice about generating code stubs for you.  The problem here is that in complex systems you might be interested in certain occurrences where it is impossible or undesirable to obtain references to all of the components that could affect the kind of occurrence you are interested in.  An Event Aggregator is a huge help in meeting the design goal of having components communicate with each other.

The PRISM way of doing this is to define a special event for absolutely every action that could occur and hooking these into an event aggregator.  This might mean (going back to the purple couch example from the previous article) a CouchAddedEvent, a CouchChangedEvent, and a CouchDeletedEvent all with a Couch payload.  Following this will surely be a ChairAddedEvent, ChairChangedEvent, ChairDeletedEvent, and so forth.  In order to avoid this event explosion, we can define more generic events with template data payloads.  The first place I’ll use this is for the ILogService.  I’m going to refactor the ILogService slightly to show two points:

    public interface ILogService: IService

    {

        void Log(DamonPayne.AGT.IoC.Types.LogMessage m);

    }

The new Types namespace will contain things like this.  Using the EventAggregator I want to be able to get an event of a certain type, with a certain kind of data payload and either subscribe to it or raise the event with a valid payload.  I don’t see any way to make these work exactly the same as the .NET events you might be used to, so let’s take a look.  First we create an AggregateEvent class to help with the generics programming. 

namespace DamonPayne.AGT.IoC.Events

{

    public class AggregateEvent<T>

    {

        public AggregateEvent()

        {

            _subscribers = new Dictionary<Action<T>, Func<T, bool>>();

        }

 

        internal void InvokeAll(T arg)

        {

            foreach (Action<T> callback in _subscribers.Keys)

            {

                if (null == _subscribers[callback] || _subscribers[callback](arg))

                {

                    callback(arg);

                }

            }

        }

 

        private Dictionary<Action<T>, Func<T, bool>> _subscribers;

 

 

        public void Subscribe(Action<T> callback)

        {

            Subscribe(callback, null);

        }

 

        public void Subscribe(Action<T> callback, Func<T, bool> filter)

        {

            _subscribers.Add(callback, filter);

        }

 

        public void Raise(T payload)

        {

            InvokeAll(payload);

        }

 

    }

}

Generics are still the answer to everything as far as I can tell.  It took some time to get the template parameters correct, but now the EventAggregator thus far is fairly simple.

namespace DamonPayne.AGT.IoC.Events

{

    /// <summary>

    /// Allow creation and subscription to decoupled events

    /// </summary>

    public class EventAggregator

    {

        static EventAggregator()

        {

            _events = new Dictionary<Type, object>();

        }

        private static Dictionary<Type, object> _events;

 

        /// <summary>

        /// Get an event representation. The event Type signifies the sort of situation that we are interested in raising or being notified of,

        /// and the payload type represents the precise type of data payload we are interested in.

        /// </summary>

        /// <typeparam name="TEventType"></typeparam>

        /// <typeparam name="TPayload"></typeparam>

        /// <returns></returns>

        public static TEventType Get<TEventType, TPayload>() where TEventType: AggregateEvent<TPayload>

        {           

            Type t = typeof(TEventType);

            if (!_events.ContainsKey(t))

            {

                ConstructorInfo noArgs = t.GetConstructor(new Type[] { });

                TEventType evnt = (TEventType)noArgs.Invoke(null);

                _events.Add(t, evnt);

            }

 

            return (TEventType)_events[t];

        }

The first example of using the event aggregator is in the ILogService implementation, MemoryLogger.  I realize I had Startup and Shutdown methods on IService but wasn’t using them, so a call to Startup was added to ServiceManager.ObtainDefaultInstance.  The MemoryLogger wants to be able to let the universe know when log messages arrive, in a decoupled fashion.  The event “Type” is MessageArrivedEvent.

    public class MessageArrivedEvent<T> : AggregateEvent<T>

    {

        public T Message { get; set; }

    }

Next we get an event that has the type of payload we are interested in.

        private MessageArrivedEvent<LogMessage> _logEvent;

 

        public void Startup()

        {

            _logEvent = EventAggregator.Get<MessageArrivedEvent<LogMessage>,LogMessage>();           

        }

 

        public void Log(DamonPayne.AGT.IoC.Types.LogMessage m)

        {

            _messages.Add(m);

            _logEvent.Raise(m);

        }

This is the code necessary to consume this event in MessageConsoleView.

        public MessageConsoleView()

        {

            InitializeComponent();

            _currentMessages = new List<LogMessage>();

            DataContext = _currentMessages;

            EventAggregator.Get<MessageArrivedEvent<LogMessage>, LogMessage>()

                .Subscribe(MessagePosted);

        }

 

        private void MessagePosted(LogMessage m)

        {

            _currentMessages.Add(m);

        }

We would need to keep the event around in order to unsubscribe later, but we haven’t gotten that far yet.  There is one other bit of functionality that I wanted to show here that isn’t used yet but is best shown along with the rest of the EventAggregator code.  We also have the notion of event filters, implemented using … wait for it… Generics.  We have the ability to subscribe to an event conditionally.  This is another slick idea stolen from PRISM.  Take a look at this temporary test method:

        public static void Test()

        {

            MessageArrivedEvent<LogMessage> myEvent =

                EventAggregator.Get<MessageArrivedEvent<LogMessage>,LogMessage>();

 

            Action<LogMessage> logCallback = msg =>

            {

                Console.WriteLine("I am the first subscriber");

            };

 

            Action<LogMessage> lc2 = msg =>

            {

                Console.WriteLine("I am the second subscriber!");

            };

 

            Action<LogMessage> filterCB = msg =>

            {

                Console.WriteLine("I only get called if the message sucks");

            };

 

            myEvent.Subscribe(logCallback);

            myEvent.Subscribe(lc2);

            myEvent.Subscribe(filterCB, lm =>  lm.Message.IndexOf("suck") != -1);

 

            LogMessage goodMsg = new LogMessage

            {

                Level = LogLevels.Debug,

                Message = "Yee haw!"

            };

 

            LogMessage suckMsg = new LogMessage

            {

                Level = LogLevels.Debug,

                Message = "this sucks!"

            };

 

            myEvent.Raise(goodMsg);

            myEvent.Raise(suckMsg);                       

        }

We subscribe to the MessageArrivedEvent<LogMessage> with three different Closures.  For the third closure, we use the overload that accepts a Func<T,bool>.  The lambda expression only returns true if the message contains “suck” in it somewhere.  Running this code shows that when we raise the event with goodMsg only logCallback and lc2 fire.  Raising the event with suckMsg passes the filter and all three callbacks fire.  This is incredibly useful to further fine tune which events a component is really interested in.

Summary and Next Steps

Service containers, service locators, model-view-presenter, and event aggregation actually have very little to do with the overall problem I set out to solve.  I could have made a service container specific to the design-time environment similar to what Winforms 2.0 uses and gotten started on the core problem sooner.  Still, this stuff may come in handy.  I have now the beginnings of a framework that I can use and refine as I discover what my needs truly are.

Source code: DamonPayne.AGT[2].zip (550.62 KB)



Sunday, September 14, 2008 8:55:10 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback

If you are just joining, you might want to start from the beginning: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx

Layouts and Modules

I’m about to steal some ideas that are in common use, ideas from PRISM, Castle Windsor, the Visual Studio design-time environment, and a thousand other things.  I’ll briefly explain some of the core concepts here.

Modules or Services

In a well designed Windows Forms application, one would have a proper separation of concerns.  Suppose my form was for place orders for big purple couches.  I would have a CouchOrderForm that implements ICouchOrderView with very little logic: populating values or potential values only, perhaps with Data Binding, and event handlers.  These event handlers would delegate the actual work to a CouchOrderPresenter of some kind.  CouchOrderPresenter would probably use to a CouchOrderDataAccess class or perhaps a CouchBussinessProcess class to accomplish the necessary tasks, and update the UI using a combination of data binding and events.  I know people who would frown upon this as “primitive”, but even getting this far would be miles ahead of the average .NET project I saw as a consultant.

For testability, this design accomplishes a fair bit of concern-separating.  I can test almost my entire program without messing around with “how do I test the logic that’s in the Form?”  For example, I have often created a unit test class that implements the ICouchOrderView interface and does asserts in the various callback.  This means that a database and whatever other “real” components are required must be present and that might be very Heavy for unit test time.  If my code was written to use ICouchDataAccess instead of CouchOrderDataAccess, it’s suddenly much easier to mock out parts of my framework.  I might also be able to supply different ICouchDataAccess implementations at run time if that makes sense for my program – though I think this need is often horribly misunderstood and over used by many advocates. 

When I originally encountered this idea we called it the Service Locator Pattern in Java.  The term “service” is a loaded word.  I used to ask interview questions along the lines of “What is a Class? Now what is an object?  Now what is a Component?  Now what is a Service?”  I would change terminology here to say “module” or “strategy”, but Service is the commonly known semantic of our times, so I’ll use service to talk about program chunks that are essentially managed components.  My Component Container/Service Locator will be a ServiceManager.  It might be useful to have some common behaviors for these components, so IService will be the base interface for components.

When I add these things to the IoC project, I notice that when I choose to Add à new item, “Interface” is not an option for Silverlight projects.  Doesn’t MSFT want us to take Silverlight seriously?

The idea is that the ServiceManager will figure out what classes are available when a component asks for an IFoo implementation.  I’ve seen lots of fascinating ways of doing this in Full Framework projects.  Config files, loading every assembly in a certain directory, manually resolving at program startup, and others.  In Silverlight, the AppManifest.xml file contains a “Deployment.Parts” tag, which contains all the assemblies that get deployed with your app.  Still, I don’t seem to be able to call Assembly.Load() on these, so for now I’m going to go with manual resolution at program startup.

Generics make getting started on a service locator a breeze:

    public class ServiceManager

    {

        static ServiceManager()

        {

            _services = new Dictionary<Type, object>();

        }

 

        private static Dictionary<Type, object> _services;

 

        /// <summary>

        /// Pass in a list of strong types and determine what service interfaces they provide to us:

        /// </summary>

        /// <param name="types"></param>

        public static void Manage(List<Type> types)

        {

            Type baseServiceType = typeof(IService);

 

            foreach (Type t in types)

            {

                Type[] interfaces = t.GetInterfaces();

                foreach (Type iface in interfaces)

                {

                    if(iface != baseServiceType)

                    {

                        Type svcInterface = iface.GetInterface("DamonPayne.AGT.IoC.IService", false);

                        if (null != svcInterface)//We know this interface extends IService

                        {

                            _services.Add(iface, ObtainDefaultInstance(t));

                        }

                    }

                }

            }

        }

 

        /// <summary>

        /// Since an object could implement more than one interface, we may have in in here under multiple keys

        /// </summary>

        /// <param name="t"></param>

        /// <returns></returns>

        protected static object ObtainDefaultInstance(Type t)

        {

            object o = null;

            var q = from val in _services.Values.AsQueryable<object>()

                    where val.GetType().IsAssignableFrom(t)

                    select val;

 

            o = q.FirstOrDefault<object>();

            if (null == o)

            {

                Type[] noArg = new Type[0];

                o = t.GetConstructor(noArg).Invoke(null);

            }

 

            return o;

        }

 

        public static TServiceType Resolve<TServiceType>()

        {

            TServiceType svc = default(TServiceType);

            Type t = typeof(TServiceType);

            if (_services.ContainsKey(t))

            {

                svc = (TServiceType)_services[t];

            }

            else

            {

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

            }

            return svc;

        }

 

 

    }

Now I need something to use to test this out.  I think I might want a Logging service of some kind to display messages to me, so I’ll create an ILogService interface in the IoC project:

    public interface ILogService: IService

    {

        void Debug(string message);

    }

 

And I’ll need to provide an implementation somewhere.  For now I’ll add a folder/namespace to the Layout project and create a simple do-nothing implementation.  In the Application_Startup event handler I can now start setting things up.  I create a MemoryLogger and:

        private void Application_Startup(object sender, StartupEventArgs e)

        {

            this.RootVisual = new Page();

            List<Type> s = new List<Type>

            {

                typeof(MemoryLogger)

            };

            ServiceManager.Manage(s);

 

            ILogService logger = ServiceManager.Resolve<ILogService>();

            //success!

        }

Now I’m starting to get somewhere.  I’m planning on creating an implicit way to get these services later.  There seems to be two schools of thought here: setting services on serviced components using a property or field, and supplying all the necessary services in the constructor.  For my next step, though, I want to get some things on the screen to help visualize how this is going to fit together.

Composite UI

My notion of a Region Manager is stolen directly from “Composite Application Guidance for WPF”.  We can abstract out the idea of a Window and various views and evolve the idea of how the UI should function over time.  I know that I will have the notion of a View, which is docked into a Region, which is managed by a RegionManager of some kind.  First, we think about these interactions and come up with a first try at interfaces.  Just to start getting some things on the screen, a View will be just this:

    public interface IView

    {

        /// <summary>

        /// Grid, etc. extends canvas

        /// </summary>

        Canvas LayoutRoot { get; }

    }

I’m not sure that I need an explicit implementation of a Region, so my IRegionManager consists of the following:

    public interface IRegionManager : IService

    {

  UIElement RootVisual { get; }

        List<string> RegionNames { get; }

        void AddView(IView v);

        void AddView(IView v, string regionName);

        IView GetView(string regionName);

        List<IView> Views { get; }

    }

The IRegionManager implementation does what I need it to do for now.  Clearly, our Page is a suitable class for serving as the Region Manager.  In the last article I envisioned three regions, but now that I’ve got my logging idea I’ll extend it to four.  I need a region for a Toolbox, Designer Surface, Property Grid, and Message Console.  Implementing the IRegionManager interface and supplying some default content to be sure I’m laying things out the way I want, the XAML and runtime application looks like so:

<UserControl x:Class="DamonPayne.HTLayout.Page"

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

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

    Width="800" Height="600">

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

        <Grid x:Name="_regionArea" Background="Red" Width="800" Height="600">

            <Grid.RowDefinitions>

                <RowDefinition></RowDefinition>

                <RowDefinition></RowDefinition>

            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>

                <ColumnDefinition></ColumnDefinition>

                <ColumnDefinition></ColumnDefinition>

                <ColumnDefinition></ColumnDefinition>

            </Grid.ColumnDefinitions>

            <Canvas x:Name="Toolbox" Grid.Row="0" Grid.Column="0" Width="Auto">               

                <TextBlock>I am toolbox</TextBlock>

            </Canvas>

            <Canvas x:Name="DesignSurface" Grid.Row="0" Grid.Column="1" Width="Auto">

                <TextBlock>I am design surface</TextBlock>

            </Canvas>

            <Canvas x:Name="PropertyGrid" Grid.Row="0" Grid.Column="2" Width="Auto">

                <TextBlock>I am property grid</TextBlock>

            </Canvas>

            <Canvas x:Name="MessageConsole" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3">

                <TextBlock>I am the message console</TextBlock>

            </Canvas>

        </Grid>

    </Canvas>

The IRegionManager implementation is incredibly straightforward and in the source code at the end of this article.  Applying some refactoring we get the ServiceManager to create the Page for us and the application startup now looks like this:

        private void Application_Startup(object sender, StartupEventArgs e)

        {           

            List<Type> s = new List<Type>

            {

                typeof(MemoryLogger),

                typeof(Page)

            };

            ServiceManager.Manage(s);

 

            IRegionManager mgr = ServiceManager.Resolve<IRegionManager>();

            RootVisual = mgr.RootVisual;

            //success!

        }

The next thing to decide is how IViews get loaded into our IRegionManager on startup.  It would be nice if I could use the same mechanism for IService wire-up, but this article is getting a bit long already.   For now, I’m going to create the visual design for the message console to set up the next article.  I create a Controls folder/namespace to hold my application specific UI components and put a MessageConsoleView in.  This component will be a UserControl that implements IView.  As soon as I implemented IView, however, I see that LayoutRoot was a poor choice of name since it forced an explicit interface implementation, so I refactor to IView.VisualRoot instead.  I also see that when I said “Canvas” was a good root because Canvas and Grid derived, I meant “UserControl”, so I refactor that too.

Now I have something to think about for the next article.  The MessageConsoleView will obviously need to get at my ILogService implementation…

Licensing

Now that I’ve got some code in here, I should decide on how I’m going to share that.  The Creative Commons license seems to fit my goals.  I’m not super up to date on open source license wording, but this one will do for now, in code-snippet form:

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

<CodeSnippets

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

  <CodeSnippet Format="1.0.0">

    <Header>

      <Title>

        Creative Commons License

      </Title>

    </Header>

    <Snippet>

      <Code Language="CSharp">

        <![CDATA[

/*

Creative Commons - attribution-noncommerical-share alike 3.0 Unported

          You are free to copy, distribute, and transmit this work.

          You are free to adapt the work.

        Under the following conditions:

          -You must attribute the author(Damon Payne, http://www.damonpayne.com) for this work but not in a way

          that suggests the author endorses you or your use of the work.

          -You may not use this work for commercial purposes

          -If you alter, transform, or build upon this work, you are free to distribute the work under the same or similar license to this one.

*/

        ]]>

      </Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>

If anyone out there wants to provide input on this license, please do.

Source code throug this article: DamonPayne.AGT[1].zip (538.15 KB)



Sunday, September 14, 2008 3:12:17 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Often times, the act of explaining a question out loud to another person involves a lot of complex and fleeting thought, we think at over 600 words per minute, and coalescing into sentences for the transmission from mental constructs to speech.  The thought process involved in this preparation for vocalization often restructures the problem in such a way that the question is answered in the asking.  An early mentor of mine called this effect “Talking to the bear”; he had a teddy bear on top of his monitor who served as a sounding board for his toughest issues, and later my issues as well. 

For a while, I’ve wanted to do some Reality Blogging.  Take a complex idea and a set of design goals, state them out loud, and slowly work towards a completed product or module.  Often times, I have presented an idea in, say, eight parts, because the idea was large enough to warrant breaking up for explanation.  The idea was fully baked before the first part was published.  The notion here, rather, is not to complete the idea but rather to work with both Visual Studio and Word open; to state design goals and see how they pan out later, to see how easy it was to refactor, to see a complex idea emerge out of nothing.

The Goal

The goal is to have an extensible, lightweight design surface and design-time environment for Silverlight.  As I have written about in the past (http://www.damonpayne.com/2007/04/24/Designer0.aspx) there are any number of neat reasons for which it would be useful to have  design time capabilities at runtime in applications.  My example application will be a Home Theater Layout application for Silverlight.  The vision is something like this:

So, I want this to be able to work in a manner we are familiar with: I drag things out of a toolbox onto a design surface, where I am allowed to drag and resize things.  When I select things I will be able to edit the selected item using a property grid.  I’ll want to support limited undo and be able to select multiple things to move around and so forth.

 In Windows forms, you can host the designer in your own application though it’s a lot of work. When the time came to make tradeoffs, the WPF designer “Cider” was not made hostable outside of Visual Studio.  Besides, there is no PropertyGrid for Silverlight, and no <<lots of other stuff>> for Silverlight, so this is really a big effort. 

Silver is Argentum in Latin, ergo its period table symbol is AG.  Ergo is also Latin, how fun.  Anyway, all the cool kids abbreviate Silverlight as “AG” and nothing sensible like “SL”.  The Latin word for “design” is more or less tela.  I dub thee, The ArGentum Tela project, or AGT for short. 

Design Goals

As Anders Heijlsberg said way back when he was famous for something called Delphi, it’s good to start out with some goals, it’s good to think about “how do I want people to use this?”  I have some design goals and functional requirements:

·         I want different aspects of the UI to function independent of each other

·         I want to be able to swap out different implementations for any aspects of the program that makes sense for

·         I want component to component communication to happen without components having references to each other

·         I want the building blocks of my program to feel like Serviced Components

·         I want to be able to serialize the results of my designer surface for later retrieval

·         I want a healthy # of common tasks accessible through the keyboard

·         I want this to work on the Mac.  I thought everyone was running Windows by now but apparently there are literally dozens of Mac users out there, so I should take their poor incomplete keyboards into account.

·         I want to package this as a reusable design-time library with a Reference Implementation – the home theater layout tool

·         I want to have as little code-behind/presenter as possible

I’ve been re-reading my GoF book, arguing with Dan almost every day, and reviewing the way the PRISM code looks.  I have some ideas about how I want to accomplish all of this.  As of now, my solution looks like this then:

·         DamonPayne.AGT

o   DamonPayne.HTLayout – the Silverlight sample project

o   HTLayoutWeb – a web test project for HTLayout

o   DamonPayne.AGT.IoC – how’s that for a name?  I’m going to pursue some inversion of control concepts for Silverlight, and that stuff will live in this assembly.

o   DamonPayne.AGT.Design – the code that makes the designer work will live here.

 

What’s next?

I’ve got Word open and Visual Studio open; it’s time to get started.  Where to begin?  In the next article we’ll start exploring some basic concepts for layouts and components.  Want to play along?  I’ll post the current source code at the end of each step.

 DamonPayne.AGT[0].zip (515.64 KB)



Sunday, September 14, 2008 10:24:11 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, July 28, 2008

As some people know, I'm very into 2channel audio listening and home theater.  I'm also a bit of a Klipsch fanboy.  I'm also intersted in acoustic science and this has all led to a listening room that is considered by some to be a bit over the top.  Recently, I've built four Silverlight applications related to my Audio/Home Theater hobbies.  I'm going to briefly introduce them here, and get into code in future articles.

Group Photo

Every year, Klipsch has an event that has come to be known simply as The Pilgrimage.  This boils down to a trip by hardcore Klipsch nuts to one of two sacred locations: the original home of Klipsch and present day manufacturing center in Hope, AR, or the engineering labs in Indianapolis, IN.  In the 1930's and 40's folks at RCA, Bell Labs, James B Lansing and Paul W. Klipsch were inventing what we call Hi-Fi.  There's a lot of fascinating history at these events, a chance to see new stuff under NDA, play with engineering equipment, and so forth.  Every year a group photo is taken.  This time, I thought creating a Silverlight application with "hotspots" would be the best way to annotate the photo: tie people's first names and forum names to a face. 

The picture scales to the size of your browser, so with a big monitor you can start to see some of the detail in the high-res original photo.  Mousing over people displays as much name as they wanted to give, forum name, and where they're from.  If you participate in any Internet community that has a real-world component, something like this to tie someone's internet handle to "Oh yeah I had some beers with him..." is pretty neat.

The application is live at http://www.klipschcorner.com/silverlight/2008/PilgrimageFaces/

Listening Room Carousel

My Klipsch site, KlipschCorner.com, has a feature where crazy people like myself can share photos of their listening rooms.  I have previously posted my conversion of the "carousel" example to a nicer Silverlight 2 appliction, and I just updated it for Beta 2, the code will be coming of course.

The application is live at http://www.klipschcorner.com/HTCarousel.aspx?Id=1&Title=Damon's Palladium Paradise

Room Mode Calculator

Many people don’t know that the shape and size of your room can have a much larger affect on sound than what equipment is in the listening room.  Everyone probably learned in high school science class that when waves collide, constructive or destructive interference can be created.  When those waves are sound waves bouncing around your walls what you end up with is not hearing what’s actually in the recording.  In order to model this behavior before building a room (or to help make educated guesses on an existing room), a room mode calculator is called for.  This tells us what frequencies stack up and what the distribution of modes is:

The application is live at

http://www.klipschcorner.com/Tools/ModeCalc.aspx

Palladium Deep Zoom

Let’s say you were really into Klipsch speakers.  Let’s further say that Klipsch came out with a product that was considered by early reviewers to be among the best speakers in the world.  Would you let the fact that they cost $20k stop you from purchasing them?  You probably would!; but then you would not be me.  You would take tons of photos to meticulously document the un-boxing and setup process so that audio fans everywhere could live vicariously through you, and so that is what I’ve done.

The application uses the standard mouse wheel/click zooming about as well has showing a blurb about each photo as you click on it ala the Hard Rock site.

The center channel zoom app is live at

http://www.klipschcorner.com/PalladiumZoom.aspx and the floorstanding speakers at http://www.klipschcorner.com/PalladiumZoom.aspx?model=p39f

As soon as I get caught up on some other things, I’ll dive into creating some articles that get into the useful parts of the XAML, C#, or Blend2.5 tricks it took to create these applications.  For now, I hope some people will appreciate these awesome speakers!



Monday, July 28, 2008 11:03:14 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, July 21, 2008

Adam Kinney has post some articles in the past showing a Silverlight application for displaying gamer cards.  When the Playstation 3 finally caught up and created a "Portable ID", I thought this would be a decent chance to demonstrate some Silverlight chops. Yes, I have a PS3 and not an XBox360.  I thought maybe I'd make an application with unnecessary animations and sounds for fun, perhaps allow you to sort friends and setup notifcations that are not available from the PSN.  Here's my Playstation portable ID:

The gist of the online status is simply a JPG, in my case http://pid.us.playstation.com/user/drpayne.jpg , that gets updated when your status changes via the console.  Excited to run off and write code I created a Silverlight 2 app and set about downloading this JPG.  Except that it doesn't work because of Silverlight's cross domain security policies.  I'm not a genious in TCP/IP or DNS/BIND, but the policies used by Silverlight (mimicing those used by Flash) seem overly restrictive and make some scenarios that should be common and easy difficult or un-doable.  There may be a reverse-tunnel situation or similar DNS trickery that is capable with this type of application, but denial of service?  Check out what I just did in this blog posting:

<img border=0 src="http://pid.us.playstation.com/user/drpayne.jpg"/

When the markup for this page is downloaded to your browser, the browser then issues seperate http requests for content that lives on other servers.  That content comes from us.playstation.com, and Google analytics, and Blogged, and others.  This is, seemingly, not a security or denial of service risk in this particular situation.  The PS3 network site shown above did not need to place a client policy XML file in the server root, and in fact they would need to do work to prevent cross domain access of this type.  Sure, Silverlight has more than just HTTP networking capabilities, in fact for a future article I have a full blown instant messanger application implemented in Silverlight using Sockets.  In terms of being a good Technology Citizen, I can see Microsoft wanting to be very careful concerning what it allows devlopers to do with more general socket programming.  But HTTP?  Isn't this part of what the web is "about" ?

What do you think?  Is the cross domain policy employed by Silverlight too restrictive?  Does it not go far enough?  Just right?



Monday, July 21, 2008 12:20:51 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, June 25, 2008

So, I am building a Silverlight 2 application related to my trip to Klipsch in Indianapolis this year, and found what seems to be to be a terrible bug in Silverlight 2 beta 2.  Multiple UIElements  cannot share event handlers.  What do I mean?  In my case, I have varous Paths inside a canvas and I'm creating hotspots.  When the mouse enters a given region, something slightly different will happen, however the exact same method is called for every single region when the mouse exits that Path.  So, suppose I have a Path called Damon, which happens to have some Damon-y things inside it and I'm going to display my name:

private void _damonPayne_MouseEnter(object sender, MouseEventArgs e)

{

AddCallout("D.R. Payne", "damonrpayne", "Hartford, WI", e);

}

When the mouse exists, I simply remove the UIElement representing the callout, which is the same for all regions on the page:

private void _damonPayne_MouseLeave(object sender, MouseEventArgs e)

{

RemoveOldCallout();

}

Now, it would seem that I could make the functionality in RemoveOldCallout a MouseEventHandler and share the same method among the various paths:

protected void RemoveOldCallout(object sender, MouseEventArgs e)

{

RemoveOldCallout();

}

Now, if I have another Path, I should be able to write code like _someOtherPath.MouseLeave += new MouseEventHandler(RemoveOldCallout);  This code, however, bombs with the following BadPropertyValue error:

 

Now, I have to write a different MouseLeave event for every single path on my page, and there's a lot of them.  I'll submit to Connect and hope this issue is fixed!



Wednesday, June 25, 2008 11:54:43 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, June 05, 2008

Ok, so, I found some great Silverlight tips on the afternoon of Day two courtesy of Jeff Prosise (http://www.wintellect.com/TechnicalBioDetail.aspx?Tech=5) .   I am definitely going to be taking his advice on some points and blogging about it as I convert my Beta 1 examples to Beta 2 examples.  Jeff had an implementation of The Game of Life in Silverlight that got me thinking about getting some Concurrency peanut butter into the Silverlight chocolate.  Would the LINQ based ray tracer work without too much modification in Silverlight?  Surely the Task Parallel Library will not work in Silverlight without serious modification and many things will likely not be available at all.  Still, I smell a side project: to what degree could we at least get Parallel.ForEach<> , Parallel.For, and perhaps some of the basic System.Threading.Tasks.Task things working in Silverlight 2.  Possible uses for this include a Silverlight based Folding @home style app and I’m sure there are others.

2008 TechEd MVP Party

I’m writing this on the morning after the 2008 MVP party, hosted in the Voodoo Room at the House of Blues in Orlando.  What a blast!  I showed up and realized that while I knew no one most other folks seemed to already know other MVPs.  Shortly thereafter Carl Franklin walked in the door.  Since I don’t get to meet Internet Famous people often, I said hi and thanked him for introducing me to Steely Dan with his rendition of Home at Last .  He seemed amused and gave me the big High Five when I said I actually bought Aja because of him. 

There were two guys there doing the whole Blues Brother s schtick.  It would seem not as many people came as RSVP’d, so I was able to call The Vanderboom and get him in to the party.  The lower attendance also meant that the free drink tickets flowed in a steady stream for all of us.  There are some pictures circulating of me swing-dancing with a woman who runs a user group in Seattle.  Hopefully no other incriminating evidence exists, everyone remaining when they kicked us out was getting wacky by that time. 

Thanks to Microsoft for putting this event on, and all the MVPs who came for a great time.



Thursday, June 05, 2008 10:01:14 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, June 03, 2008

One thing I forgot to mention from the keynote this morning, and msftguy beat me to it.  Silverlight 2 Beta 2 will ship by the end of this week.  One thing Brian does not mention is that Somasegar claimed this version would have a go-live license.

Hopefully this means SL2 is nearly ready to ship.



Tuesday, June 03, 2008 3:09:33 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, April 12, 2008

http://www.klipschcorner.com/silverlight/DeepZoomTestPage.html

I have a multi scale image sample here, using code from Scott Hanslemann and others.   I had other photos to include, but no matter what machine I tried to build this on, the Deep Zoom Composer seemed to want to consume 100% CPU and 100% memory once I got up to 10 images or so.  The Palladium speakers are certainly beautiful, and with some print quality images you can count the coils on the transistors in the crossover.  These speakers are so beautiful.



Friday, April 11, 2008 11:06:26 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, April 07, 2008

I sat down to continue some actual programming work today since it seemed support and manager-y type things had settled down.  Having gotten into WPF long before Silverlight, I found that to have a similar experience there are a lot of things I'm going to have to implement for the "CarSpot Silverlight Application Framework"

  • Commanding: I have seen references to people creating their own commanding frameworks for Silverlight 2.
  • Pages/Navigation: I wound up creating a Screen Stack for navigating from one Logical Page to another but without Pages and a Navigation Service I have to wire this all together myself. 
  • Security: It seems odd that Client Application Services was left out of Silverlight.  There are any number of ways to hack this in since the Silverlight control is sitting in the browser, but I was surprised to see that there was no "CurrentPrincipal" on the CurrentThread and so forth. I need security for my .aspx apps, why not for my Silverlight apps?  If one is building a line of busniess application, there are surely bits of information or actions that need to be hidden except for certain user roles.

Still, I'll take this over JavaScript any day.



Monday, April 07, 2008 3:30:18 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, March 22, 2008

I thought that a nice feature for my KlipschCorner System Profiles Area might be a deep zoom for the people who upload very high res photos.  I quickly realized I do not currently see any way to automate the creation of the Deep Zoom .bin files needed by the MultiScaleImage control.  One step beyond automation, it ought to be available in a server-side Silverlight API for the many people in hosted environmnets where they may not be able to install Deep Zoom Composer and shell execute an .exe. 



Saturday, March 22, 2008 11:58:13 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, March 07, 2008

Catastrophic failure!  Now that is an error message worthy of my college dating life!... anyhoo, I digress.  Many controls in winforms and WPF probably try to do things in the constructor or an "OnLoad" event of some kind that are not design-time-friendly, but the magic of the environment mostly keeps things like this from happening.  This magic dust has not yet been sprinkled upon the Silverlight 2.0 tools for VS2008, but I'm still very excited about the possibilities as this platform matures.  The error below occurs if I add some test images into the loaded event of the carousel and try to view the designer. 



Friday, March 07, 2008 8:23:31 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Ok, here is my silverlight 2 port of the ever popular Image Carousel sample.  For me, at least, it's much easier to follow what's going on than the JavaScript version.  Here's the markup required in the page:

<UserControl x:Class="ImageUploader.ImageCarousel"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="800" Height="600" Canvas.Top="0">   

<Canvas x:Name="_mainCanvas" Loaded="mainCanvasLoaded" MouseMove="whenMouseMoves"

        MouseLeftButtonDown="mainDown" MouseLeftButtonUp="mainUp" MouseLeave="mainCanvasMouseLeave" >

    <Canvas Name="imagesHolder" Canvas.Left="0" Canvas.Top="0">

        <Canvas Name="mainImageHolder" Canvas.Left="200" Canvas.Top="60" Canvas.ZIndex="169" Opacity="1.0">

            <Image Name="mainImage" Stretch="UniformToFill" Height="225" Width="300"/>

        </Canvas>

    </Canvas>

</Canvas>

</UserControl>

And here is the code-behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;

namespace ImageUploader
{
public partial class ImageCarousel : UserControl
{
public ImageCarousel()
{
InitializeComponent();
_mainDown = false;
_urls = new Dictionary<Image, string>();
_angles = new List<double>();
_imagePaths = new List<string>();
//_imagePaths.Add("Upload/1.jpg");
//_imagePaths.Add("Upload/2.jpg");
//_imagePaths.Add("Upload/3.jpg");
_dispTimer = new System.Windows.Threading.DispatcherTimer();
_dispTimer.Interval = TimeSpan.FromMilliseconds(25);
_dispTimer.Tick += new EventHandler(dt_Tick);
_dispTimer.Stop();
}

private List<double> _angles;
private bool _mainDown = false;
private List<string> _imagePaths;
private List<ScaleTransform> _scaleTransforms = new List<ScaleTransform>();
private double _speed = -0.0425;
private Dictionary<Image, string> _urls;
private System.Windows.Threading.DispatcherTimer _dispTimer;

public void AddImage(string imageUrl)
{
_mainDown = false;
_dispTimer.Stop();
_angles.Clear();
_scaleTransforms.Clear();
_urls.Clear();

for (int i = 0; i < _imagePaths.Count; ++i)
{
Canvas holder = (Canvas)_mainCanvas.FindName("imgHolder" + i);
_mainCanvas.Children.Remove(holder);
}

_imagePaths.Add(imageUrl);
Image img = (Image)_mainCanvas.FindName("mainImage");
string srcPath = _imagePaths[_imagePaths.Count -1 ];
img.Source = new BitmapImage(new Uri(GetURLBase() + srcPath));
img.SetValue(Canvas.ZIndexProperty, 169);

BuildImages();
}

private void mainCanvasLoaded(object sender, RoutedEventArgs e)
{
if (_imagePaths.Count > 0)
{
Image img = (Image)_mainCanvas.FindName("mainImage");
img.Source = new BitmapImage(new Uri(GetURLBase() + _imagePaths[0]));
img.SetValue(Canvas.ZIndexProperty, 169);
//
BuildImages();
}
}

public List<string> ServerImagePaths { get; set; }

protected void BuildImages()
{
int leftPos = 0;
for (int i = 0; i < _imagePaths.Count; ++i)
{
Canvas imgHolder = new Canvas();
imgHolder.SetValue(Canvas.NameProperty, "imgHolder" + i);
imgHolder.SetValue(Canvas.LeftProperty, leftPos);
imgHolder.SetValue(Canvas.TopProperty, 0);
//Come back to reflections...
//Canvas imgHolderReflection = new Canvas();
//imgHolderReflection.SetValue(Canvas.NameProperty, "imgHolderRef" + i);
//imgHolderReflection.SetValue(Canvas.TopProperty, 160);
//imgHolderReflection.Opacity = 1;
//LinearGradientBrush lgb = new LinearGradientBrush();
//lgb.StartPoint = new Point(0, 0);
//lgb.EndPoint = new Point(0, 1);
//lgb.GradientStops.Add(new GradientStop());
//lgb.GradientStops.Add(new GradientStop());
//lgb.GradientStops[0].Offset = .37;
//lgb.GradientStops[0].Color = Color.FromArgb(0x00, 0x00, 0x00, 0x00);
//lgb.GradientStops[1].Offset = 1;
//lgb.GradientStops[1].Color = Color.FromArgb(0x88, 0x00, 0x00, 0x00);
//imgHolderReflection.OpacityMask = lgb;
//imgHolder.Children.Add(imgHolderReflection);
//
Rectangle rec = new Rectangle();
rec.SetValue(Rectangle.NameProperty, "rec" + i);
rec.SetValue(Canvas.TopProperty, -6);
rec.SetValue(Canvas.LeftProperty, -6);
rec.Height = 92.0;
rec.Width = 92.0;
rec.Fill = new SolidColorBrush(Colors.LightGray);
imgHolder.Children.Add(rec);
//
Image img = new Image();
img.Cursor = Cursors.Hand;
img.MouseEnter +=new MouseEventHandler(img_MouseEnter);
img.MouseLeave +=new MouseEventHandler(img_MouseLeave);
img.MouseLeftButtonDown +=new MouseButtonEventHandler(img_MouseLeftButtonDown);
img.MouseLeftButtonUp +=new MouseButtonEventHandler(img_MouseLeftButtonUp);
img.Stretch = Stretch.UniformToFill;
img.SetValue(Image.NameProperty, "img" + i);
BitmapImage imgSource = new BitmapImage(new Uri(GetURLBase() + _imagePaths[i]));
_urls.Add(img, GetURLBase() + _imagePaths[i]);
img.Source = imgSource;
img.Width = 300;
img.Height = 225;

img.Opacity = 1;
imgHolder.Children.Add(img);
ScaleTransform imgCanvasScaleTrans = new ScaleTransform();
_scaleTransforms.Add(imgCanvasScaleTrans);
imgCanvasScaleTrans.ScaleX = 1;
imgCanvasScaleTrans.ScaleY = 1;
imgCanvasScaleTrans.CenterX = 50;
imgCanvasScaleTrans.CenterY = 50;
imgHolder.RenderTransform = imgCanvasScaleTrans;
//
_mainCanvas.Children.Add(imgHolder);
//
_angles.Add( i*((Math.PI*2)/_imagePaths.Count) );
}
PositionItems();
_mainDown = true;// huh?
//Set dispatcher interval
_dispTimer.Start();
}

/// <summary>
/// Not used yet
/// </summary>
/// <param name="i"></param>
protected void AddOne(int i)
{
}

void dt_Tick(object sender, EventArgs e)
{
MoveItems();
}

protected void PositionItems()
{
int radiusX=400;
int radiusY=110;
int centerX=425;
int centerY = 260;

for(int i = 0; i < _imagePaths.Count;++i)
{
double myX = Math.Cos(_angles[i])*radiusX + centerX;
double myY = Math.Sin(_angles[i])*radiusY + centerY;
Canvas imgCanvas = (Canvas)_mainCanvas.FindName("imgHolder" + i);
imgCanvas.SetValue(Canvas.LeftProperty, myX);
imgCanvas.SetValue(Canvas.TopProperty, myY);
ScaleTransform stRef = _scaleTransforms[i];
double sc = (myY - stRef.ScaleY) / (centerY + radiusY-stRef.ScaleY);
stRef.ScaleX = sc;
stRef.ScaleY = sc;
_angles[i] += _speed;
imgCanvas.SetValue(Canvas.ZIndexProperty, (int)myY);
}
}

protected void MoveItems()
{
if (_mainDown)
{
PositionItems();
}
}

void img_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{

}

void img_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Image img = (Image)sender;

     Image mainImg = (Image)_mainCanvas.FindName("mainImage");
mainImg.Source = new BitmapImage(new Uri(_urls[img]));
mainImg.SetValue(Canvas.ZIndexProperty, 169);
}

void img_MouseLeave(object sender, MouseEventArgs e)
{
//Change color BACK or something cool
}

void img_MouseEnter(object sender, MouseEventArgs e)
{
//Change color or something cool
}

private void whenMouseMoves(object sender, MouseEventArgs e)
{
Point pt = e.GetPosition(null);
double _root_xmouse = pt.X;
     double _root_ymouse = pt.Y    ;
_speed = ((_root_xmouse - 500) / 500) * 0.0755;
}

private void mainDown(object sender, MouseButtonEventArgs e)
{
_mainDown = true;
}

private void mainUp(object sender, MouseButtonEventArgs e)
{
_mainDown = false;

}

private void mainCanvasMouseLeave(object sender, MouseEventArgs e)
{

}


protected string GetURLBase()
{
string str = System.Windows.Application.Current.Host.Source.OriginalString;
return str.Substring(0, str.LastIndexOf("/")).Replace("ClientBin", string.Empty);
}
}
}


 

Not formatted as well as my last posts, I know.  It seems that Google reader doesn't like it when I paste out of Word. Now, one thing that is not very apparent is how to add this UserControl to another UserControl, assuming that JUST having an image carousel is not what you're after.  You need to include the following on the top-level user control:

xmlns:CarSpot="clr-namespace:ImageUploader"

which then allows you to refer to the code:

<CarSpot:ImageCarousel x:Name="_carousel"></CarSpot:ImageCarousel>

This is an aspect of XAML I don't like. There's nothing (that I can see) in the structure/XSD of a XAML document that tells me that an attribute value starting with "clr-namespace:" is something special.  There's a lot of magic like this in XAML.  The Visual Studio "Silverlight Chainer" as the file calls the Beta 1 tools are definately buggy and incomplete.  I am lucky to be able to hit F5 10 times before I get errors trying to debug and having to kill Cassini and VS2008.  I've also found that it's very easy to crash the designer by having naughty things in your code-behind.  It appears that unlike more mature design-time experiences there is no "IsDesignTime" flag that would allow you to keep from having code execute while designing.  I can tell that this is Cider by the wonderfully descriptive stack traces I get, yet things like dragging the new Silverlight controls onto the surface or even moving them around (I'm sick of tweaking location with Canvas.Top/Canvas.Left) are not working yet.  This is a huge step forward, but I've got to think there'll be another beta after this one now.

And that's that.  I should have some more Silverlight 2 observations later, especially about the threading and HTTP aspects.



Friday, March 07, 2008 4:36:07 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [4]  |  Trackback

I have translated the Silverlight "Image Carousel" into Silverlight 2.0 and C# rather than the "JS writing XAML" model.  If I get the code cleaned up this afternoon I will post the carousel portion.



Friday, March 07, 2008 1:33:00 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback