Architecting UI plug-ins for PRISM applications

by Marcin Kaluza 19. October 2010 05:59

It often happens that a particular feature of an application is not used very often, but when you need it you would like it to be accessible easily and without hassle: think calculator in an order entry program. Surely the user can live without it and use the calculator provided with the OS, but what if he could access a simple calculator within a click or two inside the application itself? I could give you more examples like this, but they all fall under the same umbrella of add-ons or plug-ins. Things you surely could live without but having them handy makes the work so much easier.

As it happens I have recently built similar functionality into a PRISM application and the rest of the post describes how to do it in a (hopefully) more less generic fashion.

The plan

I envisioned a plug-in to be  visual “gadget” which the user could access through a window hosting all the plug-ins available in an application in a fashion similar to Visual Studio’s Toolbox. The important thing here is the fact that each plug-in would need to provide it’s own UI: at the end of the day the application cannot up-front provide UI for plug-ins which have not yet been written. Given these requirement I came up with the following plan:

  1. The application’s main window would provide screen estate for displaying all the plug-ins in form of a PRISM’s view region
  2. The plug-ins view region would be filled by a PluginsView containing an ItemsControl which would wrap each individual plug-in into expander
  3. Underlying View Model would simply expose a collection of IPlugin elements which could be bound to the view’s item control

The Plugin

Each of the plug-ins is required to implement IPlugin interface which first of all allows it to be discovered by the PluginHost and secondly forces the plug-in to expose a “View” of sorts which can be displayed to the user. As we’re in the PRISM world here, I would expect the View to be also injected into plug-in, but it is not strictly speaking required. Each plug-in also needs to provide user readable name which will be displayed in the UI.

/// <summary>
/// Common interface for all plugins
/// </summary>
public interface IPlugin
{
    /// <summary>
    /// Gets the name of the plugin.
    /// </summary>
    /// <value>The name.</value>
    string Name { get;  }

    /// <summary>
    /// Gets the UI representation of the plugin.
    /// </summary>
    /// <value>The view.</value>
    object View { get; }
}

All the plug-ins need to be registered with the IoC container and this, in case of PRISM applications is usually done during module initialization. One thing which has to be kept in mind in such case is to give unique “key” (name) to each plug-in during registration as there will be more than one implementation of IPlugin available:

public class PluginsModule : IModule
{
    private readonly IUnityContainer _unityContainer;

    public PluginsModule(IUnityContainer unityContainer)
    {
        _unityContainer = unityContainer;
    }

    public void Initialize()
    {
        _unityContainer.RegisterType<ICalculatorPluginView, CalculatorPluginView>();
        _unityContainer.RegisterType<INewsPluginView, NewsPluginView>();

        _unityContainer.RegisterType<IPlugin, CalculatorPlugin>("Calculator");
        _unityContainer.RegisterType<IPlugin, NewsPlugin>("News");
    }
}

 

The PluginHost

The role of the plug-in host is to simply consume all the implementations of IPlugin available in the application and expose them in a single collection. The simplest way to achieve that is to get them injected into the host by an IoC container. I was toying with the idea of using MEF for this purpose but at the end of the day settled for simplicity  and decided that Unity which is anyway used by PRISM will do the job as well. The following code fragment shows the inner workings of the host. As you can see the constructor gets all the implementations of IPlugin as an array which may seem a bit awkward, but this is what Unity expects out of the box. Having said that it is possible to inject an IEnumerable<IPlugin> which would seem more natural but it requires extra parameter when it comes to registering of the PluginHost with the container.

/// <summary>
/// Hosts all the plugins in the application
/// </summary>
public class PluginHost
{
    private readonly IPluginHostView _view;
    private readonly IPlugin[] _plugins;

