Monday 31 August 2009

Retro Flip Clock WPF Control

I know, I know...I last thing we need is another WPF Clock.  However, I recently went looking for a Flip Clock control and found surprisingly few - in fact, no usable ones.  So, it was back to the drawing board.

Most of the attention has been on appearance, rather than functionality.  Feel free to modify and enhance.
You can download the source code with sample project here.

Sorry for the broken link, the server that hosted this file has been rebuilt and no longer contains my downloads.  Here is a link on my SkyDrive.  This zip file contains the core files, you will need to add them to a project.

Wednesday 26 August 2009

Ethics and IT

I was recently offered employment at an investment bank in London, to which I respectfully declined.  The agent I was dealing with was somewhat surprised and wanted to know why.  I told him that I couldn't bring myself to work for the banksters.  He was amused and replied "should my moral compass change..." the offer still stood.  Interesting.

I never considered my moral compass to be so fluid, to actually consider assisting corporate criminals rape the general public. What? Bigger? Better? Faster? More Efficiently?  I suppose I could find a way to justify it, but I couldn't see myself telling someone that is what I did for a living.

I, like the next guy, seeks to be well rewarded for their work.  That doesn't automatically mean I do anything just because the rate is high.  I consider my services to be valuable and the products I produce hopefully forward the goals and aims of my client.  I mean, why do it at all if some good doesn't come out of it?

I was discussing this point with some friends; where do you draw the line?  I knew where my line was.  But it occurred to me that I shouldn't assume everyone has or is even aware of "The Line".  That there may be some people in the IT industry who will simply work on any project provided the price was right.

Would you design software which assisted a pharmaceutical company to develop new mind-altering drugs which may result in suicide or mayhem?

