The LOB Chronicles Episode 7: MEF, Attributes, and Behaviors (Oh my!)

Thursday, September 22, 2011 by Evan Hutnick | Comments 11

The last time we all got together was before a little conference known as Build. Everyone learned a ton of information about what is coming next in Windows 8 and the WinRT platform, but one thing stuck out for sure – Metro is in. Thankfully our designers have been all over this for quite a while and we have some very nice things in the works, most of which I can’t publicly talk about (yet!), but in the terms of this application we’re definitely on the right track. And don’t forget, an investment in a Silverlight application today means that it will continue to run tomorrow on the next version of Windows, so full speed ahead with development and this week’s topic – MEF, Attributes, and Behaviors.

If you recall last time I mentioned the concept of having an Infrastructure project for sharing reusable code between modules. As it turns out we have a few nice things tucked in there already that are helping to shape the development of the CRM demo already – most notably the behaviors that we are using for populating views within our application.

First We Have an Attribute

Taking a peek at our Infrastructure project you can see a combination of ViewExportAttribute along with the AutoPopulateExportedViewsBehavior, which work hand-in-hand for enabling an easy experience for populating a view within a region by utilizing attributes. Looking at the code for ViewExportAttribute first, we can immediately make out a few things:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    [MetadataAttribute]
    public class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
    {
        public ViewExportAttribute()
            : base(typeof(object))
        { }
  
        public ViewExportAttribute(string viewName)
            : base(viewName, typeof(object))
        { }
  
        public string RegionName { get; set; }
  
        private bool isActiveByDefault = true;
        public bool IsActiveByDefault
        {
            get
            {
                return this.isActiveByDefault;
            }
            set
            {
                this.isActiveByDefault = value;
            }
        }
    }

First up we know we’re providing Metadata and working with an ExportAttribute – which provides a very strong hint that MEF is involved here. The interface simply defines our use of RegionName and IsActiveByDefault, so nothing too exciting there except that we’re sticking with a best practice to utilize an interface instead of simply implementation. The real point of interest here is RegionName – with this we will be able to provide a named region that will contain any given view, so rather than worrying about injecting region managers or anything of the sort, our implementation will be very clean. Additionally, while we define isActiveByDefault to be true initially, we can easily turn this off when necessary to load views for eventual display.

What About the Behavior?

The cat is already out of the bag that we are using Prism along with MEF, opening up the possibility for a behavior that will allow us to easily define a way to populate views once and utilize that across our application. As we did before, we’ll start with code and then run through the explanation. Here is a look at the entire behavior as it exists today:

[Export(typeof(AutoPopulateExportedViewsBehavior))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
    {
        protected override void OnAttach()
        {
            AddRegisteredViews();
        }
  
        public void OnImportsSatisfied()
        {
            AddRegisteredViews();
        }
  
        private void AddRegisteredViews()
        {
            if (this.Region != null)
            {
                foreach (var viewEntry in this.RegisteredViews)
                {
                    if (viewEntry.Metadata.RegionName == this.Region.Name)
                    {
                        var view = viewEntry.Value;
  
                        if (!this.Region.Views.Contains(view))
                        {
                            this.Region.Add(view);
                            if (!viewEntry.Metadata.IsActiveByDefault)
                            {
                                this.Region.Deactivate(view);
                            }
                        }
                    }
                }
            }
        }
  
        [ImportMany(AllowRecomposition = true)]
        public Lazy<object, IViewRegionRegistration>[] RegisteredViews { get; set; }
    }

From the top we can see that this is ready for use by MEF (Export…) as well as unique to each instance (PartCreationPolicy.NonShared)- that much is pretty standard. AddRegisteredViews is where things get interesting. After satisfying the ImportMany on RegisteredViews we are able to cycle through all views (viewEntry) in the imported collection and examine the attribute RegionName (remember this from ViewExportAttribute?). If RegionName matches the name for the given region we’re working with we then do a quick check to ensure the view is not already added to the region as well as perform deactivation if necessary – all from our metadata. If everything checks out, the view is added to the region and appropriately active if we haven’t explicitly deactivated it.

Of course you can’t simply create a behavior and assume it will just work – we need to implement this in our BootStrapper. That code is surprisingly easy, as seen here:

protected override Microsoft.Practices.Prism.Regions.IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
        {
            var factory = base.ConfigureDefaultRegionBehaviors();
            factory.AddIfMissing(typeof(CRM.Infrastructure.AutoPopulateExportedViewsBehavior).Name, typeof(AutoPopulateExportedViewsBehavior));
            return factory;
        }

Now we’ve got our behavior… behaving as expected. Couldn’t resist that joke. :)

What About Implementation?

As you might imagine from what I’ve mentioned before, our views are very much work in progress as far as visual appeal is concerned (believe me though, the ones we’ve got in the “done conceptually but not implemented” department are beautiful!), but we do have a fair number of regions defined to determine what the overall layout will look like when all is said and done. One such region within the Companies module looks like this (extra properties stripped for brevity):

<ContentControl Grid.Column="0" prism:RegionManager.RegionName="CompaniesListRegion" x:Name="CompaniesListRegion">
</ContentControl>

Standard Prism implementation with a RegionName… which then translates into the following attribute usage on one of our views:

[ViewExport(RegionName = "CompaniesListRegion")]
    public partial class CompaniesListUserControl : UserControl
    {
        public CompaniesListUserControl()
        {
            InitializeComponent();
        }
    }

Note that this is all the code we need in our code-behind for the view. With our custom attribute we have a CompaniesListRegion defined as the eventual home for this view, so when the magic behind the curtains that is MEF runs through the metadata will allow this view to be added to the correct region, if not added already, freeing us from having to do additional work to inject views or worry otherwise about managing what to load when.

Awesome, right? I love being inspired by our development team time and again.

Next Episode

We discussed a few code-heavy topics here. MEF, Behaviors, Attributes – oh my, now we’re talking a very modern .Net application that is taking advantage of the latest and greatest along with Prism for some proven results, combined with Silverlight which is providing a fantastic platform for rapid development. Of course the road to all this working wasn’t entirely straightforward, so tune into next week to see how we tackled a uniquely MEF’d up problem that we ran into when trying to get all these things working together nicely. While you wait, be sure to check out the SP1 releases from our Xaml stack (Silverlight over here, WPF over there) and we’ll see you next time!

Stay tuned!

@EvanHutnick


11 Comments

  • Rob 26 Sep 2011
    While I'm very excited to see this project progressing, I'm a little disappointed that you went the MEF route. MEF's declarative approach of attributing is "modern", but since you're mixing in Prism and presenting a lot of new concepts, I would have rather seen Unity's programmatic approach where you can actually see code doing more of the work (less 'magic').
    Maybe we'll see a lot more MEF in the future, so this is the way to go. In any case, keep it coming! 
  • Evan 27 Sep 2011
    Hey Rob,
    I can understand your stance on MEF as I'm sometimes on the fence about it as well, but for some of the things involved in this project it seemed like the Prism/MEF combination was the way to go.
    That said, this definitely isn't the last larger scale Silverlight resource we're going to be making and the next one I've got in mind uses Prism/Unity to handle everything, so it shouldn't disappoint.
    Thanks for reading!!
  • Rob 27 Sep 2011
    Good to hear Evan! Got to love Microsoft for overlapping technologies and muddying the waters.
    I have a similar project on my hands and I am very interested in knowing/seeing how you implement the search functionality.Of course, seeing the skeleton of the entire project would also help, but if you could talk about what you're thinking for the search, that would definitely be of immediate use.
  • Rob 27 Sep 2011
    Good to hear Evan! Got to love Microsoft for overlapping technologies and muddying the waters.
    I have a similar project on my hands and I am very interested in knowing/seeing how you implement the search functionality.Of course, seeing the skeleton of the entire project would also help, but if you could talk about what you're thinking for the search, that would definitely be of immediate use.
  • Felickz 31 Oct 2011
    Silverlight is NOT supported in Metro BTW..
  • Evan 31 Oct 2011
    Silverlight doesn't have to be supported in Metro view, that's why there is still Desktop view in Windows 8. :)  Best of both worlds, plus a very Silverlight-like Xaml language in WinRT that greatly reduces the learning curve. 
  • Felickz 02 Nov 2011
    Point taken, but I think that being "full speed ahead" when choosing Silverlight for a LOB app should be taken with the understanding that Microsoft is moving (has already moved its core team members http://www.riagenic.com/archives/683) to other XAML based technologies in the future.  My main take away was that Silverlight is very possibly going into maintenance mode and the yearly updates of the SL framework may slow down after v5.
  • Felickz 02 Nov 2011
    Point taken, but I think that being "full speed ahead" when choosing Silverlight for a LOB app should be taken with the understanding that Microsoft is moving (has already moved its core team members http://www.riagenic.com/archives/683) to other XAML based technologies in the future.  My main take away was that Silverlight is very possibly going into maintenance mode and the yearly updates of the SL framework may slow down after v5.
  • Felickz 02 Nov 2011
    F5 causes double post (telerik bug)
  • Felickz 02 Nov 2011
    F5 causes double post (telerik bug)
  • Evan 02 Nov 2011
    Point taken. :)  But look at Winforms, no major updates yet companies still start new projects today based on this platform because it is stable and mature.  Silverlight may be the new Winforms, receiving nothing more than service updates, but if the platform has matured to that point is that really such a bad thing?  If we're talking platform lifecycle, this means applications we write today that work on XP, Vista, Win7, and OSx (with some feature exceptions) will continue to work down the road in all these platforms as well as Windows 8 Desktop - all with a single compile.  I for one, speaking as a Silverlight MVP, would rather see the Xaml folks within Microsoft working on developing new (WinRT) or evolving existing (Windows Phone) platforms than putting all their eggs in the SL runtime, as it accomplishes most if not all goals it set out to... and if you need more features than SL has to offer for Windows development, there's always our friend WPF (which lives on with a host of improvements in .Net 4.5).  This allows Telerik as a company to also evolve our product lines as well as utilize the maturity and stability of Silverlight 5 to further enhance our suite without the hassle of the rapid development cycle that hindered some of our efforts from SL2-4.  I can't give away any secrets just yet, but 2012 is going to be a HUGE year for Telerik + Silverlight.  Trust me. :)

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)