All posts in C#

I think the title says it pretty well: we want the DatePicker control to not be stupid and allow tabbing to the calendar button so we can meet mouse-less compliance standards.

Q: Why didn’t Microsoft do this to begin with?
A: *shrugs* But, we’re going to fix it.

The code:


using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

/// <summary>
/// Extended DatePicker.
/// </summary>
public class ExtendedDatePicker : DatePicker
{
    #region Members

    private bool _tabInvoked;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="ExtendedDatePicker"/> class.
    /// </summary>
    public ExtendedDatePicker()
    {
        _tabInvoked = false;
        this.PreviewKeyDown += DatePickerPreviewKeyDown;
        this.CalendarClosed += DatePickerCalendarClosed;

        // Allow to be a tab target
        IsTabStop = true;
    }

    #endregion

    #region Events

    /// <summary>
    /// Dates the picker preview key down.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.
    /// </param>
    private void DatePickerPreviewKeyDown(object sender, KeyEventArgs e)
    {
        var datePicker = sender as DatePicker;
        if (datePicker == null || e.Key != Key.Tab) return;

        // Mark that "tab" was pressed
        _tabInvoked = true;

        // Reverse drop down opened state
        datePicker.IsDropDownOpen = !datePicker.IsDropDownOpen;
    }

    /// <summary>
    /// Dates the picker calendar closed.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.
    /// </param>
    private void DatePickerCalendarClosed(object sender, RoutedEventArgs e)
    {
        if (!_tabInvoked) return;

        // Reset marker
        _tabInvoked = false;

        // Go to next control in sequence
        var element = Keyboard.FocusedElement as UIElement;
        if (element == null) return;
        element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
    }

    #endregion
}

Here is what it does in a nutshell:

  • Makes this control a tab stop (it’s not by default)
  • Binds to the PreviewKeyDown event, which catches when Tab is pressed. It then sets a marker, telling the control that the calendar was opened via keyboard and sets IsDropDownOpen to true.
  • Binds to the CalendarClosed event, which (if Tab was invoked) resets the marker and sets the focus to the next control in the view.

It’s not overly complicated, but it solves an annoyance.

Until next time…

Welcome to Part 3 of our series Making a Better ObservableCollection.

If you missed Making a Better ObservableCollection Part 2 – Cross Threading you can get to it here.

We started by showing you a few neat extensions in Part 1 and then followed it up with cross-threading in Part 2. Now that we have an understanding of how to extend and offload the work to another thread, let’s cover another very important feature: Sorting.

We start by creating a custom comparer as learned here:


/// <summary>
/// Customer Sort Comparer.
/// Original Source:
/// http://blogs.msdn.com/b/jgoldb/archive/2008/08/28/improving-microsoft-datagrid-ctp-sorting-performance-part-2.aspx
/// </summary>
/// <typeparam name="T"></typeparam>
internal class CustomSortComparer<T> : ICustomSortComparer<T>
{
    #region Members

    /// <summary>
    /// A two argument delegate for comparing two objects.
    /// </summary>
    /// <param name="arg1">The arg1.</param>
    /// <param name="arg2">The arg2.</param>
    /// <returns></returns>
    protected delegate int TwoArgDelegate(T arg1, T arg2);

    /// <summary>
    /// A two argument delegate instance.
    /// </summary>
    private TwoArgDelegate _myCompare;

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the specified target collection.
    /// </summary>
    /// <param name="targetCollection">The target collection.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    public void Sort(ObservableCollection<T> targetCollection, string propertyName, 
        ListSortDirection direction)
    {
        // Sort comparer
        var sortComparer = new InternalSorting(propertyName, direction);

        // Sort
        var sortedCollection = targetCollection.OrderBy(x => x, sortComparer).ToList();

        // Synch
        targetCollection.SynchCollection(sortedCollection, true);
    }

    /// <summary>
    /// Performs custom sorting operation.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    internal void CustomSort(string propertyName, ListSortDirection direction)
    {
        int dir = (direction == ListSortDirection.Ascending) ? 1 : -1;

        // Set a delegate to be called by IComparer.Compare
        _myCompare = (a, b) => ReflectionCompareTo(a, b, propertyName) * dir;
    }

    /// <summary>
    /// Custom compareTo function to compare 2 objects derived using Reflection.
    /// If an aliasProperty is provided, the sort is performed on that property
    /// instead.
    /// This is ideal for columns with data types that need to be sorted by another
    /// data type.
    /// i.e. Images that need value associations, or strings with numeric entries.
    /// </summary>
    /// <param name="a">A.</param>
    /// <param name="b">The b.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <returns></returns>
    private static int ReflectionCompareTo(object a, object b, String propertyName)
    {
        // Get property value for "a"
        PropertyInfo aPropInfo = a.GetType().GetProperty(propertyName);
        var aValue = aPropInfo.GetValue(a, null);
        if (aValue == null) return 0;

        // Get property value for "b"
        PropertyInfo bPropInfo = b.GetType().GetProperty(propertyName);
        var bValue = bPropInfo.GetValue(b, null);
        if (bValue == null) return 0;

        // CompareTo method
        MethodInfo compareToMethod =
            aPropInfo.PropertyType.GetMethod("CompareTo", new[] { aPropInfo.PropertyType });
        if (compareToMethod == null) return 0;

        // Get result
        var compareResult = compareToMethod.Invoke(aValue, new[] { bValue });
        return Convert.ToInt32(compareResult);
    }

    #endregion

    #region ICompare

    /// <summary>
    /// Compares two objects and returns a value indicating whether one is less 
    /// than, equal to, or greater than the other.
    /// </summary>
    /// <param name="x">The first object to compare.</param>
    /// <param name="y">The second object to compare.</param>
    /// <returns>
    /// Value
    /// Condition
    /// Less than zero
    /// <paramref name="x" /> is less than <paramref name="y" />.
    /// Zero
    /// <paramref name="x" /> equals <paramref name="y" />.
    /// Greater than zero
    /// <paramref name="x" /> is greater than <paramref name="y" />.
    /// </returns>
    public int Compare(T x, T y)
    {
        return _myCompare(x, y);
    }

    #endregion

    #region InternalSorting

    /// <summary>
    /// Custom IComparer class to perform custom sorting.
    /// </summary>
    private class InternalSorting : CustomSortComparer<T>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="InternalSorting"/> class.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="direction">The direction.</param>
        public InternalSorting(string propertyName, ListSortDirection direction)
        {
            CustomSort(propertyName, direction);
        }
    }

    #endregion
}

