Operating System Markup Extension

•October 8, 2011 • Comments Off on Operating System Markup Extension

This is a small and simple mark-up extension that displays current date and time in your XAML. The code can be found here, and the XAML here. Below is an example of the Now mark-up extension in use.

 

That’s all Folks…

Advertisements

Construct, Initialise, Terminate and Destruct

•October 8, 2011 • Comments Off on Construct, Initialise, Terminate and Destruct

Some patterns never die; they just seem to loose favour and inhabit the darker corners of software engineering practice. One such pattern is the “Construct, Initialise, Terminate and Destruct” pattern. Those of you who have been working in C++ or Real-time Systems will realise the importance of this pattern when constructor protection and two-phase termination are required.

Constructor protection is the requirement that the constructor does not throw any exceptions or die during the construction process. Here only the absolute minimum is undertaken to bring the object in a valid state. Construction of further objects and connections to external systems (to name a few) are then delegated to the Initialise procedure. The Initialise procedure will then take care of the more difficult and possibly error prone construction. At the other end of the object’s lifetime the Terminate process terminates all services etc. that were started in the Initialise method but in the reverse order.

Recently I have observed projects bringing together new technologies and uniting them with current patterns and frameworks. One such example is the MVVM pattern with the ever more popular Unity IOC. Using these two together seems like a dream come true when it comes to injecting all those dependencies through the constructor. This raises the point of “fat” constructors where all external class that provide services through the Separation of Concerns principle. Here great care must be taken to assure that all these helper classes are also built on the same CITD pattern otherwise the probability that constructors construct other objects that in turn start service and access databases starts climbing such that the application becomes fragile and unmanageable. Care must be taken to inject only those services that are really needed as the process can be difficult to debug if anything goes wrong.

The Initialisation step can be difficult to implement separately from the constructor in many cases. However, in the MVVM for both Silverlight and WPF the simplest solution is to attach a Command to the Load and Unload event of the View. When these Command are fired the execute Action can perform all the required initialisation and termination processing required.

As I mentioned previously there can be a controlled start-up and shutdown process involved in the Initialisation and Termination. The basic rules are:

  1. During initialisation take care of yourself first before taking care of your children. That means isolate and initialise all the services that lower processing requires before calling the lower processing
  2. Call the lower processing that is required by the current level.
  3. Finish off all other initialisation processing.

For the termination process it is similar but in reverse.

Care must be taken over the two phase initialisation and termination requirements. For example the termination process, the first phase, of the View Model will entail making sure that the Model has successfully terminated before the View Model can terminate. The Model then informs the View Model via an event that the Model has terminated and hence the second phase, that of View Model termination can be completed. Initialisation follows a similar pattern and, of course, depending on your MVVM flavour, the Initialisation and Termination process might entail more Models and/or Business Logic delegates.

So enough with the theory, what does the code look like? The example I will use is that of a WPF MVVM light application where a general MVVM will be considered.

The View

The View Model is referenced via the DataContext using the following mark-up in the XAML:

 DataContext="{Binding PostboxMessagesViewModel, Mode=OneWay, Source={StaticResource Locator}}"

The following XAML mark-up is required to link the Load and Unload event to Commands within the View Model:

    <Custom:Interaction.Triggers>
        <Custom:EventTrigger EventName="Loaded" SourceName="Page">
            <GalaSoft_MvvmLight_Command:EventToCommand x:Name="etcPageLoad" Command="{Binding LoadCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
        </Custom:EventTrigger>
        <Custom:EventTrigger EventName="Unloaded" SourceName="Page">
            <GalaSoft_MvvmLight_Command:EventToCommand x:Name="etcPageUnload" Command="{Binding UnloadCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
        </Custom:EventTrigger>
    </Custom:Interaction.Triggers>
    

 

The Model

Here the Initialise and Terminate methods are defined as well as the “Execute” handlers. The Model inherits from the interface ICtorInitTermDtor as it does not need to support a Command implementation. The following code shows the ICtorInitTermDtor interface:

    using System;

    public interface ICtorInitTermDtor
    {
        #region Event handlers

        /// <summary>
        /// Occurs when initialisation processing has completed.
        /// </summary>
        event EventHandler InitialiseCompletedEvent;

        /// <summary>
        /// Occurs when terminate processing has completed.
        /// </summary>
        event EventHandler TerminateCompletedEvent;
        
        #endregion

        #region Methods

        /// <summary>
        /// Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        void Initialise();

        /// <summary>
        /// Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        void Terminate();

        #endregion
    }