Or how about software which controls a guided missile that has an impressive kill-ratio?  (no, it's not the X-Box)

No? Well somebody does.

Now, these are not new questions.  While at engineering school we had a Technology and Ethics course, but it never delved into these types of issues (which may have had something to do with the Department of Defence contracts).  Maybe no course can answer these questions, maybe only you can come up with an answer you can live with.

So, in addition to the legalised drug pushers and the war-mongers we add a new villain (well, not new, just newly exposed) - the Banker, purveyors of economic chaos and destruction.  The respected banker, pillar of the community.  Lovely.

If your love of banking is so strong, why not work for the Financial Services Authority?  I hear they're quite busy and need all the help they can get.

Tuesday 25 August 2009

Layout Manager for Prism v2

One of the issues you may encounter when working on a Prism project is the management of regions and views within your application. While the RegionManager does an adequate job of managing regions, the orchestration of views and regions is pretty much left up to the developer.

A common approach is to define string constants in a common infrastructure assembly and injecting views into regions using these constants. This gets the job done, but adds rigidity to your application. For applications which require multiple layouts, coordinating regions and views can be a bit tedious.

One common approach I would not recommend is injecting your views in your module's Initialize method.
public void Initialize()
{
    var view = new MyView();
    _Container.RegisterInstance<IMyView>(view);
    _RegionManager.Regions[RegionNames.Shell].Add(view);
}
This violates the encapsulation of the module, restricting the reuse of the module.

On one project, we opted to create a "layout module". The sole purpose of this module was to load a layout UserControl into the Shell region of the main application window, and injecting the views into its own defined regions. Definitely a step in the right direction by decoupling the module views from the regions. The layout module was defined and loaded like any other module, but had to be the last module loaded due to its dependencies. One drawback to this approach was the increasing number of dependencies. The layout module had to reference all the infrastructure assemblies of the views it was required to manage.

Still this solution felt a bit too purpose-built. And other issues quickly arose, such as multiple layout support.  Ideally we were looking for a complete decoupling of regions and views with the ability to dynamically load layouts as required.

We quite liked the idea of using layout views, views whose sole purpose was to define regions, and providing no business or UI logic. But, the source and introduction of these views needed to be dynamic and flexible. The LayoutManager is my first attempt at tackling this issue. Its purpose is to dynamically manage one or more layout configurations for a Prism application.

To compile and run the LayoutManager you will need Visual Studio 2008 SP1 and the latest version of the Composite Application Guidance for WPF and Silverlight - February 2009.

The solution is fairly standard Prism solution, consisting of an Infrastructure, Shell and Modules projects. For the sake of simplicity, I've only included a single Modules project, where normally there would be more.

The LayoutManager maintains a collection of Layout objects, which define layout controls, along with the views that will reside in the layout.

Configuration
The LayoutManager is configured by a LayoutProvider specified in your app.config file.

<section name="layoutProvider" type="Composite.Layout.Configuration.LayoutProviderSection, Composite.Layout"/>

Currently, two providers are available: ConfigLayoutProvider and XamlLayoutProvider. Custom providers can be used by inheriting from LayoutProviderBase.

ConfigLayoutProvider
Defines the LayoutManager in the app.config file as shown below:
<layoutProvider name="ConfigLayoutProvider" type="Composite.Layout.Configuration.ConfigLayoutProvider, Composite.Layout">
    <layoutManager shellName="Shell" >
      <layouts>
        <layout name="FirstLayout" 
              filename="Layouts\FirstLayout.xaml" 
              fullname="First Layout" 
              isDefault="True"
              description="This is the default layout" 
              thumbnailSource="pack://application:,,,/LayoutManager.Infrastructure;component/Resources/Images/layout1.png">
          <views>
            <view typeName="LayoutManager.Infrastructure.IViewA, LayoutManager.Infrastructure" regionName="Left"  />
            <view typeName="LayoutManager.Infrastructure.IViewB, LayoutManager.Infrastructure" regionName="Right" />
            <viewModel typeName="LayoutManager.Infrastructure.IMenuViewModel, LayoutManager.Infrastructure" regionName="Menu"  viewProperty="View"/>
          </views>
        </layout>
        ...
          </layouts>
    </layoutManager>
</layoutProvider>

XamlLayoutProvider
Defines the LayoutManager in Xaml

<layoutProvider name="XamlLayoutProvider"
type="Composite.Layout.Configuration.XamlLayoutProvider, Composite.Layout"
filename="Layouts\LayoutConfiguration.xaml"/>

The source of the Xaml can be specified by type or by filename.
<Layout:LayoutManager xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                      xmlns:Layout="clr-namespace:Composite.Layout;assembly=Composite.Layout"
                      xmlns:Infrastructure="clr-namespace:LayoutManager.Infrastructure;assembly=LayoutManager.Infrastructure"
                      ShellName="Shell">
    <Layout:LayoutManager.Layouts>
        <Layout:Layout x:Name="FirstLayout"
                       Fullname="First Layout"
                       Filename="Layouts\FirstLayout.xaml"
                       Description="This is the default layout"
                       ThumbnailSource="pack://application:,,,/LayoutManager.Infrastructure;component/Resources/Images/layout1.png"
                       IsDefault="True">
            <Layout:Layout.Views>
                <Layout:ViewModel RegionName="Menu"
                                  Type="{x:Type Infrastructure:IMenuViewModel}"
                                  ViewProperty="View" />
                <Layout:View RegionName="Left"
                             Type="{x:Type Infrastructure:IViewA}" />
                <Layout:View RegionName="Right"
                             Type="{x:Type Infrastructure:IViewB}" />
            </Layout:Layout.Views>
        </Layout:Layout>
 ...
    </Layout:LayoutManager.Layouts>
</Layout:LayoutManager>

Each Layout contains a Views collection. The views collection accommodates both Views and ViewModels. The View specifies what view control is to be loaded and what region it is to be placed in. You can also set the visibility for the view. Use the ViewProperty of the ViewModel to specify the name of the property on your ViewModel which holds the View.

The LayoutManager is loaded after all of the modules have initialized. In the Bootstrapper.cs:

protected override void InitializeModules()
{
     base.InitializeModules();
     InitializeLayoutManager();
}

private void InitializeLayoutManager()
{
     var layoutManager = LayoutConfigurationManager.LayoutManager;
     layoutManager.Initialize(Container);
     Container.RegisterInstance(layoutManager, new ContainerControlledLifetimeManager());
     //parameterless LoadLayout loads the default Layout into the Shell
     layoutManager.LoadLayout();
}
The LayoutManager requires use of the Container. Once your layouts have been loaded, call the Initialize method passing in the container.

Once that is done, you can register the LayoutManager in the container making it accessible to other modules.

Loading a Layout
Layouts are loaded by calling the LoadLayout method of the LayoutManager.

LoadLayout() loads the default layout in the Shell

LoadLayout(string layoutName) loads the named layout in the Shell

The MenuViewModel.cs illustrates the use of LoadLayout:
private void LayoutCommandExecute(ILayout layout)
{
var layoutManager = _Container.Resolve<ILayoutManager>(); 
layoutManager.LoadLayout(layout.Name);
}

The basic sequence of loading a layout is:
  1. If there is a current layout, remove it from the RegionManager.
  2. Clear out any controls that were bound to any regions. This step is necessary otherwise you will get an InvalidOperationException ("This control is being associated with a region, but the control is already bound to something else") when you try to reload it in the future. Currently, the LayoutManager only supports ItemsControls, ContentControls and Panels using the RegionManager.RegionName attached property.
  3. Add the new Layout Control to the RegionManager.
  4. Register any Regions contained within the Layout Control.
  5. Load any views associated with the new layout.
Events
There are several events raised by the LayoutManager:

  • LayoutManagerInitializedEvent raised at the end of Initialize (see MenuViewModel.cs for an example of subscribing to this event)
  • LayoutLoadingEvent raised at the beginning of LoadLayout
  • LayoutLoadedEvent raised at the end of LoadLayout
  • LayoutUnloadingEvent raised before the current layout is about to be unloaded
  • LayoutUnloadedEvent raised after the current layout has been unloaded
All of these events are published through the EventAggregator.

Limitations
Currently there are several limitations with the LayoutManager, these are:
LayoutManager currently only supports UserControls as layout controls. There is also the basic assumption that your application main window has a single region defined, where layout controls are injected. Regions must be defined in XAML using the RegionManager.RegionName attached property.

Other Considerations
While the LayoutManager does decouple the regions from the views, it does not entirely do away with string-based region names. Dynamic manipulation of regions and views in code will still rely on region names (see the AddCommandExecute method in MenuViewModel.cs on how to programmatically add layouts). And region name attributes must match actual region names in the Layout control.

A possible approach to addressing this dependency may be to introduce a RegionType enumeration such as Top, Bottom, Left, Right, Center, StatusBar, Menu, Toolbar, etc. In which case, the LayoutManager could resolve these regions regardless of string names.

I have not tested the LayoutManager in all possible scenarios, such as nested layouts and custom RegionAdapters, or with Silverlight.

You can download the LayoutManager source code here.

Monday 24 August 2009

StringBuilder Extensions

I recently had a requirement to modify some Html-based reports.  These reports were built up in code using a StringBuilder, along with report content data.  The plus-point is that it worked and delivered the desired reports.  On the downside, the code was tedious and verbose.  For example:
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<TABLE id=\"Table2\" width=\"100%\">");
stringBuilder.Append("<TR style=\"page-break-inside : avoid\"><TD class=\"reportHeaderTitle\" >" + ResourceManager.GetString("ReportTitle", culture) + "</TD></TR>");
stringBuilder.Append("<TR><TD style=\"PADDING-LEFT: 5px; PADDING-BOTTOM:10px\" width=\"100%\">");
stringBuilder.Append("<TABLE id=\"Table2\" width=\"100%\">");
stringBuilder.Append("<TR style=\"page-break-inside : avoid\"><TD class='reportHeader' align=\"center\" width=\"50%\">" + ResourceManager.GetString("ReportSubTitle", culture) + "</TD>");
stringBuilder.Append("<TD class='reportHeader' align=\"center\" width=\"50%\">" + ResourceManager.GetString("ReportResults", culture) + "</TD></TR>");
...

I was staring at 1000s of lines of code like this.  After a while it was all a blur.

My first impulse was to subclass the StringBuilder and build up some Html-specific methods.  Unfortunately, the StringBuilder class is sealed.   I also looked at the HtmlTextWriter class, a class primarily used when working with custom server controls.  But, this seemed overkill for the purpose.  I just needed a clean way to build-up an Html string in code.

I then turned to writing a set of Extensions Methods to provide this functionality.
#region Using Directives

using System.Text;

#endregion

namespace Common.Extensions
{
    public static class StringBuilderExtensions
    {
        public static void BeginTable(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("<table>");
        }

        public static void EndTable(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("</table>");
        }

        public static void BeginRow(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("<tr>");
        }
        ...
    }
}

In refactoring the code I first pulled all of the inline styles out and into style classes.  Then I began migrating the existing code over to use the new extension methods.  The refactored code looks something like this:

var stringBuilder = new StringBuilder();
stringBuilder.HorizontalRule();
stringBuilder.BeginH2();
stringBuilder.Append(ResourceManager.GetString("ReportTitle", _Culture));
stringBuilder.EndH2();

stringBuilder.BeginTable();
stringBuilder.BeginTHead();
stringBuilder.BeginRow();
stringBuilder.BeginCell("reportHeader");
stringBuilder.Append(ResourceManager.GetString("ReportSubTitle", _Culture));
stringBuilder.EndCell();
stringBuilder.BeginCell();
stringBuilder.Append(ResourceManager.GetString("ReportResults", _Culture));
stringBuilder.EndCell();
stringBuilder.EndRow();
stringBuilder.EndTHead();
...
In it's current state, the extensions only supply the Html tags that were required, but could easily be expanded to support all of the commonly used tags.  I personally find this refactored code much easier to read and maintain.