Q: So, what is going on with this comparer?
A: Here is a summary:

  • Sort is called against an ObservableCollection, a property name, and a sort direction.
  • A sort comparer is derived by calling an internal class which invokes the CustomSort method.
  • The CustomSort method assesses the sort direction as an integer and then uses reflection to compare each set of values (ReflectionCompareTo).
  • A sorted collection is created against the target collection with the sort comparer applied.
  • The target collection is synched against the sorted collection.

Next, we augment our ObservableCollectionEx class:


/// <summary>
/// Extends the ObservableCollection object:
/// 1. Allows cross-thread updating to offload UI operations.
/// 2. Allows full sorting capabilities without affecting the UI thread.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableCollectionEx<T> : ObservableCollection<T>, IObservableCollectionEx<T>
{
    #region Members

    private readonly ICustomSortComparer<T> _sortComparer;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the 
    /// <see cref="ObservableCollectionEx{T}"/> class.
    /// </summary>
    public ObservableCollectionEx()
    {
        _sortComparer = new CustomSortComparer<T>();
    }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    /// <param name="collection">The collection.</param>
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the observable collection by the property and sort direction.
    /// </summary>
    /// <param name="propertyName">The property within the ObservableCollectionExtender object
    /// to sort by.</param>
    /// <param name="direction">The desired sort direction.</param>
    public void Sort(string propertyName, ListSortDirection direction)
    {
        if (!this.Any()) return;
        _sortComparer.Sort(this, propertyName, direction);
    }

    /// <summary>
    /// Sorts the specified expression.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="expression">The expression.</param>
    /// <param name="direction">The direction.</param>
    public void Sort<TProperty>(Expression<Func<T, TProperty>> expression, 
    ListSortDirection direction)
    {
        if (!this.Any()) return;
        Sort(expression.GetPropertyName(), direction);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
    /// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)  
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }
}

/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;

#endregion

What’s new is our 2 Sort methods. One uses a property name and the other an expression to strongly define the property. Both accomplish calling the sort comparer we just built earlier.

Q: So, why do all this? What was the point?
A: Custom sort comparers will give us the ability to increase the sorting performance of our ObservableCollections when bound to controls. It will also allow us to perform this work in an async thread instead of having to create a CollectionViewSource on the UI thread which will negatively impact the user experience during updates.

Next time, we will look at applying this to a DataGrid to greatly improve sort performance and reliability.

Welcome to Part 2 of our series Making a Better ObservableCollection. If you missed Making a Better ObservableCollection Part 1 – Extensions you can get to it here.

In this next section I am going to share a version of my ObservableCollectionEx that allows cross-threading. The idea here is to have an ObservableCollection which you can update from an Async thread so as not to impact the owning thread. This is especially useful in WPF when you don’t wish to block the UI thread while performing collection updates.

Let’s see the code:


/// <summary>
/// Initializes a new instance of the 
/// <see cref="ObservableCollectionEx{T}"/> class.
/// </summary> 
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    public ObservableCollectionEx()
    {
    }

    ///
    /// Initializes a new instance of the
    ///  class.
    ///
    ///The collection.
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
    /// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Overridden NotifyCollectionChangedEventHandler event.
    /// </summary>
    public override event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
}

The constructors are pretty straight forward. We want to have an empty constructor and one that allows an immediate “AddRange” of an IEnumerable just like List and ObservableCollection allow, but that is not really the point of this post.

The main feature here is a slightly reformatted version of a wonderful post from a blog called New Things I Learned which covers cross-thread access with an ObservableCollection.

So, what does this code do?

  1. First we use BlockReentracy to prevent changes to the collection while we are evaluating it.
  2. Next, we get the Invocation List from the CollectionChanged event handler. This is the list of delegates subscribing to the event.
  3. Evaluate each delegate by retrieving it’s Target and casting it to a DispatcherObject.
  4. Make sure the DispatcherObject is valid and that the current thread has access.
  5. Invoke the delegate in the DispatcherObject’s thread.

It’s not the simplest code to follow when you aren’t overly familiar with working with event delegates but it gets the job done quite well.

Next time, we will talk about using a custom SortComparer to improve DataGrid performance.

Until next time.

It’s actually kind of difficult to imagine writing anything in WPF without using at least one ObservableCollection or several instances of INotifyPropertyChanged (INPC). And it’s important to note that neither of these are perfect out of the box. I have spent a bit of time talking about ways to streamline your INPC implementations in the 3-part series “Creating a Model Base“, and plan to have another update to that soon. This topic will focus on the ObservableCollection.

Here are a few reasons why we use it:

  • When controls bind to them, Add, Move, and Remove actions automatically update in the UI without any need to directly update those controls.
  • It has a CollectionChanged event allowing us to leverage when items are added, moved, or removed.

Essentially, it let’s us know when our collection gets updated and allows us to leverage the UI without much effort.

Q: So, why extend it?
A: Because there are some extras all good collections should have to make our lives easier.

For this article, we’ll start simple with a few extensions, then move on to multi-threading, then sorting.

 
Apply

One extension I have always wanted to see in any collection is the ability to commit changes to a list in one line. That is what Apply is good for.

So, normally if you wanted to set all the items in a collection to true (assuming the model has a property called “IsSelected”), you would do the following:


foreach (var item in collection)
{
    item.IsSelected = true;
}

With Apply I could write the same code like this:


collection.Apply(x => x.IsSelected = true);

The code is relatively simple:


/// <summary>
/// Applies the specified changes to the collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="predicate">The predicate.</param>
public static void Apply<T>(this IEnumerable<T> items, Action<T> predicate)
{
    foreach (var item in items)
    {
        predicate(item);
    }
}