The code in the Model is basically as follows:

    public class PostboxModel : BaseModel, IPostboxModel
    {

        #region Implementation of ICtorInitTermDtor

        /// <summary>
        ///   Gets or sets the load command. This command provides the connection between the View's
        ///   Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        public RelayCommand LoadCommand { get; set; }

        /// <summary>
        ///   Gets or sets the unload command. This command provides the connection between the View's
        ///   Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        public RelayCommand UnloadCommand { get; set; }

        /// <summary>
        ///   Gets or sets a value indicating whether this <see cref = "SearchPageModel" /> is terminating.
        /// </summary>
        /// <value><c>true</c> if terminating; otherwise, <c>false</c>.</value>
        protected bool Terminating { get; set; }

        /// <summary>
        ///   Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        public void Initialise()
        {
            this.LoadData();
        }

        /// <summary>
        ///   Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        public void Terminate()
        {
            // stop all current requests to host
            this.Terminating = true;
            this.TerminateDataAccess();

            // remove all data containers
            this.UnloadData();
        }

        /// <summary>
        ///   Loads the data.
        /// </summary>
        private void LoadData()
        {
            if (this.UseLocalData)
            {
                this.LoadLocalData();
            }
            else
            {
                this.LoadLiveData();
            }
        }

        /// <summary>
        ///   Locals the live data.
        /// </summary>
        private void LoadLiveData()
        {
        }

        /// <summary>
        ///   Loads the local data.
        /// </summary>
        private void LoadLocalData()
        {
        }

        private void TerminateDataAccess()
        {
            if (!this.UseLocalData)
            {
            }
        }

        /// <summary>
        ///   Unloads the data.
        /// </summary>
        private void UnloadData()
        {
            if (this.UseLocalData)
            {
                this.Items.Clear();
            }
            else
            {
                this.LoadLiveData();
            }
        }

        #endregion
    }

The View Model

Here the Commands are defined as well as the “Execute” handlers. The View Model constructor has the Model injected as a parameter so:

    public interface IPostboxMessagesViewModel : IBaseViewModel
    {
    }

Additionally, the View Model implements the ICtorInitTermDtorCmd interface (which may also be implemented within a View Model base class)

    using GalaSoft.MvvmLight.Command;

    public interface ICtorInitTermDtorCmd : ICtorInitTermDtor
    {
        #region Commands

        /// <summary>
        /// Gets or sets the load command. This command provides the connection between the View's
        /// Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        RelayCommand LoadCommand { get; set; }

        /// <summary>
        /// Gets or sets the unload command. This command provides the connection between the View's
        /// Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        RelayCommand UnloadCommand { get; set; }

        #endregion

    using System;
    using System.Collections.ObjectModel;
    using System.Runtime.Serialization;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
    using Interfaces;
    using Utilities;

    public class BaseViewModel
    {
        private readonly IBaseModel model;

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseViewModel"/> class.
        /// </summary>
        /// <param name="pModel">The p model.</param>
        protected BaseViewModel(IBaseModel pModel)
        {
            this.model = pModel;
        }

        /// <summary>
        /// Called when the initialisation has been completed.
        /// </summary>
        protected void OnInitialiseCompleted()
        {
            var loc = InitialiseCompletedEvent;
            if (loc != null)
            {
                loc(this, new EventArgs());
            }
        }

        /// <summary>
        /// Called when the initialisation has been completed.
        /// </summary>
        protected void OnTerminationCompleted()
        {
            var loc = TerminateCompletedEvent;
            if (loc != null)
            {
                loc(this, new EventArgs());
            }
        }

        /// <summary>
        /// Occurs when initialisation processing has completed.
        /// </summary>
        public event EventHandler InitialiseCompletedEvent;

        /// <summary>
        /// Occurs when terminate processing has completed.
        /// </summary>
        public event EventHandler TerminateCompletedEvent;

        /// <summary>
        /// Gets or sets the load command. This command provides the connection between the View's
        /// Load event and the View Model
        /// </summary>
        /// <value>The load command.</value>
        public RelayCommand LoadCommand { get; set; }

        /// <summary>
        /// Gets or sets the unload command. This command provides the connection between the View's
        /// Unload event and the View Model
        /// </summary>
        /// <value>The unload command.</value>
        public RelayCommand UnloadCommand { get; set; }

    }

The actual ViewModel with the specific parts removed is as follows:

    using System;
    using System.ComponentModel;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
    using Interfaces.Postbox;

    public class PostboxMessagesViewModel : BaseViewModel, IPostboxMessagesViewModel
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PostboxMessagesViewModel"/> class.
        /// </summary>
        public PostboxMessagesViewModel()
            : base(null)
        {
            this.RegisterCommands();
        }

        /// <summary>
        /// Registers the commands.
        /// </summary>
        private void RegisterCommands()
        {
            this.LoadCommand = new RelayCommand(this.Load);
            this.UnloadCommand = new RelayCommand(this.Unload);
        }

        /// <summary>
        /// Unloads this instance.
        /// </summary>
        private void Unload()
        {
        }

        /// <summary>
        /// Loads this instance.
        /// </summary>
        private void Load()
        {
        }

        #region Implementation of INotifyPropertyChanging

        /// <summary>
        /// Occurs when [property changing].
        /// </summary>
        public event PropertyChangingEventHandler PropertyChanging;

        #endregion

        #region Implementation of ICtorInitTermDtor

        /// <summary>
        /// Initialises this instance. Called within the LoadCommand's "Execute" handler.
        /// </summary>
        public void Initialise()
        {
        }

        /// <summary>
        /// Terminates this instance. Called within the UnloadCommand's "Execute" handler.
        /// </summary>
        public void Terminate()
        {
        }

        #endregion

        #region Implementation of ICtorInitTermDtorCmd

        #endregion

        #region Implementation of IBaseViewModel

        /// <summary>
        /// Gets or sets the change view command.
        /// </summary>
        /// <value>The change view command.</value>
        public RelayCommand ChangeViewCommand { get; set; }

        #endregion

    }