You can download the StringBuilderExtensions.zip here.
I hope this may be of some use.

Extension Method Usage

If you are like me, once you have the Big Extension Method Realisation you tend to go overboard on their use.  This is eventually followed by the more practical Extension Method Usage Realisation.  This second realisation puts Extension Methods into perspective.

For example, you could quite easily create an extension method like this:
public static bool IsNull(this object thisObject)
{
return thisObject == null;
}
This method can replace
if(myObject==null)

with...

if(myObject.IsNull())

But is it worth it?  Is there anything wrong with myObject==null?  Of course not.  So why change it or replace it with an extension method.  It adds no new functionality, just a change of syntax.
Additionally, you will often collect your extension methods and place them in a common assembly.  Use of the above extension method creates an dependency on this assembly.  But what does it buy you?  I would argue, nothing.  So, lose it.
From my experience in working with extension methods, I've found the most valuable to be methods that supply missing functionality or ones that encapsulate often repeated code. 
An example of this is checking if a collection contains any items, kind of like a cousin of String.IsNullOrEmpty.  For me, this is a prime candidate for an extension method, and should have been included in the IList interface (IMHO).

public static bool HasItems(this IList collection)
{
return collection != null && collection.Count > 0;
}
So we reduce
if(myCollection!=null && myCollection.Count>0)

to a clean and simple

if(myCollection.HasItems())

I know there is not a shortage of resources on the web regarding extension methods.  However, after working with them for a few years, I have managed to collect a few "must have" extension methods, which I will be posting over time.

CodeProject Articles

I have authored two CodeProject articles on WPF and Prism.

WPF Gadget Container Control
http://www.codeproject.com/KB/WPF/WPFGadget.aspx

Layout Manager for Prism v2
http://www.codeproject.com/KB/WPF/PrismLayoutManager.aspx

If you are interested in this area of .Net, check them out and let me know what you think.

Introduction

This is my second blogging attempt, the first failed due to blogger's guilt. Now I have a renewed sense of purpose and dedication and have allocated time to make this a respectable blog. Round Two...