It’s our familiar for loop that processes each item with the same predicate in a lambda expression.

 
AddRange and RemoveRange

I am of the belief every collection should allow you to add or remove multiple items at a time. We already have this functionality in List, so why not here? It’s actually not hard at all with our friend >Apply:


/// <summary>
/// Adds the range.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void AddRange<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Add range to local items
    collection.Apply(items.Add);
}

/// <summary>
/// Removes the range.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void RemoveRange<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Remove range from local items
    collection.Apply(p => items.Remove(p));
}

If we want to add multiple items to our collection, for example, all items that are selected in a new collection, all we would have to do is this:


collection.AddRange(newCollection.Where(p => p.IsSelected));

If we want to remove the unselected items listed in the new collection, we would do this:


collection.RemoveRange(newCollection.Where(p => p.IsSelected == false));

 
 
SynchCollection

Another common need is to be able to synch the items of 2 collections. This is often used as a way to maintain the instance of the target collection having it update itself based on the contents of another collection.

Our first method matches the item content in our target collection based on the provided source:


/// <summary>
/// Synches the collection items to the target collection items.
/// This does not observe sort order.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void SynchCollection<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Evaluate
    if (collection == null) return;

    // Make a list
    var list = collection.ToList();

    // Add items not in FilteredViewItems that are in list
    items.AddRange(list.Where(p => items.IndexOf(p) == -1).ToList());

    // Remove items from FilteredViewItems not in list
    items.RemoveRange(items.Where(p => list.IndexOf(p) == -1).ToList());
}

AddRange and RemoveRange are a big help here; allowing us to tidy up our code quite a bit.

Q: What about sorting?
A: We need to do a little more work for this, but with an ObservableCollection instead of a List as our target this is possible.


/// <summary>
/// Synches the collection items to the target collection items.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
/// <param name="canSort">if set to <c>true</c> [can sort].</param>
public static void SynchCollection<T>(this ObservableCollection<T> items, 
    IList<T> collection, bool canSort = false)
{
    // Synch collection
    SynchCollection(items, collection.AsEnumerable());

    // Sort collection
    if (!canSort) return;

    // Update indexes as needed
    for (var i = 0; i < collection.Count; i++)
    {
        // Index of new location
        int index = items.IndexOf(collection[i]);
        if (index == i) continue;

        // Move item to new index if it has changed.
        items.Move(index, i);
    }
}

Since we are talking sorting, we should be talking about ObservableCollections. We go ahead and use our previous SynchCollection method to handle the synch. After that, all we need to do is compare indexes between collections and move items as needed.

Note: This collection does not take a sort comparer because it assumes the source collection is already sorted. You should handle this type of operation independently.

And that’s all there is to it. Next time we will talk about Multithreading an ObservableCollection to offload the work from the UI thread.

If you have ideas for other extensions you would like to see, comment below.

Until next time.

As you may know, there is no double-click event for an Image in WPF. However, there are definitely cases where we might want one. But the question is, how do we pull this off while upholding a pure MVVM implementation?

Some considerations:

  • Consideration 1: We should use Commands instead of Events.
  • Consideration 2: In order to get 2 clicks, we will have to look at the MouseDown event, and look at it’s ClickCount since that is something an Image can handle.
  • Consideration 3: Since this is an Image, there is no inherent Command property. In this case, we will need to use a library that provides one.

To start, let’s create a separate class that implements ICommand. In this example all we are doing is firing a MessageBox on successful completion of the event. In a pure implementation a MessageBox would not be considered kosher, but we are just going to use it for our example so we know if it worked:

using System;
using System.Windows;
using System.Windows.Input;

namespace DoubleClickImageTest
{
    /// <summary>
    /// A test command.
    /// </summary>
    public class TestCommand : ICommand
    {
        public virtual bool CanExecute(object parameter)
        {
            return true;
        }

        public virtual void Execute(object parameter)
        {
            // We shouldn't put message boxes in commands normally, but we will for this test.
            MessageBox.Show("Hey, we just double-clicked our Image!",
                "Double-Click Test", MessageBoxButton.OK);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
}

Next, because this is MVVM we should create a View Model that will hold our command as a property:

using System;

namespace DoubleClickImageTest
{
    public class TestViewModel
    {
        #region Properties

        /// <summary>
        /// Gets or sets my command.
        /// </summary>
        /// <value>
        /// My command.
        /// </value>
        public TestCommand MyCommand { get; set; }

        #endregion

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="TestViewModel"/> class.
        /// </summary>
        public TestViewModel()
        {
            MyCommand = new TestCommand();
        }

        #endregion
    }
}

To finish setting up the View Model, we will add it to the code-behind of our View and set the Data Context of the View to the View Model instance.

using System;
using System.Windows;

namespace DoubleClickImageTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Members
        
        private TestViewModel _vm;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindow"/> class.
        /// </summary>
        public MainWindow()
        {
            // Get and set VM
            _vm = new TestViewModel();
            this.DataContext = _vm;

            // Initialize UI
            InitializeComponent();
        }

        #endregion
    }
}

We have now satisfied the conditions of Consideration 1.

In order to satisfy Consideration 2, we will need to do the following:

  1. Write a class called CustomImage that extends System.Windows.Control.Image.
  2. In CustomImage create a RoutedEvent and RoutedEventHandler to facilitate the double-click event.
  3. In CustomImage override OnMouseLeftButtonDown to evaluate the click count.

First, we create the RoutedEvent:

/// <summary>
/// The mouse left button double click event
/// </summary>
public static readonly RoutedEvent MouseLeftButtonDoubleClick =
    EventManager.RegisterRoutedEvent(
        "MouseLeftButtonDoubleClick",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(CustomImage));

Next we will create the RoutedEventHandler to execute MouseLeftButtonDoubleClick :