    /// <summary>
    /// Initializes a new instance of the <see cref="PluginHost"/> class.
    /// </summary>
    /// <param name="view">The view.</param>
    /// <param name="plugins">The plugins.</param>
    public PluginHost(IPluginHostView view, IPlugin[] plugins)
    {
        _view = view;
        _plugins = plugins;
        _view.DataContext = this;
    }

    public IPluginHostView View
    {
        get { return _view; }
    }

    /// <summary>
    /// Gets the list of all the plugins available.
    /// </summary>
    /// <value>The plugins.</value>
    public IEnumerable<IPlugin> Plugins
    {
        get
        {
            return _plugins;
        }
    }
}

The PluginHostView

The purpose of the view is to show all the available plug-ins, each with his own specific UI. There is nothing magical about it apart from the fact that the item control uses item template which in turn uses plugin-provided UI as a content for the expander. I settled for the expander as I wanted to allow the user to collapse or expand them as and when required.

<Grid>
    <ItemsControl ItemsSource="{Binding Plugins}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Expander Margin="5,5,5,0"
                          Header="{Binding Name}"
                          Content="{Binding View}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

 

Implementing a plug-in

Now given all the groundwork implementing a plug-in is a doodle. The code is fundamentally no different to any other view/view-model combo as the sample below illustrates:

public class NewsPlugin : IPlugin
{
    private readonly INewsPluginView _view;

    public NewsPlugin(INewsPluginView view)
    {
        _view = view;
        _view.DataContext = this;
    }

    /// <summary>
    /// Gets the name of the plugin.
    /// </summary>
    /// <value>The name.</value>
    public string Name
    {
        get { return "Latest News"; }
    }

    /// <summary>
    /// Gets the UI representation of the plugin.
    /// </summary>
    /// <value>The view.</value>
    public object View
    {
        get { return _view;  }
    }

    /// <summary>
    /// Gets the news.
    /// </summary>
    /// <value>The news.</value>
    public IEnumerable<string> News
    {
        get
        {
            return new[]
                       {
                           "Frogs Invade Pratt's Bottom...",
                           "Developer's bonuses set to triple in 2012",
                           "Read more..."
                       };
        }
    }
}

 

Wrapping it all up

The attached sample application illustrates how all the parts interact together. The full source code can be found here.

image

Tags: ,
Categories: C# | CAL | WPF | PRISM

WPF DomainUpDown control

by Marcin Kaluza 30. September 2010 22:21

There is an MSDN page which made me chuckle recently when I was looking for a WPF equivalent of winform’s DomainUpDown control: according to the page the equivalent control in WPF is a TextBlock and two repeater buttons :) Cobbling together controls and code every time a need arises is not very appealing, so I decided to give it a go and create a WPF equivalent. The idea behind the DomainUpDown control is to allow the user to pick a value from a predefined list in a fashion similar to that of a ComboBox. The difference however is the fact that DomainUpDown does not require as much screen estate as the combo.  As you can imagine the control requires two dependency properties: one containing currently selected value and the other containing the list of “options”; when you click up or down either the next or the previous value from the list is selected. The following XAML illustrates how to use the control:

   1:         <Controls:DomainUpDown 
   2:                                 Value="{Binding CurrentValue}"
   3:                                 Items="{Binding Strings}" />

 

The sample application (screenshot below) illustrates various use cases of the control, including setting the value which is not present in the list of predefined “options” as well as behaviour of control when the list of has not been set.

image 

The code for the control and the sample app is available as part of the SharpFellows.Toolkit so I won’t bother you with the details here, but the majority of it is related to synchronisation of current value and its index in the list of options. This is important  when you set the value through binding (or code behind) and click either up or down buttons. One would expect that the correct next/previous value from the list is selected but this is where the original fails miserably and I found this behaviour rather annoying. The control also supports selecting the value using up/down keys and for those really inquisitive the full code is listed below.

