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!


About the Author

Evan Hutnick

works as a Developer Evangelist for Telerik specializing in Silverlight and WPF in addition to being a Microsoft MVP for Silverlight. After years as a development enthusiast in .Net technologies, he has been able to excel in XAML development helping to provide samples and expertise in these cutting edge technologies. You can find him on Twitter @EvanHutnick.

Comments

Comments are disabled in preview mode.