/// <summary>
/// Occurs when [mouse left button double click event handler].
/// </summary>
public event RoutedEventHandler MouseLeftButtonDoubleClickEvent
{
    add
    {
        AddHandler(MouseLeftButtonDoubleClick, value);
    }
    remove
    {
        RemoveHandler(MouseLeftButtonDoubleClick, value);
    }
}

Lastly, we need to override the OnMouseLeftButtonDown event.

So, why are we doing this? We know images have OnMouseLeftButtonDown, so we can use that to observe the click-count and call our event. In this case, we observe the ClickCount of our MouseButtonEventArgs. If we observe 2 clicks, we raise the MouseLeftButtonDoubleClick event:

/// <summary>
/// Invoked when an unhandled <see cref="E:System.Windows.UIElement.MouseLeftButtonDown" /> 
/// routed event is raised on this element. Implement this method to add class handling for 
/// this event.
/// </summary>
/// <param name="e">The <see cref="T:System.Windows.Input.MouseButtonEventArgs" /> that 
/// contains the event data. The event data reports that the left mouse button was pressed.
/// </param>
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
    // Double-click
    if (e.ClickCount == 2)
    {
        RaiseEvent(new MouseLeftButtonDoubleClickEventArgs(
            MouseLeftButtonDoubleClick, this));
    }
    base.OnMouseLeftButtonDown(e);
}

/// <summary>
/// MouseLeftButtonDoubleClick EventArgs.
/// </summary>
public class MouseLeftButtonDoubleClickEventArgs : RoutedEventArgs
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MouseLeftButtonDoubleClickEventArgs"/> 
    /// class.
    /// </summary>
    /// <param name="routedEvent">The routed event identifier for this instance of the 
    /// <see cref="T:System.Windows.RoutedEventArgs" /> class.</param>
    /// <param name="source">An alternate source that will be reported when the event is 
    /// handled. This pre-populates the
    /// <see cref="P:System.Windows.RoutedEventArgs.Source" /> property.</param>
    public MouseLeftButtonDoubleClickEventArgs(RoutedEvent routedEvent, object source)
        : base(routedEvent, source)
    {
    }
}

So, in it’s entirety, the CustomImage class looks like this:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace DoubleClickImageTest
{
    /// <summary>
    /// Custom image.
    /// </summary>
    public class CustomImage : Image
    {
        /// <summary>
        /// The mouse left button double click event
        /// </summary>
        public static readonly RoutedEvent MouseLeftButtonDoubleClick =
            EventManager.RegisterRoutedEvent(
                "MouseLeftButtonDoubleClick",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(CustomImage));

        /// <summary>
        /// Occurs when [mouse left button double click event handler].
        /// </summary>
        public event RoutedEventHandler MouseLeftButtonDoubleClickEvent
        {
            add
            {
                AddHandler(MouseLeftButtonDoubleClick, value);
            }
            remove
            {
                RemoveHandler(MouseLeftButtonDoubleClick, value);
            }
        }

        /// <summary>
        /// Invoked when an unhandled <see cref="E:System.Windows.UIElement.MouseLeftButtonDown" /> 
        /// routed event is raised on this element. Implement this method to add class handling for 
        /// this event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Windows.Input.MouseButtonEventArgs" /> that 
        /// contains the event data. The event data reports that the left mouse button was pressed.
        /// </param>
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            // Double-click
            if (e.ClickCount == 2)
            {
                RaiseEvent(new MouseLeftButtonDoubleClickEventArgs(
                    MouseLeftButtonDoubleClick, this));
            }
            base.OnMouseLeftButtonDown(e);
        }

        /// <summary>
        /// MouseLeftButtonDoubleClick EventArgs.
        /// </summary>
        public class MouseLeftButtonDoubleClickEventArgs : RoutedEventArgs
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="MouseLeftButtonDoubleClickEventArgs"/> 
            /// class.
            /// </summary>
            /// <param name="routedEvent">The routed event identifier for this instance of the 
            /// <see cref="T:System.Windows.RoutedEventArgs" /> class.</param>
            /// <param name="source">An alternate source that will be reported when the event is 
            /// handled. This pre-populates the
            /// <see cref="P:System.Windows.RoutedEventArgs.Source" /> property.</param>
            public MouseLeftButtonDoubleClickEventArgs(RoutedEvent routedEvent, object source)
	            : base(routedEvent, source)
            {
            }
        }
    }
}

For Consideration 3 we need to extend our image to allow Commanding, and to do that we will use the Interactivity Library from Expression Blend.

Now, we will build our XAML. Let’s start by adding our CustomImage to a window:

<Window x:Class="DoubleClickImageTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DoubleClickImageTest"
        Title="MainWindow" Height="350" Width="525">

    <Grid>
        <local:CustomImage 
            Width="200"
            Height="200"
            Source="/DoubleClickImageTest;component/cat_popcorn.jpg" 
            HorizontalAlignment="Center"
            VerticalAlignment="Center">
        </local:CustomImage>
    </Grid>
</Window>

It looks like this at runtime:

Popcorn Cat

So far, there is nothing overly significant about this implementation. Our CustomImage is behaving just like a normal image and it’s center-mass in our window.

Now, let’s add our new MouseLeftButtonDoubleClick event.

<Window x:Class="DoubleClickImageTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DoubleClickImageTest"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:CustomImage 
            Width="200"
            Height="200"
            Source="/DoubleClickImageTest;component/cat_popcorn.jpg" 
            HorizontalAlignment="Center"
            VerticalAlignment="Center">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseLeftButtonDoubleClickEvent">
                    <i:InvokeCommandAction Command="{Binding Path=MyCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </local:CustomImage>
    </Grid>
</Window>

You will notice we included the Interactivity Library in the header:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Also, this new block inside CustomImage:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseLeftButtonDoubleClickEvent">
        <i:InvokeCommandAction Command="{Binding Path=MyCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

This says: “whenever MouseLeftButtonDoubleClick is fired, invoke MyCommand“.

Note: you will notice this works exactly the same as setting a command on a Button.

So, now when we double-click our image, we see this:

Popcorn Cat with a double-click event

And just like that, we created our double-clickable image.

Until next time…