That’s all folks…

INotifyPropertyChange

•October 8, 2011 • Comments Off on INotifyPropertyChange

By now almost all of you that have been confronted with the MVVM pattern have also seen the importance of the INotifyPropertyChange interface for the View Model. However, regardless of where it gets used one cannot escape it components of the name “Interface Notify Property Changed”. Let’s have a closer look at an example to see where all this is leading:

        /// <summary>
        /// The <see cref="MyProperty" /> property's name.
        /// </summary>
        public const string MyPropertyPropertyName = "MyProperty";

        private bool m_myProperty = false;

        /// <summary>
        /// Gets or sets a value indicating whether my property has been set.
        /// </summary>
        /// <value><c>true</c> if has been set; otherwise, <c>false</c>.</value>
        public bool MyProperty
        {
            get
            {
                return m_myProperty;
            }

            set
            {
                if (m_myProperty == value)
                {
                    return;
                }

                m_myProperty = value;
                RaisePropertyChanged(MyPropertyPropertyName);
            }
        }

As we all know we have to implement the event and have some way to execute it. Hence the “NotifyPropertyChange” method appears in the code. Regardless whether this method removes the need for the “magic string” or not it should get executed only when the property changes, i.e. the setter is called as seen above.

So, what is the problem? Everything is clear. We know all this.

Okay.

So, how many of you have seen this type of code, or maybe have written something similar?

        /// <summary>
        /// The <see cref="PropertyWithSideEffect" /> property's name.
        /// </summary>
        public const string PropertyWithSideEffectPropertyName = "PropertyWithSideEffect";

        private bool _myPropertyWithSideEffect = false;

        /// <summary>
        /// Gets or sets a value indicating whether property with side effect has changed.
        /// </summary>
        /// <value>
        ///     <c>true</c> if the property with side effect has changed; otherwise, <c>false</c>.
        /// </value>
        public bool PropertyWithSideEffect
        {
            get
            {
                return _myPropertyWithSideEffect;
            }

            set
            {
                if (_myPropertyWithSideEffect == value)
                {
                    return;
                }

                _myPropertyWithSideEffect = value;
                DoSomeRatherLongProcessing();
                GetSomethingFromTheDatebase();
                RaisePropertyChanged(PropertyWithSideEffectPropertyName);
            }
        }

Now maybe someone out there can tell me why I need to call methods from within a setter? I see none. However the property now has a really bad side effect associated with it.