using System;
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace SharpFellows.Toolkit.Controls
{
    /// <summary>
    /// WPF DomainUpDown control
    /// </summary>
    [TemplatePart(Name = &quot;PART_UpButton&quot;, Type = typeof(RepeatButton))]
    [TemplatePart(Name = &quot;PART_DownButton&quot;, Type = typeof(RepeatButton))]
    [TemplatePart(Name = &quot;PART_TextBox&quot;, Type = typeof(TextBox))]
    public class DomainUpDown : Control
    {
        #region Fields
        private int _selectedIndex;
        private RepeatButton _upBuppton;
        private RepeatButton _downButton;
        #endregion

        #region Dependency properties
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(&quot;Value&quot;, typeof (object), typeof (DomainUpDown), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));

        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.Register(&quot;Items&quot;, typeof(IEnumerable), typeof(DomainUpDown), new PropertyMetadata(OnItemsChanged));
        #endregion

        /// <summary>
        /// Initializes the <see cref="DomainUpDown" /> class.
        /// </summary>
        static DomainUpDown()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DomainUpDown), new FrameworkPropertyMetadata(typeof(DomainUpDown)));
            BorderBrushProperty.OverrideMetadata(typeof(DomainUpDown), new FrameworkPropertyMetadata(SystemColors.ControlLightBrush));
        }

        /// <summary>
        /// Gets or sets the selected value.
        /// </summary>
        /// <value>The value.</value>
        public object Value
        {
            get { return GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Gets or sets the items.
        /// </summary>
        /// <value>The items.</value>
        public IEnumerable Items
        {
            get { return GetValue(ItemsProperty) as IEnumerable; }
            set { SetValue(ItemsProperty, value);}
        }

        /// <summary>
        /// Gets or sets the index of the selected value.
        /// </summary>
        /// <value>The index of the selected.</value>
        protected int SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                if (_selectedIndex == value)
                    return;

                _selectedIndex = value;

                Value = Items.Cast<object>().Skip(SelectedIndex).First();
            }
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            if (e.Key == Key.Down)
            {
                if (_downButton != null)
                    _downButton.Focus();

                OnDown(this, null);
                e.Handled = true;
            }

            if (e.Key == Key.Up)
            {
                if (_upBuppton != null)
                    _upBuppton.Focus();

                OnUp(this, null);
                e.Handled = true;
            }
        }

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes call 
        /// <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (_upBuppton != null)
                _upBuppton.Click -= OnUp;

            if (_downButton != null)
                _downButton.Click -= OnDown;

            // Get the parts and attach event handlers to them
            _upBuppton = GetTemplateChild("PART_UpButton") as RepeatButton;
            _downButton = GetTemplateChild("PART_DownButton") as RepeatButton;

            if (_upBuppton != null)
                _upBuppton.Click += OnUp;

            if (_downButton != null)
                _downButton.Click += OnDown;
        }

        /// <summary>
        /// Invoked whenever an unhandled <see cref="E:System.Windows.UIElement.GotFocus"/> event reaches this element in its route.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Windows.RoutedEventArgs"/> that contains the event data.</param>
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);

            // Move focus immediately to the buttons
            if (_upBuppton != null)
                _upBuppton.Focus();
        }

        private void OnUp(object sender, RoutedEventArgs routedEventArgs)
        {
            if (SelectedIndex > 0)
                SelectedIndex--;
        }

        private void OnDown(object sender, RoutedEventArgs routedEventArgs)
        {
            if (Items != null && SelectedIndex < Items.Cast<object>().Count() - 1)
                SelectedIndex++;
        }

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var updown = d as DomainUpDown;
            SynchroniseValueWithIndex(updown, e.NewValue);
        }


        private static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var updown = d as DomainUpDown;
            SynchroniseValueWithIndex(updown, updown == null? null : updown.Value);
        }

        private static void SynchroniseValueWithIndex(DomainUpDown updown, object newValue)
        {
            if (updown == null || updown.Items == null)
                return;

            int i = 0;

            foreach (var element in updown.Items)
            {
                if (element.Equals(newValue))
                {
                    updown.SelectedIndex = i;
                    break;
                }

                i++;
            }
        }    
    }
}
Tags:
Categories: WPF