Note: This is a continuation of the very popular article Customizing the WPF MenuItem in XAML.

The source code for this solution (Visual Studio 2012) can be downloaded HERE

Q: I have a WPF submenu but I want to limit it’s height. How do I do that?
A: As mentioned in the earlier post you need to override the default Aero MenuItem template and components for WPF.

Q: So, where do we need to modify the style to fix this?
A: There are actually 2 areas in the style that require modification.

These are lines 180 – 199 of the Style (as seen in the original post linked above):


<ContentControl Name="SubMenuBorder"
			Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
			IsTabStop="false">
	<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
				  Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
		<Grid RenderOptions.ClearTypeHint="Enabled">
			<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
				<Rectangle
				Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}" 
				Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}" 
				Fill="{StaticResource SubMenuBackgroundBrush}" />
			</Canvas>
			<ItemsPresenter Name="ItemsPresenter" Margin="2"
						KeyboardNavigation.TabNavigation="Cycle"
						KeyboardNavigation.DirectionalNavigation="Cycle"
						SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
						Grid.IsSharedSizeScope="true"/>
		</Grid>
	</ScrollViewer>
</ContentControl>

Notice on line 183 (line 4), we added the MaxHeight=”400″

We repeat the again in the code between lines 457 and 476 of the Style (as seen in the original post linked above):


<ContentControl Name="SubMenuBorder"
				Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
				IsTabStop="false">
	<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
				  Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
		<Grid RenderOptions.ClearTypeHint="Enabled">
			<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
				<Rectangle
				Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}" 
				Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}" 
				Fill="{StaticResource SubMenuBackgroundBrush}" />
			</Canvas>
			<ItemsPresenter Name="ItemsPresenter" Margin="2"
						KeyboardNavigation.TabNavigation="Cycle"
						KeyboardNavigation.DirectionalNavigation="Cycle"
						SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
						Grid.IsSharedSizeScope="true"/>
		</Grid>
	</ScrollViewer>
</ContentControl>

Notice on line 460 (line 4), we added the MaxHeight=”400″

The blocks are virtually identical except the first one affects the initial Submenu and the second handles the child Submenu.

The intent here is to control the MaxHeight with a property so, we are going to do just that.

My Project file layout:

Project file layout in VS2012

Important files:

  • MenuItemEnhanced.xaml: Our new and improved MenuItem control (now with submenu height features!)
  • MenuItemEnhanced.xaml.cs: Code-behind for our new and improved MenuItem control.
  • Styles.xaml: A ResourceDictionary containing the MenuItem style mentioned above in it’s entirety.

All right. We saw the initial style sheet in it’s “glory”. Lets look at the code for the new control.

The MenuItemEnhanced XAML:


<MenuItem x:Class="Xcalibur.UI.Menu.Controls.MenuItemEnhanced"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <MenuItem.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Xcalibur.UI.Menu;component/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </MenuItem.Resources>
</MenuItem>

Nothing overly remarkable here. We created a new control that is based on MenuItem.

Now the code-behind:


using System.Windows;
using System.Windows.Controls;

namespace Xcalibur.UI.Menu.Controls
{
    /// <summary>
    /// An extended MenuItem
    /// </summary>
    public partial class MenuItemEnhanced : MenuItem
    {
        #region Properties
        
        /// <summary>
        /// Gets or sets the height of the max submenu.
        /// </summary>
        /// <value>
        /// The height of the max submenu.
        /// </value>
        public double MaxSubmenuHeight
        {
            get { return (double)GetValue(MaxSubmenuHeightProperty); }
            set { SetValue(MaxSubmenuHeightProperty, value); }
        }

        /// <summary>
        /// Gets or sets the height of the max submenu (Dependency Property)
        /// </summary>
        public static readonly DependencyProperty MaxSubmenuHeightProperty =
            DependencyProperty.Register("MaxSubmenuHeight", typeof(double), 
            typeof(MenuItemEnhanced), new PropertyMetadata(400.0));
        
        #endregion

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="MenuItemEnhanced"/> class.
        /// </summary>
        public MenuItemEnhanced()
        {
            this.InitializeComponent();
        }

        #endregion
    }
}

Here, we created a dependency property called MaxSubmenuHeight of type double. The default height in this case is 400.0 since we used that height in the last post. Nothing more needed to be done here.

The kicker is updating our ResourceDictionary to observe our new, awesome DependencyProperty. To do that, we will go back to those 2 sections of XAML in the style sheet and change this:

MaxHeight=”400″

to this:

MaxHeight=”{Binding Path=MaxSubmenuHeight,RelativeSource={RelativeSource TemplatedParent}}”

That’s all there is to it.

And now for your viewing pleasure (as included in the project linked above), here is an example. The initial menu has been capped at 600px and it’s submenu at 200px:


<Window x:Class="TestBed.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:menu="clr-namespace:Xcalibur.UI.Menu.Controls;assembly=Xcalibur.UI.Menu"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Menu>
            <menu:MenuItemEnhanced Header="Test Menu" MaxSubmenuHeight="600">
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Submenu" MaxSubmenuHeight="200">
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                </menu:MenuItemEnhanced>
            </menu:MenuItemEnhanced>
        </Menu>
    </Grid>
</Window>

And that looks like this:

Screenshot of our MenuItemEnhanced control in action

That’s all for now. Hope I helped you solve this annoying issue.

One of the bigger annoyances dealing with programming in Silverlight is the deployment of XAP files. In order to properly update a XAP file you typically:

  1. Rename XAP file to a ZIP file.
  2. Extract the ServiceReferences.ClientConfig file.
  3. Update the configuration file with the proper IP information.
  4. Update the ZIP file and save.
  5. Rename ZIP file back to XAP.

Doesn’t that sound stupid? I think so. What the hell was Microsoft thinking?

Q: So, how do we do that with code so we can skip this frustration?
A: Let’s first look at a few factors:

  • We are using the .Net 4.0 Framework.
  • Don’t bother using System.IO.Packaging.ZipPackage. It thinks XAP files are always corrupt. It’s annoying.
  • We are just updating the IP information.

