First Post

by Administrator 4. January 2010 02:31

After having taken most of December 2009 and the first few days of January off of blogging I’m back with my traditional first post of 2010.  There are six years worth of blogging history listed to the right, and I was blogging for 6 years before that, starting as so many people did on Geocities.  I do enjoy using the new year as a moment of reflection, and I do enjoy putting a subset of that reflection online as a public record I can go back and check.

How was 2009?

Blogging & Community

I’m not sure if I’ve ever come out and said this, but my goal with blogging has never been to write a Widely Accessible blog.  I’ll leave that to people who are extremely good at it like Shawn Wildermuth and Tim Huer.  There are plenty of people who will write the “Quick tour of Silverlight 4” articles and bless them for it, I know I benefit from their work.

I like to write more in depth articles, things not everyone will get their head around.  Things involving design patterns, Reflection Emit, and approaches to wrangling complexity.  On one hand maybe that makes me an elitist, on the other hand I like to think I’m just catering to a different (smaller) audience.  If you want to know how to do TwoWay binding to dictionaries in Silverlight 3 or handle single-import Recomposition in MEF then I’m your man.

From that perspective I think I did pretty OK in 2009.  I would have liked to have gotten much further in the development of the AGT project and possibly blogged a little bit more in general.  In Q4 2009 I tried to settle on a 1 article per week rhythm that I didn’t quite meet.

Community wise I exceeded my own goals in all areas but one.  I was re-awarded the Microsoft MVP, I did something like 9 public speaking events and attended several first party Microsoft events like MDC.  What I wish I’d done more of was to attend my own local .NET User Group meetings.  I have a valid excuse for this, however.  These are always on Tuesdays, and Tuesday night is extremely overloaded for me.  I have the BigHammer Patterns and Practices group which is usually followed by a wine tasting in downtown Milwaukee, a trivia game, and the occasional CarSpot Reunion.

Personal

2009 was a hard year.  I worked way too much in 2009.  This cannot continue.  My kids are growing up so fast and they are outrageously fun.  While we managed to have some family fun, I did goof in not taking my wife on a kid-less vacation in 2009.  Hopefully my friends and family will agree that I took my cooking to the next level, though there are many levels yet to go.  Despite some hard stretches this year, I did get my dream car and stockpiled a lot of wine in my basement.

The biggest personal defeats of 2009 were just plain not finding time for some of my personal projects and not exercising at all.

Professional

Professionally, 2009 was murder.  I went from being Director of Solution Strategy at CarSpot at the end of 2008 to freelance consultant while looking for work to Guy who Writes Code at BigHammer.  I worked way too much and every other area of my life suffered as a result.  Enough said about that.

Goals for 2010

Blogging & Community Goals

  • Consistent with my stated blogging goals, I’ve got a lot of solid technical content coming in 2010.  I’m shooting for a rhythm of one technical and one personal/cultural article per week.  I would also like to pull of one Political article per month – I’m sure there will be plenty to talk about in 2010.
  • I need to make it to the Milwaukee-area Wisconsin .NET User’s group more often when there’s not delicious wine to be tasted.
  • I am attending the 2010 Global MVP Summit next month, which should be completely awesome.
  • There’s a good chance I’m going to MIX in March, which should also be awesome.
  • I would like to do at least six Regional speaking events in 2010 ranging from nearby to Indianapolis to Minnesota.
  • I would like to possibly encourage the reformation of the local Silverlight Special Interest Group
  • I would like to schedule some Nerd Lunches and Nerd Dinners, which I did VERY little of in 2009

Personal

I need to work less in 2010, and I need to take my wife on a vacation. 

  • I also absolutely need to make time for some of my personal projects. 
    • Assuming Windows Mobile 7 ever happens I’d like to have one mobile application and one website pulling in a few bucks by the end of the year.
  • Light exercise, like 30 sit-ups a day (starting tomorrow)
  • I need to re-engage with the Klipsch community !
  • Buy a snow blower or contract a service (my neighbors know what I mean)

Other than that my personal goals are none of your business ;)

Professional

I need to work less in 2010.  I also have some goals specific to my current position that I can’t really talk about.  There’s also a lot of overlap here with the Blogging and Community Goals.  Professional-wise I think 2010 may be a year to shore up and make sure I’m as good in certain areas as I think I am. 

I am also going to get at least “C+ competent” at regular expressions this year.  I’ve avoided becoming even familiar with regex for the past 15+ years and this must stop.  A very simple URL rewriting regex for IIS 7 took me WAY too long to get right today.

I wish you all a happy and productive 2010.

Tags:

Using MEF to Work Offline in Silverlight 3

by Administrator 8. December 2009 02:22

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.

Tags:

Silverlight 4

by Administrator 25. November 2009 20:36

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!

Tags:

Speaking at Chippewa Valley Code Camp 2009

by Administrator 6. November 2009 04:18

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.

Tags:

New Car

by Administrator 23. October 2009 20:30

In order to increase the prestige of the Microsoft MVP program and the practice of .Net user group presentations, I have purchased a new automobile.

DamonS4

This is my 2010 Audi S4 – there are many like it but this one is mine.  After driving the WRX for almost 9 years I thought I could splurge a little bit. 

Tags:

Silverlight and Mailto:

by Administrator 22. October 2009 04:24

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.

Tags:

Speaking at MadCamp and 7 Fest

by Administrator 15. October 2009 01:36

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

Tags:

Argentum Tela Design Surface [21] &ndash; Name Provider

by Administrator 5. October 2009 02:08

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.

Tags:

Argentum Tela Design Surface [20] &ndash; Getting up to speed

by Administrator 25. September 2009 02:00

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.

Tags:

History for View Based Navigation in Silverlight 3

by Administrator 22. September 2009 00:17

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.

Tags:

About the author

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

INETA Community Speakers Program

Month List

Page List

flickr photostream