What about when I cannot be bothered to set another non-associated property? Now what property/properties has/have changed? I see only one but two have been notified.

        /// <summary>
        /// The <see cref="PropertyWithBadRaisePropertyChanged" /> property's name.
        /// </summary>
        public const string PropertyWithBadRaisePropertyChangedPropertyName = "PropertyWithBadRaisePropertyChanged";

        private bool _myProperty = false;

        /// <summary>
        /// Gets or sets a value indicating whether the property with bad raise property changed has changed.
        /// </summary>
        /// <value>
        ///     <c>true</c> if property with bad raise property changed has changed; otherwise, <c>false</c>.
        /// </value>
        public bool PropertyWithBadRaisePropertyChanged
        {
            get
            {
                return _myProperty;
            }

            set
            {
                if (_myProperty == value)
                {
                    return;
                }

                _myProperty = value;
                RaisePropertyChanged(PropertyWithBadRaisePropertyChangedPropertyName);
                RaisePropertyChanged(MyPropertyPropertyName);
            }
        }

This misuse is almost unforgivable as it tells the system to rebind to a property that has not changed. Even more unforgivable is the misuse inside the property itself where not only the property change event is raise for the property but also another one appears raising the property change event for another property. This is a very unwanted side-effect for the GUI designer/developer.

If you want that the raise the event that notifies everyone that your property has changed then you can just change the property itself. If the property does not need to be changed then do not raise the property changed event.

So either the developer does not know correct usage or is too lazy to code properly – whichever way you look at it the code is ugly and incorrect. Since the property changed is an event there is nothing stopping the developer listening and handling this event as well. Of course, there is the overhead of maintaining those “magic string” or LINQ expressions. I know this does not get rid of the urge to raise property changed events, but it does reduce the raising non-related property change events within the property setter.

So the next time you feel the need to raise the property change event in another place in the code just set the property correctly though it setter.

That’s all folks…

MVVM – More Lessons Learned from the Trenches

•July 27, 2011 • Comments Off on MVVM – More Lessons Learned from the Trenches

Separation of Concerns

You would think that this was the goal of MVVM. It certainly was one of the goals of the MVC pattern on which MVVM is based. So why do we find it so hard to accomplish? A certain amount of this confusion lies in the early descriptions of the MVVM pattern. Once these little mistakes have entered the collective subconscious of the .Net developer community it is very hard to eradicate them. So, please take your time to define your Model as being a MODEL and not just a single DTO or collection of them. We all know what the View is there for as well as the View Model. Think about the requirements of a Model; its overall status with respect to validation, being dirty and when the Model should inform the View Model of its state change to name just a few important points.

Model Status Change

As mentioned above, one of the primary concerns of the Model is to inform the View Model of its state and any changes to it. Consider that we are loading a Model with data from a number of background services or feeds. It is common to use the Busy Indicator control for the purpose of disabling the View until the data has been prepared in the Model. As we start the possibly long process of loading data we have our Busy Indicator set and the user cannot enter any data into the application (I hope that this can be cancelled or navigated away from, but this is another topic…). Hence we set the Busy Indicator’s IsBusy property to true and off we go to get our data. After a short while our data has been collected and prepared in the Model and we need to inform the user that all is well and that they may enter data into the application. Easy – all we have to do is to set the Busy Indicator’s IsBusy to false. That can be done as the last step in our collection process just after we have received the last block of data. Then we need only pass the completed event of our last data access on to the ViewModel and handle the IsBusy right there in the View Model. WRONG.

Consider a refactoring of the data collection process or even an enhancement in later versions. Now it is time to go hunting for the “correct” moment to set that IsBusy property to false. Beginning to get the picture? The resetting of the IsBusy property is the responsibility of the Model. Hence the model must inform the View Model that data collection has been completed and thereafter raise an event to the View Model indicating this state change. Internally the Model should track the data collection process and NOT misinform the View Model using asynchronous data completion events.

So what is needed? If your MVVM is using the BackgroundWorker a manager needs to be designed which collects all the BGW and registers with each Completed event. Once all the BGW have completed the manager will issue its own event that can be caught by the Model and transformed or just forwarded to the View Model. For those using the async calls directly a little more work needs to be done. However, there are a number of examples out there in cyberspace. Once again they need to raise an event upon completion of all async call which will then be forward to Model and on to View Model.

