IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Creating Office-Style Textual Separators in WPF with Grouped Menus

Friday 31 March, 2006, 02:29 PM

I've recently been catching up on Jensen Harris's Office UI blog. Earlier this year he talked about one of the new features of the Office 12 UI: textual menu separators. These allow menus to be split into sections, but instead of just using a horizontal line, each section has a title.

I thought I'd take a crack at doing the same in WPF. It turns out to be easy, because WPF does all the work for you. The ItemsControl class provides support for grouping items, with a header for each group. (Beatriz Costa provides a great explanation of WPF's intrinsic grouped binding support here.) And since MenuItem derives from ItemsControl you can use this feature in any databound menu.

Here's what it looks like:

A menu grouped with textual separators

I did this by binding the menu to a data source via the CollectionViewSource type. (I don't know of a way to use grouping without data binding.) Let's take a look at the code, one piece at a time. This example runs on the February CTP of WinFX:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Page.Resources>
    <XmlDataProvider x:Key="menuData">
      <x:XData>
        <Items xmlns="">
          <Item Title="Foo..." GroupName="First Group" />
          <Item Title="Do stuff" GroupName="First Group" />
          <Item Title="Mangle" GroupName="First Group" />
          <Item Title="Crimp" GroupName="Another Group" />
          <Item Title="Splange" GroupName="Another Group" />
          <Item Title="Frobnicate..." GroupName="Another One" />
        </Items>
      </x:XData>
    </XmlDataProvider>

    ...

That's just an XML data island. You probably wouldn't use this technique in a real application. But for this example we need a data source, so the simplest thing to do is add one inside the XAML. (Otherwise I'd have needed to introduce some codebehind, adding unnecessary and irrelevant complications.)

Next, we wrap this in a CollectionViewSource, providing a PropertyGroupDescription indicating that we'd like to group by the data's GroupName attribute:

  
    <CollectionViewSource x:Key="src"
        Source="{Binding Source={StaticResource menuData},
                         XPath=Items/Item}">
      <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="@GroupName"/>
      </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

  </Page.Resources>
  
  ...

Next, we define our menu. This Menu element is the top-level menu bar; the interesting part is its one and only drop down menu, represented as a MenuItem.

(The MenuItem type is potentially confusing, as it is used to represent both submenus, and items in submenus. The same type is used for both kinds of item because menus can be arranged in a hierarchy. Consider a sub-submenu: it is both a menu in its own right, and also an item in another menu. You might like to think of it as an example of the composite pattern.)

  <Menu VerticalAlignment="Top">
    <MenuItem Header="MyMenu"
              ItemsSource="{Binding Source={StaticResource src}}">

      ...

Note that this drop down menu is bound to the CollectionViewSource we defined earlier. This determines the contents of the menu. We need to tell WPF how we would like it to map the items in this data source onto properties of the individual MenuItem objects that make up each entry in drop down MenuItem. (WPF will generate a child 'item container' object for each item of source data. Each class that derives from ItemsControl uses its own item container type. In the case of a MenuItem, these item containers are also MenuItem objects.) We define this mapping by supplying a Style, for the item containers:

      <MenuItem.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
          <Setter Property="Header"
                  Value="{Binding XPath=@Title}" />
        </Style>
      </MenuItem.ItemContainerStyle>

Finally, we need to tell WPF how we'd like it to display each of the group headings. We do this by supplying a GroupStyle object, using its HeaderTemplate property to define the appearance:

      <MenuItem.GroupStyle>
        <GroupStyle>
          <GroupStyle.HeaderTemplate>
            <DataTemplate>
              <Grid Background="#d9e4ec">
                <TextBlock Text="{Binding Name}" Margin="4" />
                <Rectangle Stroke="{x:Static SystemColors.MenuBrush}"
                           VerticalAlignment="Top" Height="1" />
                <Rectangle Stroke="#bbb" VerticalAlignment="Bottom"
                           Height="1" />
              </Grid>
            </DataTemplate>
          </GroupStyle.HeaderTemplate>
        </GroupStyle>
      </MenuItem.GroupStyle>
    </MenuItem>
  </Menu>

</Page>

You might be wondering what the Name property in the TextBlock's binding expression refers to. We've not defined any such property anywhere in this example. It turns out that the implicit binding context for the GroupStyle.HeaderTemplate is an object of type CollectionViewGroup. WPF creates one of these for each group in the source data.

And that's it. As Beatriz's original article shows, this isn't limited to menus - anything that derives from ItemsControl can present grouped data. Indeed, I've not really added a great deal beyond that article. I just thought it would be interesting to show the technique in the context of this specific example.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk