An Approach to Read-Only Screens in WPF

Introduction
For a line of business application I worked on recently, there was a need to put various screens in read-only mode based on various criteria.  In this article, I will present a simple approach I took to allowing screens to be placed in read-only mode using styles and binding.

The Read-Only Helper
The first thing that I needed was a way to tell a screen (or just a section of a screen), and it’s child controls, whether or not read-only mode is enabled.  For this I used a simple attached property.  An attached property is a good fit here since it can be set on any object and its value can be inherited down the tree.

 public static class ReadOnlyHelper
    {

        public static readonly DependencyProperty ReadOnlyModeProperty = DependencyProperty.RegisterAttached("ReadOnlyMode",
            typeof(bool), typeof(ReadOnlyHelper), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));

        public static bool GetReadOnlyMode(DependencyObject element)
        {
            return (bool)element.GetValue(ReadOnlyModeProperty);
        }

        public static void SetReadOnlyMode(DependencyObject element, bool value)
        {
            element.SetValue(ReadOnlyModeProperty, value);
        }

    }

Application Styles
Now that I had a mechanism to tell a screen it should be in read-only mode, I needed a way for the various controls to obey that setting and make themselves read-only or not.  To handle this, I chose to use application wide styles for each of the control types for the following reasons.

  • I was already using them to provide a consistent look of all controls throughout the application.
  • I would not have to write any procedural code.
  • The mechanism would be available by default anywhere in the application.

All that I had to do was to add property setter(s) that were bound to the attached property provided by the read-only helper to control what it meant for a control to be in read-only mode.

One of the simplest implementations was for the TextBox.  It already provides a read-only mode, through the IsReadOnly property, that allows a user to copy the contents (which can be nice) but does not allow changes to it.  All that I needed to do was bind the IsReadOnly property to the ReadOnlyHelper ReadOnlyMode property.

<Style TargetType="{x:Type TextBox}">
    <Setter Property="IsReadOnly" Value="{Binding Path=(local:ReadOnlyHelper.ReadOnlyMode), RelativeSource={RelativeSource Self}}" />        
</Style>

Of course not all controls have an IsReadOnly property nor do most define what it means to be read-only.  In these cases it was up to me to decide how to handle read-only mode.  One example of this was the CheckBox.  Although it was an option, I didn’t want to just disable all CheckBox controls when read-only because I did like the grayed out text.  What I decided to do was:

  • Disable hit testing so that a user could not click on the check box to change the value.
  • Disable tab stop so that a user could not tab to the control to change the value with the spacebar.

An important point here is that you have some flexibility in what can do to make a control read-only.   If you want to just disable a control for read-only mode, you can.

I then went through the same exercise for all other controls in the application.  In fact, for some buttons (Save buttons for example) I actually made them hidden when the parent screen was read-only since it would not apply.

Putting the Two Together
Now that I had a way to specify read-only mode and a way to modify control properties based on that setting, all that was left was to define the screens.  For a screen that needed to have a read-only mode, the attached property was placed on the root object of the screen.  It was bound to a property of the view model that controlled the mode of the screen.

<StackPanel local:ReadOnlyHelper.ReadOnlyMode="{Binding IsReadOnly}" Margin="4">            
    <TextBlock Text="Name:" />
    <TextBox Width="200" HorizontalAlignment="Left"/>

    ...
                        
</StackPanel>

Conclusion
The combination of an Attached Property and application wide styles became an effective mechanism for managing the ability to place a screen (or parts of a screen) into read-only mode without adding much to the overall architecture of the application.

Posted in Programming, WPF | Tagged , | 4 Comments

WPF Dynamic Context Menu – Revisited

Introduction
In my last article on this subject (quite awhile ago) I discussed a technique for generating a dynamic context menu for rows in a ListView control.  While that article demonstrated how the contents of a context menu could be generated, it did not discuss how to wire up commands to the options so that clicking them would actually do something.  In this article I will describe one technique for handling this issue.  In addition, this article will also show how to use a single context menu for every row.

Representing Menu Options
Because the solution needed to be MVVM friendly, it made sense to represent each menu option with a ViewModel (VM).  A simplified version of such a VM is used for this example and is illustrated below.

MenuItemVM

(Note that a property called DisplayName is supplied by the base ViewModel class.)