To solve the ZIP file library issue, I turned to DotNetZip: http://dotnetzip.codeplex.com/. It’s by far one of the most convenient libraries out there and it’s free.

First, let’s look at how we update the configuration file if it was committed to a MemoryStream. In this snippet we:

  1. Grab all the contents from the MemoryStream.
  2. Replace the IP information in the content.
  3. Clear the MemoryStream.
  4. Overwrite the stream contents with the new content.
  5. Reset the position in the stream to 0.

/// <summary>
/// Updates the configuration file.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateConfigurationFile(MemoryStream stream,
    string replacementIp, string destinationIp)
{
    bool isSuccessful = false;

    try
    {
        // Read current file
        var reader = new StreamReader(stream);
        stream.Position = 0;
        var contents = reader.ReadToEnd();
                
        // Update IP information
        var newContents = contents.Replace(replacementIp, destinationIp);

        // Reset contents of stream
        stream.SetLength(0);

        // Overwrite original configuration file
        var writer = new StreamWriter(stream);
        writer.Write(newContents);
        writer.Flush();

        // Set position in stream to 0.
        // This allows us to start writing from the beginning of the stream.
        stream.Seek(0, SeekOrigin.Begin);

        // Success
        isSuccessful = true;
    }
    catch (Exception)
    {
    }

    // return
    return isSuccessful;
}

Our main method below does this:

  1. Extract the ServiceReferences.ClientConfig file.
  2. Call the UpdateConfigurationFile method above to revise the IP information.
  3. Update the ZIP file and commit the changes.

/// <summary>
/// Updates the silverlight configuration file.
/// </summary>
/// <param name="configFileName">Name of the config file.</param>
/// <param name="xapFilePath">The xap file path.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateSilverlightConfigurationFile(string configFileName,
    string xapFilePath, string replacementIp, string destinationIp)
{
    // Check file path
    if (!File.Exists(xapFilePath)) { return false; }

    // Open XAP and modify configuration
    using (var zip = ZipFile.Read(xapFilePath))
    {
        // Get config file
        var entry = zip[configFileName];
        var stream = new MemoryStream();
        entry.Extract(stream);

        // Manipulate configuration
        var updated =
            UpdateConfigurationFile(stream, replacementIp, destinationIp);

        // Evaluate
        if (updated)
        {
            // Replace existing configuration file in XAP
            zip.UpdateEntry(configFileName, stream);
            zip.Save();
        }
    }

    // return
    return true;
}

So, let’s look at the code in it’s entirety so that we get an implementation example as well as the needed includes:


using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ionic.Zip;

namespace XAPFileUpdaterTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            // Intialize UI
            InitializeComponent();

            // Parameters
            string configFile = "ServiceReferences.ClientConfig";
            string xap = @"MyAwesomeApp.xap";
            string replacementIp = "127.0.0.1";
            string destinationIp = "12.23.45.67";

            // Check
            var isClientConfigUpdated =
                UpdateSilverlightConfigurationFile(
                   configFile, xap, replacementIp, destinationIp);

            // Setup message
            var message =
                (isClientConfigUpdated) ? "was successful" : "failed";

            // Notify user
            MessageBox.Show("The current update " + message);
        }

        /// <summary>
        /// Updates the configuration file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateConfigurationFile(MemoryStream stream,
            string replacementIp, string destinationIp)
        {
            bool isSuccessful = false;

            try
            {
                // Read current file
                var reader = new StreamReader(stream);
                stream.Position = 0;
                var contents = reader.ReadToEnd();

                // Update IP information
                var newContents = contents.Replace(replacementIp, destinationIp);

                // Reset contents of stream
                stream.SetLength(0);

                // Overwrite original configuration file
                var writer = new StreamWriter(stream);
                writer.Write(newContents);
                writer.Flush();

                // Set position in stream to 0.
                // This allows us to start writing from the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);

                // Success
                isSuccessful = true;
            }
            catch (Exception)
            {
            }

            // return
            return isSuccessful;
        }

        /// <summary>
        /// Updates the silverlight configuration file.
        /// </summary>
        /// <param name="configFileName">Name of the config file.</param>
        /// <param name="xapFilePath">The xap file path.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateSilverlightConfigurationFile(string configFileName,
            string xapFilePath, string replacementIp, string destinationIp)
        {
            // Check file path
            if (!File.Exists(xapFilePath)) { return false; }

            // Open XAP and modify configuration
            using (var zip = ZipFile.Read(xapFilePath))
            {
                // Get config file
                var entry = zip[configFileName];
                var stream = new MemoryStream();
                entry.Extract(stream);

                // Manipulate configuration
                var updated =
                    UpdateConfigurationFile(stream, replacementIp, destinationIp);

                // Evaluate
                if (updated)
                {
                    // Replace existing configuration file in XAP
                    zip.UpdateEntry(configFileName, stream);
                    zip.Save();
                }
            }

            // return
            return true;
        }
    }
}

With the help of DotNetZip we were able to update the XAP file with our revised IP information.

That’s all for now. I hope I helped you with this annoyance.

In my last post I went over how to retrieve the Windows Product Activation information from Windows Vista / 7.0 / 2008. Today, we are going to do something on the same thread but very much more desirable. And that is retrieving the Windows Key information.

For as long as Windows has had a key, admins have been looking to be able to retrieve the Windows key information on the fly: especially when they need to do a re-install and lost their original media casing.

So, let’s get right to it with a console application.


static void Main(string[] args)
{
    string computername = Environment.MachineName;
    const RegistryHive registryRoot = RegistryHive.LocalMachine;
    const string sSubKeyName = @"SOFTWAREMicrosoftWindows NTCurrentVersion";
    const string sValueName = "DigitalProductId";
    string windowsKey = "";

    // Get product Id
    RegistryKey regoutput = RegistryKey.OpenRemoteBaseKey(
        registryRoot, computername, RegistryView.Default).
        OpenSubKey(sSubKeyName);

    // Convert to byte array
    byte[] digitalProductId;
    if (regoutput != null)
    {
        digitalProductId = regoutput.GetValue(sValueName) as byte[];

        // Get Windows Product Key
        windowsKey = (digitalProductId != null)
            ? DecodeProductKey(digitalProductId)
            : "The product key was not accessible";
    }

    else
    {
        windowsKey = "The product key was not accessible";
    }

    // Output 
    Console.WriteLine(windowsKey);
    Console.Read();
}

