Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Grouped-Checkable-Menus for Composite-Criteria-Parameters

4.00/5 (2 votes)
29 Nov 2011CPOL4 min read 17.2K   224  
A menu-based UI filtering solution in which the first level will show the filtering criteria and the next levels will give the optional values in more and more granular resolution options.

Sample Image

Introduction

Dealing with large data-sets often requires some kind of filtering mechanism. For more than once, I've found that the following filtering approach comes in handy (for both UI and code-design). What I was aiming for was a menu-based UI filtering solution in which the first level will show the filtering criteria and the next levels will give the optional values in more and more granular resolution options. Each criteria can have only single or none filter value, where none means no filtering. I also wanted to have a visual indicator of whether any filtering is being applied*.

Here are some screenshots that might make it more clear:

Image 2 Image 3 Image 4 Image 5

* A faster way to understand what I am aiming for might be to download and run the sample project...

Background

UI Aspect

Using menu as the filtering-parameters-selection-platform has the advantage of minimum client area while giving the user information about the filtering criteria (in more details as the menu expands).

Implementation

The suggested implementation is heavily based on AttachedProperties. This allows to implement all the functionality inside the View and pass the ViewModel only the relevant information through binding. In the ViewModel, Using a filter/search command and LINQ/lambda, for instance, a result bound list could be populated for display (not implemented in this sample).

AttachedProperties (AP)

The next paragraph is for readers not familiar with AttachedProperties. If you are familiar with the concept, you can skip to the 'Using the code' section.

Here's a quote:

'The concept of Attached Properties (AP) is one of the most innovative features of WPF and also one of the least utilized.'

--WPF Control Development Unleashed/SAMS

Rather than explaining what APs are in WPF (which you can find in any WPF book), I'll try to convey its idea using a 'real-life' sample:

Imagine a custom desk internet-ordering-form, it has a few lines that enables the user to specify its properties:

  • Color
  • Width
  • Height
  • Depth

At the bottom, there's a submit button which when pressed sends this form and, as a consequence, a new desk will be built (instantiated).

All is well, except...you would like to have some drawers...

In an 'AP-enabled-reality', all you'll need to do is to take out your 'magic pen' and hand-write another line. Now the form would look like this:

  • Color-green
  • Width-100
  • Height-50
  • Depth-50
  • NumberOfDrawers

NumberOfDrawers has a default value of zero, so you can omit its value if you wish a desk with no drawers.

What we actually did here is an alteration/customization/extension of an existing 'initialization-form'.

To make things even more interesting - let's say that we want the Height value to increase in 20 cm for every additional drawer we'll set (in the NumberOfDrawers property). Luckily, just like the default value set for the NumberOfDrawers property, we can also set a callback that will get triggered on every change of its value. There, we could update the Height property like so:

Height=_BaseHeight+(NumberOfDrawers*20)

I hope that this small analogy helped in giving an initial clue about the role AP has in WPF.

I've found AttachedProperties especially useful in two circumstances:

  1. Functionality Extension.
  2. Binding/Animation enabling for properties that aren't DependencyPropertys; there, AP (which are Bindable/Animatable as they are DependencyProperty) acts as a proxy.

Using the code

In order to achieve the desired functionality, I've used AP to extend the MenuItem Elements.

In XAML:

Every criteria top level MenuItem has the IsGroupValueHolder AP set to True, for code-implementation purposes, and Value set to a bound ViewModel's Matching property.

XML
<MenuItem Header="Rating" local:CheckGroupHelper.IsGroupValueHolder="True" 
    Style="{StaticResource CheckGroupChildStyle}" 
    local:CheckGroupHelper.Value=
       "{Binding Rating,Mode=OneWayToSource}">

Every criteria bottom level checkable value MenuItem has Value set to some predefined parameter.

XML
<MenuItem Header="5" Style="{StaticResource CheckGroupChildStyleLeaf}" local:CheckGroupHelper.Value="5">

Inside styles:

'CheckGroupChildStyleLeaf' - IsCheckable sets to True (to allow user check), and IsChecked sets to our own IsCheckGroupChecked AP (which we monitor for value changes) via Binding.

'CheckGroupChildStyle' - IsCheckable sets to False, and asterisk visibility is controlled by Binding to the Value AP, using the AnyString2Asterisk Converter.

In code (CheckGroupHelper):

Inside IsCheckGroupCheckedChanged (relevant only for 'leaf' MenuItem):

If checked:

  1. Uncheck the currently checked value in this criteria-group (which consequently will bubble an empty value up to the group's value-holder).
  2. C#
    MenuItem micc = GetGroupCurrentlyCheckedLeaf(mi);
    if (micc != null)
    {
        micc.IsChecked = false;
    }
  3. Bubble this 'leaf' value.
  4. C#
    mi.Parent.SetValue(CheckGroupHelper.ValueProperty, 
              obj.GetValue(CheckGroupHelper.ValueProperty));

If unchecked: bubble empty value.

C#
mi.Parent.SetValue(CheckGroupHelper.ValueProperty, "");

Inside ValueChanged:

  • Bubble value up the MenuItems tree.
  • Don't bubble empty value beyond the criteria's top level MenuItem (IsGroupValueHolder =True) if any other criteria top level MenuItem has a value (so, the 'Filters' MenuItem will still show that it has a value!).

Points of Interest

  1. I hope that with this simple demonstration, I have successfully shown how, by using AttachedPropertys, we can achieve special UI functionality, with minimum code, while preserving a clean MVVM infrastructure.
  2. In previous versions of this application, I used a 'Named-Groups' approach (just like a radio-button's GroupName property).
  3. This approach, although it gives a lot of design flexibility, is redundant for this kind of scenario. Groups here are clustered and defined by the MenuItems hierarchy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)