Another VM, such as the one for the host window, is tasked with supplying the contents of the context menu by exposing a collection of MenuItemViewModels.  The context menu ItemsSource property will be bound to this collection.

Defining the Context Menu
The Context Menu is defined as a resource of the host window (the main window in this example).

ContextMenuDef

One important thing to note here.  To allow the binding of the ItemsSource to work correctly, the DataContext of the menu must be set to the DataContext of the window.

To convert each MenuItemViewModel into a menu item object, a style is used to map various properties from the VM to the menu item.   For this example, the style is simple.

ContextMenuStyle

Note the last property setter here .  It automatically supplies the command with the item represented by the row clicked on.  This is optional.  You can always just bind the ViewModel’s CommandParameter to that property and supply the value yourself.

Generating and Displaying the Menu
Now for the real work.  The process of generating the available options for the menu and displaying it begins when a user requests the context menu by right-clicking on a row.  When that happens, the ContextMenu.ContextMenuOpening event is raised and calls a handler in the host window’s code behind (the ItemContainerStyle of the ListView wires up this event).

The event handler obtains a reference to the item represented by the row.  It then calls into the VM of the host window to update the collection of MenuItemViewModels for the item clicked on.  If any options are available, the menu’s placement target is set to the row clicked on and then the menu is opened.  If no options are generated, no menu is displayed.

ContextMenuOpening

The code that determines what menu items are available for a given item can be whatever it needs to be.  The only requirement is that it updates the collection of MenuItemViewModels that represents the context menu.  For this example, a simple switch statement is used to populate the collection based on a type code.  Simple relay command instances are used to provide an action when a menu item is clicked.

CalcMenuMethod

Conclusion
The technique described here for displaying a dynamic context menu for rows in a ListView control is effective and MVVM friendly.  The source code for the sample can be downloaded here.

Posted in Programming, WPF | Tagged , , | 12 Comments

WPF MRU ComboBox

Introduction
For a recent project, I needed a most recently used (MRU) list of phone numbers presented on a dialog box. In a previous article, I presented a re-useable Observable MRU list portion of my solution. In this article, I will describe the simple, but effective custom control I created to present (and modify) the list.

The Control
The custom control in this case turned out to be very simple. It only needed to expose two dependency properties. The first, called MruSource, holds an instance of an ObservableMruList that provides the list data. The second, called Text, is a string that holds the currently selected value. The definitions of these properties are as follows.

The control watches for changes to the Text property value. When a change is detected, the MRU list is updated with the new value.

The Template
The control template defines an editable, searchable ComboBox that serves as the user interface for the MRU list. The ItemsSource is bound to the MruSource property. The Text property of the ComboBox is bound to the Text property of the control. The other attribute settings just refine the behavior of the control. The complete template is shown below.

The data bindings hook everything together. When the Text property changes, which will happen if an item is selected from the list or the user types in a new value, the MRU list is updated, which then updates the dropdown list.

And that is it.

Conclusion
The code for the MruComboBox turned out to be very simple, but the result was a very effective control for presenting an MRU list. The full source code for the MruComboBox and the ObservableMruList as well as a demo application can be downloaded here.

Posted in Programming, WPF | 9 Comments

Fly-By Help for Menus in WPF

Introduction
Fly-By help is an old feature of Windows applications where a short description of a menu option is displayed in the status bar when hovering over a menu option.  Although old, some applications, including Windows 7 Explorer, still use it.  I thought it would be fun to create a component that would provide the fly-by help feature for menus in WPF.  In this article I will describe the solution I came up with: the Fly-By Help Service.

How It Works
First and foremost, the service needed to provide a way to specify the text to use for the fly-by help.  This task is handled by an Attached Property called FlyByText.

The service needed a way to determine if a menu is selected either by hovering over with the mouse or by using the keyboard. As it turns out, the routed events GotFocus and LostFocus provide this information for both the mouse and keyboard.  Since these events bubble, it meant that the service could catch these events for an entire menu.

The service uses another Attached Property, named IsEnabled, to trigger the wiring of these events at some parent UIElement.  When this property changes value, handlers for each event are either added or removed.

The real work then happens in the event handlers. When a GotFocus event is received for a MenuItem that is a member of a popup menu, the service retrieves the fly-by help text for that item. It then sets the FlyByText property of the root element (where the service is enabled) to that text.

When the LostFocus event is received, the FlyByHelp property of the root element is set to null.