In this example, I am choosing my local machine as the target. The location of the value we want is located in the registry here:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionDigitalProductId.

We then get the byte array representing the key and decode it with the below method.


/// <summary>
/// Decodes a Microsoft product key based on the provided digital product id.
/// </summary>
/// <param name="digitalProductId">The digital product id.</param>
/// <returns></returns>
private static string DecodeProductKey(byte[] digitalProductId)
{
    // Offset of first byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 34H.
    const int keyStartIndex = 52;

    // Offset of last byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 43H.
    const int keyEndIndex = keyStartIndex + 15;

    // Possible alpha-numeric characters in product key.
    var digits = new[]
            {
                'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R',
                'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9'
            };

    // Length of decoded product key
    const int decodeLength = 29;

    // Length of decoded product key in byte-form.
    // Each byte represents 2 chars.
    const int decodeStringLength = 15;

    // Array of containing the decoded product key.
    var decodedChars = new char[decodeLength];

    // Extract byte 52 to 67 inclusive.
    var hexPid = new ArrayList();
    for (int i = keyStartIndex; i <= keyEndIndex; i++)
    {
        hexPid.Add(digitalProductId[i]);
    }
    for (int i = decodeLength - 1; i >= 0; i--)
    {
        // Every sixth char is a separator.
        if ((i + 1) % 6 == 0)
        {
            decodedChars[i] = '-';
        }
        else
        {
            // Do the actual decoding.
            int digitMapIndex = 0;
            for (int j = decodeStringLength - 1; j >= 0; j--)
            {
                int byteValue = (digitMapIndex << 8) | (byte)hexPid[j];
                hexPid[j] = (byte)(byteValue / 24);
                digitMapIndex = byteValue % 24;
                decodedChars[i] = digits[digitMapIndex];
            }
        }
    }
    return new string(decodedChars);
}

Now, if you are running 64-bit Windows (and most of us are), you may be running into problems getting your byte array. Why is that? Well, the short answer is that it has to do with your Configuration Manager settings. The default platform target is x86: which does not have access to several 64-bit spaces including certain registry values.

You can change that by doing the following in Visual Studio:

  1. Open your Project Properties.
  2. Go to the Build tab.
  3. For each Configuration set the Platorm target to Any CPU.
  4. For Visual Studio 2012, make sure to uncheck Prefer 32-bit.

That’s all there is to it.

Until next time.

In today’s blog I am going to give you the method I use to obtain the Windows Product Activation information in Astronomy.

This information looks like it does below:

Screen Capture

Q: So, where did this information come from?
A: Well, as you may know, you can obtain this information from the following script:
C:WindowsSystem32slmgr.vbs

I always felt this needed to be modified for use in .Net, so I refactored this a bit and created my own replacement method:


/// <summary>
/// Retrieves licensing information.
/// </summary>
/// <param name="managementScope">The management scope.</param>
/// <returns></returns>
public static string GetLicensingInfo(ManagementScope managementScope)
{
	// Output
	string result = "Information Unavailable";
	const string windowsAppId = "55c92734-d682-4d71-983e-d6ec3f16059f";

	// Default values
	const uint HR_SL_E_GRACE_TIME_EXPIRED = 0xC004F009;
	const uint HR_SL_E_NOT_GENUINE = 0xC004F200;

	// Set WMI query
	var query =
		new WqlObjectQuery(
			"SELECT LicenseStatus, GracePeriodRemaining " +
			"FROM SoftwareLicensingProduct " +
			"WHERE (PartialProductKey <> NULL) AND " +
			"(ApplicationId='" + windowsAppId + "')");

	// Get items
	using (var searcher = new ManagementObjectSearcher(managementScope, query))
	
	// Get data
	using (var colItems = searcher.Get())
	{
		foreach (ManagementBaseObject obj in colItems)
		{
			string subline;

			// License Status
			int licenseStatus = Convert.ToInt32(obj.GetPropertyValue("LicenseStatus"));

			// Time remaining: minutes / days
			int gpMin = Convert.ToInt32(obj.GetPropertyValue("GracePeriodRemaining"));
			int gpDay = gpMin / (24 * 60);

			// Evaluate
			switch (licenseStatus)
			{
				case 0:
					result = "Unlicensed";
					break;

				case 1:
					result = "Licensed";
					if (gpMin > 0)
					{
						subline =
							String.Format(
								"Activation expiration: " +
								"{0} minute(s) ({1} day(s))", gpMin, gpDay);
						result += "n" + subline;
					}
					break;

				case 2:
					result = "Initial grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "n" + subline;
					break;

				case 3:
					result =
						"Additional grace period (KMS license expired or " +
						"hardware out of tolerance)";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "n" + subline;
					break;

				case 4:
					result = "Non-genuine grace period.";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "n" + subline;
					break;

				case 5:
					result = "Notification";

					uint licenseStatusReason;
					uint.TryParse(obj.GetPropertyValue("GracePeriodRemaining").ToString(), 
						out licenseStatusReason);
						
					// Evaluate
					switch (licenseStatusReason)
					{
						case HR_SL_E_NOT_GENUINE:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(non-genuine).", licenseStatusReason);
							break;

						case HR_SL_E_GRACE_TIME_EXPIRED:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(grace time expired).", licenseStatusReason);
							break;

						default:
							subline =
								String.Format("Notification Reason: 0x{0}.",
												licenseStatusReason);
							break;
					}
					result += "n" + subline;
					break;

				case 6:
					result = "Extended grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "n" + subline;
					break;

				default:
					result = "Unknown";
					break;
			}
		}
	}
	
	// return
	return result;
}

An overboard implementation:


/// <summary>
/// Connects to a target computer through Kerberos authentication.
/// </summary>
/// <param name="computername">The computername.</param>
/// <param name="user">The user.</param>
/// <param name="securePass">The secure pass.</param>
/// <returns>
/// A ManagementScope context for the current connection.
/// </returns>
public static ManagementScope Connect(string computername, string user, 
	SecureString securePass)
{
	// Build an options object for the remote connection
	var options = new ConnectionOptions
		{
			Impersonation = ImpersonationLevel.Impersonate,
			EnablePrivileges = true
		};

	// Set properties
	// Check name
	if (String.IsNullOrEmpty(computername))
	{
		computername = ".";
	}

	// Check credentials
	// Cannot pass a blank user and password.
	if (!String.IsNullOrEmpty(user))
	{
		options.Username = user;
		options.SecurePassword = securePass;
	}
	
	// Make a connection to a remote computer.
	var managementScope =
		new ManagementScope(@"\" + computername + @"rootcimv2", options);

	try
	{
		// Connect
		managementScope.Connect();
	}
	catch (Exception ex)
	{
		throw ex;
	}
	
	// return
	return managementScope;
}

/// <summary>
/// Gets the license you so desperately want.
/// </summary>
public void GetMyLicense()
{
	// Get the management scope
	var myManagementScope = Connect(".", "XCALIBURjarzt", {my secure password});

	// Get the licensing
	string licensing = GetLicensingInfo(myManagementScope);

	// Report to client
	MessageBox.Show("My current licensing status: " + licensing);
}

So, really that’s all there is to it.

Happy coding!

One of the issues I have seen around the web is in regard to TabControls and missing content. The “missing” content in this case is the content of tab items within a TabControl that are not in focus. By design, a TabControl will only load the content of a TabItem once it has been brought into focus. This is not bad design if you think about it for a moment.

Q: Shouldn’t we just load into memory what we need at the time we need it to minimize our footprint?
A: Yes, definitely. This is good application design.

But, what about the case where I need information from other tabs that have not yet been brought into focus? For example, what if I have a GridView in another tab and I want to show the row count right when the control is loaded? Well, the answer to that is pre-loading your tab items. And since the TabControl does not have this behavior built-in by default, we will have to do it ourselves.

Here is one way I saw on the web that I wanted to bring to your attention:


for (var i = 0; i < myTabControl.Items.Count; i++)
{
	myTabControl.SelectedIndex = i;
	myTabControl.UpdateLayout();
}

Well, this looks pretty good and it has very little code. This should work, right? No, not in all cases. The reason is that this will cycle through the tabs as fast as the UpdateLayout() call completes on each tab. Because this implementation will not necessarily wait until each tab is done loading, we cannot guarantee that the content of each tab has finished loading into the Visual Tree. What we need is a solution that waits until each tab item has completely loaded its content before we move to the next tab.

So, let’s state our list of objectives:

  • Objective 1: Each tab item should wait to complete loading before the next tab item is selected.
  • Objective 2: We should hide the Tab Control from view until it is done pre-loading.
  • Objective 3: We should return to the first tab in the sequence at completion.

The approach:

In order to effectively meet the first objective, we should chain the loading of each tab through recursion.

So, here are our 2 methods:


/// <summary>
/// Preloads tab items of a tab control in sequence.
/// </summary>
/// <param name="tabControl">The tab control.</param>
public static void PreloadTabs(TabControl tabControl)
{
    // Evaluate
    if (tabControl.Items != null)
    {
        // The first tab is already loaded
        // so, we will start from the second tab.
        if (tabControl.Items.Count > 1)
        {
            // Hide tabs
            tabControl.Opacity = 0.0;

            // Last action
            Action onComplete = () =>
            {
                // Set index to first tab
                tabControl.SelectedIndex = 0;

                // Show tabs
                tabControl.Opacity = 1.0;
            };

            // Second tab
            var firstTab = (tabControl.Items[1] as TabItem);
            if (firstTab != null)
            {
                PreloadTab(tabControl, firstTab, onComplete);
            }
        }
    }
}

/// <summary>
/// Preloads an individual tab item.
/// </summary>
/// <param name="tabControl">The tab control.</param>
/// <param name="tabItem">The tab item.</param>
/// <param name="onComplete">The onComplete action.</param>
private static void PreloadTab(TabControl tabControl, TabItem tabItem, Action onComplete = null)
{
    // On update complete
    tabItem.Loaded += delegate
    {
    	// Update if not the last tab
    	if (tabItem != tabControl.Items.Last())
    	{
    	    // Get next tab
            var nextIndex = tabControl.Items.IndexOf(tabItem) + 1;
            var nextTabItem = tabControl.Items[nextIndex] as TabItem;

            // Preload
            if (nextTabItem != null)
            {
                PreloadTab(tabControl, nextTabItem, onComplete);
            }
        }

        else
        {
            if (onComplete != null)
            {
                onComplete();
            }
        }
    };

    // Set current tab context
    tabControl.SelectedItem = tabItem;
}

So what’s going on here?

  1. PreloadTabs acts as our public method where we pass the TabControl.
  2. The opacity of the TabControl is set to 0 (Objective 2). We do not change the visibility in this case because the UI will not properly update if Visibility is Collapsed.
  3. PreloadTabs will make sure it has tab items and then call our private method PreloadTab for the second tab in the collection (since the first tab would already be loaded by the instantiation of the TabControl).
  4. PreloadTab evaluates the second tab item in the collection by setting the TabControl.SelectedItem property to the current tab.
  5. Once the tab item has completed loading, it will be evaluated. (Objective 1)
  6. If it is not the last tab item in the collection, the next tab item is retrieved and passed to the PreloadTab method.
  7. Note: This behavior will repeat until the last item in the tab item collection has been reached.
  8. Once the last item has been reached, the onComplete action is called.
  9. When the onComplete action is called, we set the SelectedIndex of the TabControl back to 0 (Objective 2), and then restore the opacity of the TabControl back to 1 (Objective 3).

And just like that we have a tab pre-loader for our TabControl.

That’s all for now. Happy coding.