View Status Change

It always seems difficult to control the visibility and whether a control is enabled or not given a certain state within user’s work process. There seems to be a number of options available from “control” properties, attached properties to using the CanExecute of a control associated Command. Using a Command’s CanExecute for driving the user interface seems at first a simple thing to do. However the difficulty arises when the control does not support a Command property. This leads to a mixing of the three options and hence a confusing implementation within the View Model. Personally, I dislike the CanExecute option and tend to use Commands only for one-way flow from View to View Model. I prepare “control” properties for use when driving the View’s controls and attach these properties to either the control’s enabled, opacity or visibility properties as need be. Hence I have a single method used consistently throughout the UI.

Once this process has been achieved the fun can begin – how can I efficiently control each and every one of these properties without pulling what little hair I have out. Here I would employ a method similar to that described above for the Model status change. Although I have yet to find a framework that is simple and efficient to use I am convinced that the best way currently is to use some form of state machine. Is there anyone out there with another and/or better suggestion?

Validation where?

Basically validation takes place throughout the MVVM. Yes, it can be simplified such that the same validation can be used in the Model as well as in the View Model but this is a very limiting case. Plan ahead and make sure the View that there are enough attached properties or Blend behaviours that implement numeric input, Regex input, length constraints, drag and drop and cut and paste such that even the most avid tester cannot get data into the application that you do not want especially through the “front door”. I have seen enough applications that logically transform the Model data representation into more fields on the form than data actually saved within the Model. Hence there is the need to provide validation on the View Model. Note: this is also apparent in the newer MVVM diagrams from Microsoft where the IErrotInfo interface is implemented both in the View Mode as well as in the Model. Finally, the Model should always be valid and consequently the need for validation at this level – which is usually the first (and sometimes the last) validation to be implement in the MVVM. Conclusion: perform validation at all levels within the MVVM.

Side Effects in Property Setters

This is really not a difficulty with MVVM but a more general problem that manifests itself in UI controls that expose a SelectedItem property (Combo box, List, Data Grid, etc.). The tendency is to perform a two-way data bind to the SelectedItem property in the View Model. When the UI property changes the View Model see this change in the setter of the SelectedItem in the View Model and starts some processing task. This leads to a rather ugly side effect. Many MVVM frameworks recognise this limitation and provide an EventToCommand attached property or Blend behaviour. Hence a Command can be attached to the SelectedItemChanged event and this Command can be readily processed within the View Model. The side effect is eradicated and the SelectedItem can still be accessed for its data but, importantly, it stays a property. Moreover, the use of a Command between the View and the View Model is MVVM conformant. Conclusion: use a Command with the EventToCommand facility to remove side effects in View Model property setters.

I am sure that a number of other items will come up in the future on this topic. But until then, that’s all folks…

 

 

Lorem Ipsum Attached Property

•May 11, 2011 • Comments Off on Lorem Ipsum Attached Property

I do not know about everyone out there but sometimes I cannot wait to have the back-end available before I start with the UI and UX. For data grids and the like Expression Blend supports automatic generation of dummy data. However, this type of automatic support seems to stop there and filling random text into text boxes, text blocks, and rich text blocks can be tedious and not very thrilling. Well, after doing this in a number of projects over time I got to thinking how I could simplify this process when building a prototype and remembered that some projects had resorted to Lorem Ipsum text fills to flesh-out the design. Therefore, I started to think how I could automate text generation using Lorem Ipsum in some way. Well this is how it all turned out…

Lorem Ipsum

Lorem Ipsum has been around for a very long time predating the modern DTP systems and many electronic typesetting applications. Lorem Ipsum dates back to the 1500s when an unknown printer took a galley of type and scrambled it to make a type specimen book. Contrary to popular belief, Lorem Ipsum is not simply random text and has roots in a piece of classical Latin literature dating back over 2000 years. The reason why it is being used today is a long established fact that a designer will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters making it look like readable text.

Design

The challenge here is to produce readable text using a dictionary of Latin words. Additionally, there is also a need to define the number of paragraphs, sentences per paragraph, the number of words and/or the average number of words in a sentence. Another feature would be to have the possibility to have the text as bullet points or some kind of list. It would be easy to produce fixed sentences with a given number of words but written text is almost never so structured.