Displaying The Fly-By Help Text
The Fly-By Help Service is only responsible for determining the appropriate help text. A view is still tasked with displaying the text in an appropriate location, such as a status bar. As noted above, the service sets the FlyByText property of the element where the service is enabled based on the menu selected. This means that a view can then simply use Binding to display the help text where ever it wants to.


Conclusion
The Fly-By Help Service is simple component that can provide a little extra assistance to users of your application. It can be used with both main and context menus easily.  The source code for the service and demo application can be downloaded here.

Posted in Programming, WPF | Tagged , | Leave a comment

Observable MRU List

For a recent project, I needed a most recently used (MRU) list of phone numbers presented on a dialog box.  Since I was using WPF for the presentation layer, I wanted to utilize data binding for the list.  I also wanted some flexibility on what types of objects could be stored by the MRU list for future re-usability.

Due to my data binding requirement, I thought it would make sense to derive my MRU class from the .NET ObservableCollection<T> class.  It already implemented a generic observable collection.  All that was needed was to add the MRU functionality.  It turned out that the additions were straightforward.

The following is the source to the class I came up with.


public class ObservableMruList<T> : ObservableCollection<T>
{
    private readonly int _maxSize = -1;
    private readonly IEqualityComparer<T> _itemComparer = null;

    public ObservableMruList() : base()
    { }

    public ObservableMruList(IEnumerable<T> collection) : base(collection) 
    { } 

    public ObservableMruList(List<T> list) : base(list)
    { }
    
    public ObservableMruList(int maxSize, IEqualityComparer<T> itemComparer) : base()
    {
         _maxSize = maxSize;
         _itemComparer = itemComparer;
    }

    public ObservableMruList(IEnumerable<T> collection, int maxSize, IEqualityComparer<T> itemComparer) : base(collection)
    {
         _maxSize = maxSize;
         _itemComparer = itemComparer;
         RemoveOverflow();
     }

     public ObservableMruList(List<T> list, int maxSize, IEqualityComparer<T> itemComparer) : base(list)
     {
         _maxSize = maxSize;
         _itemComparer = itemComparer;
         RemoveOverflow();
     }

     public int MaxSize
     {
         get { return _maxSize; }
     }

     public new void Add(T item)
     {
         int indexOfMatch = this.IndexOf(item);
         if (indexOfMatch < 0)
         {
              base.Insert(0, item);
         }
         else
         {
             base.Move(indexOfMatch, 0);
         }
         RemoveOverflow();
     }

     public new bool Contains(T item)
     {
         return this.Contains(item, _itemComparer);
     }

     public new int IndexOf(T item)
     {
         int indexOfMatch = -1;
         if (_itemComparer != null)
         {
             for (int idx = 0; idx < this.Count; idx++)
             { 
                 if (_itemComparer.Equals(item, this[idx]))
                 {
                     indexOfMatch = idx; break;
                 }
             }
         }
         else
         {
             indexOfMatch = base.IndexOf(item);
         }
         return indexOfMatch;
      } 

      public new bool Remove(T item)
      {
            bool opResult = false;

            int targetIndex = this.IndexOf(item);
            if (targetIndex > -1)
            {
                this.RemoveAt(targetIndex);
                opResult = true;
            }

            return opResult;
      }

      private void RemoveOverflow()
      {
          if (this.MaxSize > 0)
          {
              while (this.Count > this.MaxSize)
              {
                  this.RemoveAt(this.Count - 1);
              }
          }
      }
}

In addition to features one would expect in a MRU list, I added the ability to limit the size of the list as well as the ability to specify the comparer to use when looking for items. I added the latter feature since I needed the ability to create a case insensitive string MRU without creating a specialized implementation.

In a future article, I will use this class as a base for a WPF MRU ComboBox.

Posted in Programming, WPF | 2 Comments

Bound NavigationBar and the Options Dialog

Actipro Software provides a great NavigationBar control for WPF. One can use binding along with data templates to generate the navigation panes. However, one limitation with this approach is that the built-in Navigation Pane Options dialog cannot manage the collection properly. When that dialog is used, the DataContext of some of the items can end up set to {DisconnectedItem}. I suspect that this occurs because the options dialog is removing the viewmodels from the collection and then re-adding them to modify the display order.

Since I wanted to use data binding to create the Navigation Panes but still provide an options dialog, I set out to create a replacement dialog that could handle a bound collection. My guess was that all I needed to do was to create a way to manage the collection without removing and re-adding any of the items.