WPF Spinner – take two

by Marcin Kaluza 20. September 2010 20:48

The WPF wait indicator which I have published a while ago proved to be good as a starting point but somewhat limited when it came to using it in production: the major problem was the fact that changing its appearance required pretty much  total rework. As soon as I put it to use, new requirements arrived and it  was clear that we need a slightly different approach. First of all there was the issue of size, as sometimes you may need a smaller wait indicator, secondly the content was not very flexible and if it was to be changed, the key frame animation driving the whole thing needed to be reworked from scratch. So after a bit of head scratching I opted for a “lookless” approach and decided to implement a custom WPF ContentControl which would provide scaling and animation capabilities. To cut the long story short you can see the final effects below:

image

The control is dead easy to use as the following piece of XAML illustrates.

   1:         <Spinner:ContentSpinner Margin="10"
   2:                                  BorderBrush="Black"
   3:                                  BorderThickness="2" />

 

To make our life easier, the ContentSpinner provides a couple of dependency properties: first there is the Scale property which allows the user to scale the content, secondly NumberOfFrames property controls how many frames will be included in the animation and thirdly RevolutionsPerSecond drives a number of rotations the content will make each second. Also if you are not happy with the default content feel free to provide your own as in the XAML below:

   1:          <Spinner:ContentSpinner Margin="10"
   2:                                  BorderBrush="Black"
   3:                                  BorderThickness="2"
   4:                                  NumberOfFrames="8"
   5:                                  Content="{StaticResource blueDotsCanvas}">
   6:   
   7:          </Spinner:ContentSpinner>

 

The following code fragment illustrates the most important aspects of the control. The basic idea is that the control applies RotateTransform to its content and the transform is then animated using DoubleAnimationUsingKeyFrames. The number of frames is driven by the NumberOfFrames property and duration of the animation is calculated based on the number of required RevolutionsPerSecond.

   1:  private void StartAnimation()
   2:  {
   3:      if (_content == null)
   4:          return;
   5:   
   6:      var animation = GetAnimation();
   7:   
   8:      _content.LayoutTransform = GetContentLayoutTransform();
   9:      _content.RenderTransform = GetContentRenderTransform();
  10:   
  11:      _storyboard = new Storyboard();
  12:      _storyboard.Children.Add(animation);
  13:   
  14:      _storyboard.Begin(this);
  15:  }
  16:   
  17:  private void StopAnimation()
  18:  {
  19:      if (_storyboard != null)
  20:      {
  21:          _storyboard.Remove(this);
  22:          _storyboard = null;
  23:      }
  24:  }
  25:   
  26:  private void RestartAnimation()
  27:  {
  28:      StopAnimation();
  29:      StartAnimation();
  30:  }
  31:   
  32:  private Transform GetContentLayoutTransform()
  33:  {
  34:      return new ScaleTransform(ContentScale, ContentScale);
  35:  }
  36:   
  37:  private Transform GetContentRenderTransform()
  38:  {
  39:      var rotateTransform = new RotateTransform(0, _content.ActualWidth / 2 * ContentScale, _content.ActualHeight / 2 * ContentScale);
  40:      RegisterName(ANIMATION, rotateTransform);
  41:   
  42:      return rotateTransform;
  43:  }
  44:   
  45:  private DoubleAnimationUsingKeyFrames GetAnimation()
  46:  {
  47:      NameScope.SetNameScope(this, new NameScope());
  48:   
  49:      var animation = new DoubleAnimationUsingKeyFrames();
  50:   
  51:      for (int i = 0; i < NumberOfFrames; i++)
  52:      {
  53:          var angle = i * 360.0 / NumberOfFrames;
  54:          var time = KeyTime.FromPercent(((double)i) / NumberOfFrames);
  55:          DoubleKeyFrame frame = new DiscreteDoubleKeyFrame(angle, time);
  56:          animation.KeyFrames.Add(frame);
  57:      }
  58:   
  59:      animation.Duration = TimeSpan.FromSeconds(1 / RevolutionsPerSecond);
  60:      animation.RepeatBehavior = RepeatBehavior.Forever;
  61:   
  62:      Storyboard.SetTargetName(animation, ANIMATION);
  63:      Storyboard.SetTargetProperty(animation, new PropertyPath(RotateTransform.AngleProperty));
  64:   
  65:      return animation;
  66:  }