With this information the properties required for the Attached Property can now be defined:

  1. Number of paragraphs in the generated text.
  2. The total number of words in the generated text
  3. The number of words in a sentence. An automatic deviation is also possible here as well.
  4. The approximate/average number of sentences in a paragraph. An automatic deviation is also possible although not explored here.

The code can be found here, and the XAML here. Below is an example of the Lorum Ipsum attached property in use.

The Current User Mark-up Extension

•May 9, 2011 • Comments Off on The Current User Mark-up Extension

This is a small and simple mark-up extension that displays the name of current user in your XAML. The code can be found here, and the XAML here. Below is an example of the Current User mark-up extension in use.

That’s all Folks…

Digital Peak Level Meter

•May 6, 2011 • Comments Off on Digital Peak Level Meter

I am currently embarking on a new project where I will be emulating hardware Input/Output device and sensors. To this end there is need of some standard hardware controls that require to be emulated. Luckily a number of these controls are easily derived from standard controls already available within both WPF and Silverlight. The post looks at the first of these controls – a digital peak level meter Many of you used or at least seen these displays on today’s digital HIFI equipment – the more gaudy ones have lots of flashing lights that seems to go with today’s musical tastes.

The goal of the blog is to produce a stereo digital peak level meter similar to the one below.

The simplest starting point is to consider what is already available in Silverlight and WPF. Here the choice is quite simple – the progress bar will suffice once it has been restyled. Remember that we can consider the “progress” as the current level from our input source.

The following tools will be used in the preparation of control:

  1. Expression Blend
  2. Visual Studio 2010

The project is split into the following parts:

  1. Definition of the UX along with the look.
  2. The attached property that converts a value into a discrete “level” to be displayed.
  3. The dynamics of displaying discrete levels without resorting to C# wherever possible – always a personal challenge.
  4. The test application

What I will not be covering will include producing a production standard “look-less” control with a nice design time experience and I18N support although the finished private version will include such features. However, I have already covered all of these topics elsewhere in this blog.

The Experience

The basic control uses colour to indicate levels similar to the actual hardware device; green for the major part of the display i.e. everything in the normal range. Yellow is used for levels that will have an effect on the data recording process and red will be used for those levels that should cause concern. A final white level indicates some sort of “clipping” of the levels. The green range contains 32 levels, the yellow 22, the red 9 and the final white one – a total of 64. Of course there can be more or less of these levels as required. The colours are defined as Brushes as follows:

    <SolidColorBrush x:Key="GreenBlockBrush" Color="#FF17F317" />
    <SolidColorBrush x:Key="YellowBlockBrush" Color="#FFFBFC01" />
    <SolidColorBrush x:Key="RedBlockBrush" Color="#FFE80705" />
    <SolidColorBrush x:Key="WhiteBlockBrush" Color="White" />

Each level is defined as a Rectangle that is either visible or not. I have applied a minimal with for each level as well as minimum distance separating the levels. The standard widths are 4pixels for the level and 2pixels for the separator. These are defined as the column width detail and not on the rectangles themselves. The basic layout, showing the second level filled, in Blend is:

The Level Conversion Attached Property

The attached property catches changes on the Progress Bar’s Value property and converts them into the output level. As this a cumulative display the actual number of blocks needs to be calculated and the correct colouring applied. To properly achieve this we need to trigger a filling of the correct number of levels that reflect the input value

Once this property has been attached to the Progress Bar changes in value will result in the correct number of blocks being shown.

Display Dynamics

The real work gets done to display the levels. The property changed event of the Progress Bar triggers the displaying (filling here) of the rectangles. Pretty simple stuff but very effective. The down side to this approach is that each bar is filled depending on the value assigned to the Level property of the LevelHelper class. Hence for the 32nd bar to be displayed all the bars up until this point will also be displayed i.e. 32 fills are performed on this trigger leading to a huge amount of XAML. However, it seems to work without any appreciable flashing or delay.

The Test Application

To see the digital peak level meter in action I have a simple WFP application with the custom progress bar data bound to the value of a slider. As the slider moves the number of levels is displayed that is associated with the slider value. In a real world application data binding to a source of fast changing values can result in some strange side effects. Hence it recommended that the data source averages or a set of values before updating the custom progress bar.

The source can be found here, the XAML mark-up here, and the simple test application source here.

That’s all Folks…