The NavBarOptionsWindow
The NavBarOptionsWindow contains the solution I came up with. Its user interface is the same as the built-in dialog, but the implementation works for a bound collection of view models.
The dialog take a reference to the NavigationBar control to be managed on construction. As the dialog loads, it builds its own representation of the available navigation panes. This representation consists of an ObservableCollection of PaneItemWrapper objects, described by the following diagram.

PaneItemWrapper class

The following code snip-it, taken from the PopulateAvailableOptions method, shows how this collection is created.

Note that when binding the ItemsSource property to an ObservableCollection of viewmodels, the NavigationBar’s Items property will contain a list of those viewmodel objects.  The NavigationPane controls are retrieved using the ItemContainerGenerator of the NavigationBar control.  This collection is what is manipulated by the dialog when the user elects to show or hide items and change their order.

When the user elects to save their changes, the ApplyLayoutChanges method is invoked.  This method applies the changes to the underlying Items collection of the NavigationBar control without destroying the collection or regenerating any NavigationPane objects.  There are two key steps to this process.  The first is to cast the ItemsSource of the NavigationBar control to an ObservableCollection of objects.  This will allow for the moving of items within the collection without having to remove and re-add them.

The second is to move the items in the ItemsSource around to match the order of the viewmodels specified by the user.  This is done via the Move method so that the items never leave the collection which means the NavigationPane elements will not be regenerated.

For the full details on how the ApplyLayoutChanges method works, please refer to the demo application.

Replacing the Built-In Dialog

Unfortunately, we can’t actually replace the dialog provided by Actipro, but we can utilize the CustomizeButtonClick event to remove the existing menu option and then create our own clone option that invokes the replacement dialog.  Please refer to the demo application for an example of how to do this.

And there you have it.  A dialog that can handle a bound collection of Panes for the Actipro NavigationBar control.  The full source for the dialog and demo application can be downloaded here.  You will need your own copy the Actipro NavigationBar control for the demo to work (the runtime is not provided).

One additional thing to note here is that there is still the issue of restoring the layout.  Using the built in functionality will result in the same problem of disconnected items.  This issue will be addressed in a future article.

Posted in Programming, WPF | Tagged , , | Leave a comment

Mutually Exclusive Checkboxes in WPF

While rare, there has been a few times where I have been asked to implement mutually exclusive checkboxes within a user interface.  Recently I was asked to do this within an application written in WPF.  A quick internet search on turned up one solution that looked promising.  However, it utilized a static dictionary used to cache group members.  While this worked for a single window or user control, it does not work when multiple instances of the same UI are available at the same time because of the static dictionary.

Without a ready-made solution, I decided to create my own solution.  I wanted a behavior similar to how radio buttons can be grouped in WPF and I did not want to store control references.  I ended up with what I called the Toggle Button Group Service.  This single class provides two attached properties in order to expose the grouping functionality.

The first property, named IsEnabled, is used to enable the grouping of toggle buttons within a UIElement container, such as a Border or GroupBox.  When set to true, an event handler is attached to the ToggleButton.CheckedEvent.  When a ToggleButton (or derived class) is checked, the event handler looks for all other buttons in the group and sets their state to unchecked.

The second property, named GroupName, provides a way to specify different groups within the same container, similar to radio buttons.

The service class and demo project can be downloaded here.  After downloading, change the extension to .zip and then extract.

The following XAML illustrates how the attached properties are used.

<Border BorderBrush="Black" BorderThickness="1" CornerRadius="4" Padding="5" local:ToggleButtonGroupService.IsEnabled="True">
<StackPanel>
<CheckBox Content="Option One" Margin="0,1"/>
<CheckBox Content="Option Two" Margin="0,1"/>
<CheckBox Content="Option Three" Margin="0,1"/>
<CheckBox Content="Option Four" Margin="0,1"/>
<GroupBox Header="Other " Margin="0,5" Padding="1,2">
<StackPanel>
<CheckBox Content="Option Five" Margin="0,1" local:ToggleButtonGroupService.GroupName="Other"/>
<CheckBox Content="Option Six" Margin="0,1" local:ToggleButtonGroupService.GroupName="Other"/>
</StackPanel>
</GroupBox>
</StackPanel>
</Border>

Posted in Programming, WPF | 2 Comments