There are a couple of things which I need to mention though when it comes to the custom content: if you want it to be a part of resource dictionary, you may need to set the x:Shared property to False as otherwise it will be impossible to display two spinners simultaneously. Secondly the content should be symmetrical and its horizontal and vertical alignment needs to be set to Center as otherwise the spinner may get a bit wonky. Sample code and the control are available as part of the SharpFellows.Toolkit.

Tags:
Categories: WPF

The Mythical “Private Office”

by Marcin Kaluza 9. September 2010 19:55

Several years ago I read an interesting book dealing with, among other things, developers productivity: based on research done at  IBM’s Santa Teresa facility, the author suggested that the productivity of developers can be substantially higher than industry average provided that they have private offices. Apparently, if you allow developers to work in an environment which does not provide any distractions, they are able to crank more code. Fair enough. I have to admit here that I read the text several years ago, so I might have gotten some names wrong here, but the idea stuck with me (and some other people as well) for a long time: the private office was the holy grail of software development.

I see and hear similar ideas surfacing from time to time and what amazes me nowadays is the fact that people still believe that number of lines of code (commonly used measure of developers productivity) is indeed a valid metric. If so, how come we’re all not paid by the number of lines we produce each month?

People pay us to develop software, hoping that they will get more out of it when it is up and running. When evaluating software projects they are interested in the value the product can provide to their business, not in the lines of code cranked in order to produce it. And this leads to an interesting conclusion: the amount of code anyone writes per month is completely irrelevant, it’s the value they provide that matters.

In contrast to “private office” camp, one of the fundamental aspects of Agile development is co-location of team members: the team should occupy the same office space. I have to admit that initially I was a bit skeptical, wondering why are the prophets of Agile so insistent on entire teams (not only devs) working so close together. But if you were lucky enough to work on a properly run agile project, you must have witnessed first hand the continuous buzz of conversation between team members: people will bounce ideas of each other, testers will harass developers and vice versa, pairs will discuss or argue approaches to solving problems etc. Some will say that this is not the most productive environment, and surely it’s not, but only if  measure productivity by the amount of code cranked per man-month.

The “conversation-bus” allows team members to publish/subscribe on demand so they can “plug” themselves into any conversation at any time in order to either add something of value or get something out of it. This has two major effects: first of all it promotes knowledge sharing and secondly (and more importantly) it allows pretty much anyone to contribute and add “value”. If you ask me, this is far more important than time lost writing code as out of those tiny conversation a better solution will invariably emerge. So keep the conversation-bus up and running and for those rare moments when you want to be all on your own get yourself a pair of headphones.

On the other hand if you ever hear someone on your team saying  “oh no, this is not the way you do it, why didn’t you ask?”, or people generally complaining about “communication”, read between the lines: they are not complaining about communication, they are complaining that they were not involved in the decision making process and no amount of meetings, emails, approvals, committees, steering groups or similar “remedies” is going to change it. Deprive people of ability to contribute, lock them in cells (or private offices) and the net effect is sinking team morale as there is nothing more frustrating than seeing your teammates trying to reinvent the wheel and invariably ending up with a square. Trust me, I have seen it way too many times, very often through the window of my virtual “private office”.

Tags:
Categories: Agile