WPF Dynamic Context Menu

For a project I was working on recently, I needed the ability to create a context menu for individual rows of a listview within a WPF application.  Each row could have a different context menu, or no menu at all.  I found few articles about this.  The ones I did find typically pre-generated the context menu during binding which to me was not a good approach for performance reasons.  So I did some prototyping and here is an approach that I came up with.

The following XAML snippet is from a window that contains a simple ListView control bound to some XML (for demo purposes).

<ListView ItemsSource="{Binding Source={StaticResource DemoData}, XPath=Widgets/Widget}" Margin="5" VerticalAlignment="Stretch">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding XPath=@name, Mode=OneWay}"></GridViewColumn>
                    <GridViewColumn Header="Type" DisplayMemberBinding="{Binding XPath=@type, Mode=OneWay}"></GridViewColumn>                    
                </GridView>
            </ListView.View>            
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <EventSetter Event="ContextMenu.ContextMenuOpening" Handler="Row_ContextMenuOpening"></EventSetter>
                </Style>
            </ListView.ItemContainerStyle>                        
        </ListView>

The first piece to the solution is the EventSetter in the ItemContainerStyle.  It sets up an event handler that is called when a context menu is requested on a row.  The event handler does not fire during binding, which is what I wanted.

The event handler is what processes the request and builds a context menu for the row clicked on.  Since this is just a sample, all the code will just be in the event handler.  For a production application, I would delegate the work to the ViewModel of the window.  Lets now take a look at how the event handler processes the context menu request.

private void Row_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {

            List<string> menuOptions = new List<string>();
            ListViewItem sourceRow = sender as ListViewItem;

            if (sourceRow != null)
            {
                sourceRow.ContextMenu = new ContextMenu();

                XmlElement targetItem = sourceRow.DataContext as XmlElement;
                if (targetItem != null)
                {
                    sourceRow.ContextMenu.ItemsSource = GetMenuItemsForWidget(targetItem);
                }
                
                if (sourceRow.ContextMenu.Items.Count > 0)
                {
                    sourceRow.ContextMenu.PlacementTarget = this;
                    sourceRow.ContextMenu.IsOpen = true;
                }
                else
                {
                    sourceRow.ContextMenu = null;
                }
            }

        }

The first thing the handle does is obtain a reference to the row right-clicked on.  In this case, that is a ListViewItem object.  Then a new context menu is assigned, clearing out any previous menu.  (Note: if it were good enough to calculate the menu once, one could skip the rebuilding of the menu)  The handler then gets a reference to the item bound to the row.  In this case, it is a XML element.  Another method is then called to generate the menu options for the bound item.  We will look at that method later.  Finally, if the context menu created has items, the context menu is shown.  Otherwise, the context menu is set to null so that no menu is displayed (Note: if this is not done, a small empty menu will be displayed).

For this sample, the GetMenuItemItemsForWidget method just returns a list of strings to use a menu items.  In a real application, a method like this could return a collection of menu item models or commands, etc.  The sample method is shown below.

private List<string> GetMenuItemsForWidget(XmlElement widget)
        {
            List<string> menuOptions = new List<string>();

            string itemType = widget.GetAttribute("type");
            switch (itemType)
            {
                case "clock":
                    menuOptions.Add("Adjust Time");                    
                    break;
                case "meter":
                    menuOptions.Add("Change Color");                    
                    break;
                case "viewer":
                    menuOptions.Add("Select Directory");
                    break;
            }

            return menuOptions;
        }

And that is how I created a dynamic context menu for ListView item rows with no overhead during binding.

Posted in Programming, WPF | 7 Comments

VS Command Prompt Shortcut

Add a Visual Studio Command Prompt shortcut to the folders context menu in Explorer
A lot of people have a "Command Prompt Here" shortcut on their folder context menu in explorer.  It is an easy task to add one
using the registry editor.  As a programmer, I’ve always wanted an entry for the Visual Studio command prompt as well.  Here is how to add one.
 

1. Goto HKLM/Software/Classes/Folder/Shell using the registry editor.
2. Create a new key called VSPrompt.  For the default value, enter the text you want for the menu option.
3. Create a new key under VSPrompt called command.
4. Set the default value to the following:  
   cmd.exe /k ""C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat"" x86 && title Visual Studio Command Prompt && pushd %L

 

Posted in Computers and Internet | Leave a comment