Updated RibbonBar

Update to RibbonBar – is now a separate Resource file. Simply merge into the resource dictionary and use the RibbonBarStyle on the TabControl you want to look like a Ribbon. The sample below can be copied direct into XAML viewer (e.g. Kaxaml). This is a XAML only version so doesn’t contain any of the command binding stuff, curvy tab headers etc.

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="http://www.spencen.com/downloads/RibbonBarResource.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<StackPanel>
<TabControl Style="{StaticResource RibbonBarStyle}">
<TabItem Header="Tab 1">
<ToolBarTray>
<ToolBar>
<Button Tag="http://www.spencen.com/downloads/Button1_48.png">Button 1</Button>
<Button Tag="http://www.spencen.com/downloads/Button1_24.png">Button 2</Button>
<Button Tag="http://www.spencen.com/downloads/Button1_24.png">Button 2.1</Button>
</ToolBar>
</ToolBarTray>
</TabItem>
<TabItem Header="Tab 2">
<ToolBarTray>
<ToolBar>
<Button>Button 3</Button>
<Button>Button 4</Button>
<ComboBox Text="ComboBox 1" Width="80"/>
</ToolBar>
</ToolBarTray>
</TabItem>
</TabControl>
</StackPanel>
</Page>

Grab the RibonBarResource.xaml from here.

Revisiting Commands

Having put together the basics of the user interface for my current project I decided to do some more reading regarding the best practices around using a Command Pattern in WPF.

Specifically this means determining how to use the ICommand and RoutedCommand. My previous attempt (without reading any of the documentation) was something of a failure but I pretty much have the same goals in mind:

  1. UI elements that perform an action that is not purely UI related are required to be bound to a Command. This must be easily done in XAML.
  2. The Window/Page itself should have minimal (i.e. as close to possible to zero) code in the code behind. This means no ugly event handlers.
  3. The Command or more likely the Command Binding mechanism will be responsible for setting attributes on the associated UI element(s). So this includes things like Text, Image, Tooltip, Enabled, Visible etc.

The closest thing I’ve found so far in terms of recommended approaches is the collection of blog entries written by Dan Crevier back in 2006. Dan does a great job of discussing a simplified version of the Data Model – View – View Model (DV-V-VM) that the team working on Microsoft Max used. Having said that there are some approaches that he takes that I either find too unwieldy (one command per class) or (just as likely) don’t fully comprehend.

Reading straight out of the MSDN documentation I stared with:

    <Page.CommandBindings>
        <CommandBinding Command="{x:Static pages:RibbonTest.WriteEntryCommand}" 
Executed
="CommandBinding_Executed"
CanExecute
="CommandBinding_CanExecute"/>
</Page.CommandBindings>
...
<Button Command="pages:RibbonTest.WriteEntryCommand"/>

This involved defining my own RoutedCommand on the page itself (pages:RibbonTest) and then binding the Executed and CanExecute events to event handlers on the page. Of course this doesn’t meet any of my three requirements, – it’s too much XAML, it requires code behind event handlers and with the exception of the Enabled property doesn’t “reflect” command properties to the UI element.

Needless to say I went through a whole raft of possible solutions from here (refer to references below), trying each in turn to see what worked for me and what didn’t.

At the moment I have created an attached dependency property (as per Dan’s post) so that my XAML is nice and concise.

<TabControl Name="tabControl2" Style="{StaticResource RibbonBarStyle}">
    <TabItem Header="Holidays">
        <ToolBarTray>
            <ToolBar Header="Select a Holiday">
                <Button commands:CommandBinder.Command="{Binding Controller.NewHoliday}"/>
                <Button commands:CommandBinder.Command="{Binding Controller.OpenHoliday}"/>
</ToolBar>
        </ToolBarTray>
    </TabItem>
...


</TabControl>

The Controller referenced above is instantiated by the form and defines all of the commands available for the form. It is also responsible for keeping the form state which is then reflected in the commands, and thus by the buttons. The commands are actually a custom class (UICommand) that is a “wrapper” around a RoutedCommand and contains other metadata that is associated with the command. This includes the caption text, full description, image, IsEnabled and IsAvailable (visible/security). The wrapped RoutedCommand also defines any keyboard gestures.

Taking this approach means that:

  1. Only the attached property needs to be set – the CommandBindings are not required – this is done automatically by the attached property.
  2. The command execution logic (callback) lives either in the controller, or in a UICommand derivative (for common functions like navigation).
  3. The only code required in the form code-behind is the instantiation of the controller. Of course this could easily be done in XAML too if preferred.
  4. In addition to setting the Text (via binding), Enabled and Visibility properties I’m also using the CommandBinder to build a groovy Tooltip complete with input key shortcuts.

XAML Ribbon with Tooltip

Of course its not all roses – there are more than a couple of “nasty” bits in my implementation at the moment. One surprising one was an oddity in the ICommandSource interface. Within the CommandBinder attached property I thought to use this interface to set the Command property on the source at the same time as I registered the RoutedCommand. However, it turns out that the ICommandSource.Command property is read-only – most frustrating.

More than anything though I’m curious as to how everyone else tackles implementing a command pattern (and MVC/MVP etc.) in WPF?

References:

WPF Commands on Code Project

Karl on WPF

Rob Relyea on ICommand, RoutedCommand and RoutedUICommand

Josh Smith on RoutedCommand

Josh Smith on MVC

Commands in Prism (some great comments)

ToolBar Buttons

For the ToolBar/RibbonBar in my hobby application I wanted to continue the trend of using “out-of-the-box” components and simply applying a style to give the correct look.


When placed within a ToolBar control the standard controls (Button, CheckBox, ComboBox etc.) all get a custom style applied giving them a “flat” appearance that suits a ToolBar. Overriding this style turns out to be very easy. Its simply a matter of creating a new style with the correct name. This is explained in the MSDN documentation here.


In practice it looks something like:

<TabControl Style=”{StaticResource RibbonBarStyle}”>
<TabControl.Resources>
<Style TargetType=”{x:Type Button}” x:Key=”{x:Static ToolBar.ButtonStyleKey}”>

</Style>
</TabControl.Resources>

</TabControl>

A common approach is to use the style to override the Template and remove the default Style (using OverridesDefaultStyle=”true”). Going down this path means that your style needs to cater for all button states, e.g. disabled, pressed, focused, mouseover etc.


Currently I’ve opted for a simpler alternative by using the Style to override only the ContentTemplate that defines the layout of just the content portion of the Button, rather than the entire Template.


So the XAML that I had been using to insert an image button into a ToolBar:

<Button> 
   
<StackPanel>
       
<Image Source=”/Images/film.png” Width=”48″/>
       
<TextBlock HorizontalAlignment=”Center”>Video</TextBlock>
   
</StackPanel>
</Button>
<Button HorizontalAlignment=”Left”>
   
<StackPanel Orientation=”Horizontal”>
       
<Image Source=”/Images/folder24.png” Width=”24″/>
       
<TextBlock VerticalAlignment=”Center”>Open in Windows Explorer</TextBlock>
   
</StackPanel>
</Button>

Becomes:

<Button Tag=”/Images/film.png”>Video</Button>
<
Button Tag=”/Images/folder24.png”>Open in Windows Explorer</Button>

For now I’m going to live with the image URI being stuck in the Tag property. Soon I plan on getting rid of the image and text references to be replaced with a single command reference, i.e. binding the command will set the image and text of the button in addition to the button enabled state, visibility etc.


The Style that I’m using is as follows:

<Style TargetType=”{x:Type Button}” x:Key=”{x:Static ToolBar.ButtonStyleKey}”> 
<Setter Property=”Margin” Value=”0″/>
<Setter Property=”Padding” Value=”0″/>
<Setter Property=”HorizontalAlignment” Value=”Left”/>
<Setter Property=”ContentTemplate”>
<Setter.Value>
<DataTemplate DataType=”{x:Type Button}”>
<Border x:Name=”Border” CornerRadius=”4″>
<StackPanel x:Name=”PART_panel” Orientation=”Horizontal”>
<Image x:Name=”PART_image” Margin=”4,0,0,0″
Width=”{Binding RelativeSource={x:Static RelativeSource.Self},
Path
=Source.PixelWidth}”
Source
=”{Binding RelativeSource=
{
RelativeSource FindAncestor, AncestorType=Button},
Path
=Tag}”/>
<ContentControl x:Name=”PART_text”
Text
=”{Binding RelativeSource=
{
RelativeSource FindAncestor, AncestorType=Button},
Path
=Content}”
VerticalAlignment
=”Center” Margin=”3,0,4,0″/>
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding=”{Binding ElementName=PART_image, Path=Source.PixelHeight}”
Value
=”48″>
<Setter TargetName=”PART_panel” Property=”Orientation” Value=”Vertical”/>
</DataTrigger>
<DataTrigger Binding=”{Binding RelativeSource=
{
RelativeSource FindAncestor, AncestorType=Button},
Path
=IsEnabled}”
Value
=”false”>
<Setter TargetName=”PART_panel” Property=”Opacity” Value=”0.5″/>
<Setter TargetName=”PART_text” Property=”HorizontalAlignment” Value=”Center”/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

The images themselves are shown using the native resolution. You would have thought Stretch=”None” might have done that – but don’t be fooled. For reasons relating to DPI scaling you have to set either the Width or Height to an absolute value. I used the Binding {Binding ElementName=PART_image, Path=Source.PixelHeight} to achieve that.


One trick I did do here is use a Data Trigger to change the placement of the text in relation to the image based on the size of the image that is used. Its a little clunky but I’m using images that are either 16, 24 or 48 pixels sqaure. The 16 and 24 pixel sqaure images have the text positioned to the right of the image. For 48 pixel square images the text is positioned underneath.


So the following XAML:

<TabItem Header=”Hello”> 
<ToolBarTray>
<ToolBar Header=”Hello World, I’m a Ribbon Group”>
<Button Tag=”/Images/note_edit_48.png”>Write Entry</Button>
<Button Tag=”/Images/users_family_24.png”>Maintain Users</Button>
<Button Tag=”/Images/folder_24.png”>Open in Windows Explorer</Button>
<Button Tag=”/Images/weather_48.png”>Record Weather</Button>
</ToolBar>
<ToolBar Header=”Second Group”>
<Button Tag=”/Images/film_48.png”>Video</Button>
<Button Tag=”/Images/earth_location_24.png”>Maintain Locations</Button>
<Button Tag=”/Images/money2_24.png” IsEnabled=”False”>Maintain Currencies</Button>
<Button Tag=”/Images/presentation_24.png”>Slideshow</Button>
</ToolBar>
</ToolBarTray>
</
TabItem>

Generates the following styled TabItem within a parent TabControl.


XAML Ribbon with Button ContentTemplate Style


The buttons lay themselves out nicely arranged automatically from left to right because I’ve set the ToolBar’s ItemsPanel to be a fixed height WrapPanel:

<ItemsPanelTemplate> 
<WrapPanel Orientation=”Vertical” Height=”70″/>
</ItemsPanelTemplate>

If you want to customise the button more – for example to give it an orange gradient mouse over effect then you’ll need to override the Template rather than ContentTemplate in the Button Style.

[Updated 24-Mar-2008 to use ContentControl instead of TextBlock in the ContentTemplate to allow for Buttons that wanted full control of their own content.]

XAML Ribbon Bar – Part 3

Doh! Various remembered references to WPF element z-ordering and ClipToBounds properties suddenly all came together with the realisation that negative values are allowed on the Margin property!

So the “removal” of the line between the tab header and the border around the tab content becomes so much nicer using the following:

  • Set the element containing the ItemsPresenter (tab headers) to have Panel.ZIndex=”5″ so that it gets painted after the content border element.
  • Set the TabHeaderBorder to have Margin=”0,0,0,-2″ so that it intrudes a few pixels into the content border’s area. Because the z-index ensure the TabHeaerBorder gets painted after the border this effectively lets the header overwrite the content border.
  • Tweak TabHeaderBorder OnRender() so that the background extends a few pixels (default of 2) past the curvy border. The extra background will be used to cover up the border (which means the bottom gradient of the tab header must match the top gradient colour of the content area – which is the whole effective I’m trying to achieve anyway).
  • Use a Trigger to <Setter Property=”ClipToBounds” Value=”True”/> on the TabItem when <Trigger Property=”IsSelected” Value=”False”>. This ensures that if a mouse over effect is used to render a background it won’t intrude into the content area unless its the selected tab.

So I can now ditch the whole TabContentBorder class – which was thoroughly nasty anyhow.

I’ve modified the XAML and uploaded here. I’ve changed the uploaded version to use a plain Border instead of my TabHeaderBorder so you won’t get the ultra curvy tabs but this means it can be run as a stand-alone XAML file. All the image references fail of course so there are no pretty images.

XAML Ribbon Bar – Part 2

When designing the XAML Ribbon Bar I really got stuck figuring out how to get the curved effect on the Tab Item. At one stage I used a Path instead of the Border in the markup, so instead of:

<Border CornerRadius="5,5,0,0" 
BorderBrush
="Red" BorderThickness="1,1,1,0" Background="Yellow"
HorizontalAlignment
="Left" Padding="4,2"> <TextBlock>Header Text</TextBlock> </Border>

XAML Tab Header using Border

I used:

<Canvas Height="25">
    <Path Stroke="Red" StrokeThickness="1" Fill="Yellow"
Data
="M 5,22 Q 10,22 10,17 V 5 Q 10,0 15,0 H 75 Q 80,0 80,5 V 20 Q 80,22 85,22"/> <TextBlock Margin="12,4">Header Text</TextBlock> </Canvas>
XAML Tab Header using Path 

The problem with this was I just couldn’t figure out how to get it to scale nicely to the tab header content (names on the tabs). You can’t bind to the X and Y properties on a point, and as it turns out there is a bug that means you can’t bind to the StartPoint either.

After spending too long trying to figure this out I resigned myself to the fact that I was going to have to write some C# code to get the job done. This turned out to be trivial…

public class TabHeaderBorder : Border
{
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
PathSegmentCollection segments = new PathSegmentCollection();
segments.Add(new QuadraticBezierSegment(
new Point(CornerRadius.BottomLeft, ActualHeight),
new Point(CornerRadius.BottomLeft, ActualHeight - CornerRadius.BottomLeft), true)); segments.Add(new LineSegment(
new Point(CornerRadius.BottomLeft, CornerRadius.TopLeft), true)); segments.Add(new QuadraticBezierSegment(
new Point(CornerRadius.BottomLeft, 0),
new Point(CornerRadius.BottomLeft + CornerRadius.TopLeft, 0), true)); segments.Add(new LineSegment(
new Point(ActualWidth - CornerRadius.TopRight - CornerRadius.BottomRight, 0), true)); segments.Add(new QuadraticBezierSegment(
new Point(ActualWidth - CornerRadius.BottomRight, 0),
new Point(ActualWidth - CornerRadius.BottomRight, CornerRadius.TopRight), true)); segments.Add(new LineSegment(
new Point(ActualWidth - CornerRadius.BottomRight, ActualHeight - CornerRadius.BottomRight), true)); segments.Add(new QuadraticBezierSegment(
new Point(ActualWidth - CornerRadius.BottomRight, ActualHeight),
new Point(ActualWidth, ActualHeight), true)); segments.Add(new LineSegment(
new Point(0, ActualHeight), false)); PathFigure borderPath = new PathFigure(new Point(0, ActualHeight), segments, true); PathFigureCollection figures = new PathFigureCollection(); figures.Add(borderPath); PathGeometry borderGeometry = new PathGeometry(figures); dc.DrawGeometry(Background, new Pen(BorderBrush, BorderThickness.Left), borderGeometry); } }

Which gave the visual effect I was after and the same content capabilties as the Border. This meant that by default it would automatically size to its content. I did cheat a little bit in that the BorderThickness is only treated as a single value – but I did allow separate corner radius values.

<controls:TabHeaderBorder CornerRadius="5, 5, 10, 10" 
BorderBrush
="Red" BorderThickness="1" Background="Yellow"
HorizontalAlignment
="Left" Padding="12,4"> <TextBlock>Header Text</TextBlock> </controls:TabHeaderBorder>

XAML Tab Header 1

XAML Tab Header With LineNow – how do I get rid of the horizontal line between the selected Tab Header and the Tab Content? Of course the easiest solutions was to simply not paint the content Border’s top edge but I decided that I cared enough.

XAML Tab Header Without LineAgain – I couldn’t figure out how to approach this in XAML. So given how easy it was to create the TabHeaderBorder class I created a TabControlBorder. This control gets “linked” to the actual TabControl to check for and subscribes to its SelectedIndex changed event. The event handler then invalidates the controls visuals so that the OnRender method can draw the border and then “paste” a line directly underneath where the TabItem’s would be drawn. It certainly isn’t pretty but its the most adaptable solution I’ve come up with so far. The results look Ok – unless you really zoom in using a tool like Windows Magnifier which allows the WPF vectors so scale correctly. Once zoomed you can see that the lines are slightly disjointed – not surprisingly because they are drawn on two different UI elements.

    public class TabContentBorder : Border
    {
... Dependency property stuff
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) { InvalidateVisual(); } protected override void OnRender(System.Windows.Media.DrawingContext dc) { if (TabControl != null && TabControl.SelectedIndex >= 0) { TabItem selectedItem = (TabItem) TabControl.SelectedItem; double x = selectedItem.TransformToAncestor(TabControl).Transform(new Point(0,0)).X; // Use the base paint - we'll paint "over" it with the background colour base.OnRender(dc); // Erase the border line segment - this is making a rash assumption that a linear gradient brush is used! dc.DrawRectangle(
new SolidColorBrush(((LinearGradientBrush) Background).GradientStops[0].Color), null,
new Rect(x + 1, -1, selectedItem.ActualWidth - 1, BorderThickness.Top + 2)); } else { base.OnRender(dc); } }

Yuerk – that isn’t pretty – will have to do for now though

I’ve changed the ItemsPanelTemplate for the Toolbar to be a fixed height WrapPanel and its pretty much giving me what I want now. Although – I have a very limited set of requirements

 XAML Ribbon Bar - Part 2

And just for laughs here the same content but without Style=”{StaticResource RibbonBarStyle}” applied.

 XAML Ribbon Bar Default Style

Interestingly what got me started down this whole Ribbon path was the fact that the only way I could find to switch off the display of the Overflow chrome on the ToolBar was to override its default template.

Bigpond Movies

I’m a big fan of Windows Media Center. At home we only have one television that actually gets used – and everything we watch comes through the attached Media Center PC. Whether that be live TV, recorded TV, home video or downloaded video. Building the Home Theatre PC

The HTPC is home assembled and running Windows Media Center 2005 (Rollup 2). It was mindbogglingly finicky to install so I’ve never bothered trying to upgrade to Windows Vista. Besides which the wife would be most irate if it went offline – she’s come to take the pausing of live TV for granted and can’t seem to do without it. Being difficult to install is much the fault of the TV Tuner card hardware and software as it is Media Center (reminds me of the early video editing days, and even earlier desktop publishing days). Also – being in Australia we have to use shareware TV guide software which comes in several components that must be integrated correctly. Oh and don’t forget downloading your own MPEG video codec.

Of course you could buy a ready made box – but where’s the fun in that!?

Anyhow just recently I was flicking through the Online menu in Windows Media Center and came across the Bigpond Movies button. Being an anti-fan of Telstra and knowing that they hire some of the worst UI designers on the planet (just check-out their websites) I wasn’t expecting much.

Well – it appears I was wrong. Its the first “online” Media Center application that’s really impressed me. Most of the video of demand type offerings go through the awkward transition as they jump from a Media Center powered UI to a clunky web one. But Bigpond Movies is rendered using the Media Center UI – and it shows. Transitions are smooth, the UI is well thought out and “10 foot” friendly.

As a rule I don’t watch many videos. The last time I rented a movie was about 10 years ago. But I was so impressed with Bigpond’s snazzy Media Center app that I couldn’t resist. Bonus points go to them for:

  1. Making it run on Media Center under both Windows XP and Vista (there were some significant API changes between the two).
  2. Use of .NET Framework 3.0 (not sure what for but its an installation dependency).

Once you’ve selected added movies to your cart and checked out…

Bigpond Movies Move Download Info

You are taken to the My Downloads page where you can check the status of the movies you’ve ordered. This has a UI similar to the XBOX with smoothly scrolling vertical tabs. The movies are large (e.g. 1.4 Gb) but even on my modest 1500Kb connection they are ready for watching in about 10 minutes as the video is buffered.

Bigpond Movies

WPF Jobs and Cider

Some great sounding jobs up for grabs at Redmond. It appears his soul is currently consumed with LINQ but based on his astute observations here I think maybe Paul should be applying for a decision making position on the Cider team!

The more that I play with WPF (my real job is still WinForms) the more I’m amazed at how unhelpful the Visual Studio WPF designer (Cider) is. I can’t imagine that anyone actually uses it to layout form content. What’s worse there are even third party tools that seem to do a much quicker and more robust job of previewing XAML markup “as you type”.

I’ve started using Kaxaml in preference to Visual Studio for hand hacking WPF fragments – its just quicker and doesn’t require so many restarts . What was really surprising is that it even does a better job of XAML Intellisense!

Yet there are things that I really yearn for in the WPF designer. Particularly a resource manager – at a simple level just a treeview showing all the resources defined in my application/project. Also a better way of visualizing data binding at a high level would be a great bonus.

XAML Ribbon Bar – Part 1

I’ve been trying to dream up a functional but elegant user interface for my mystery app. At work I’ve been using the DevExpress WinForms control suite and I have to say I’ve been very impressed. Their ribbon bar in particular is pretty swish and seems to be very popular at the moment.

I’d already gone down the path of having a tab control to separate the functional modes in which the application operates. Keeping the UI relatively simple by only giving access to the functions that meet the current mode of operation. But my “out of the box” WPF tab control was looking pretty dull.

So… how to convert the standard WPF controls into something more elegant – something looking a little like a Ribbon Bar perhaps? Well, I must say I had a blast doing this – and finally ended up with a prototype that managed to get reasonably close without having to use anything but XAML. All the magic is done simply using the standard WPF controls (TabControl, ToolBarTray, ToolBar, Button etc.) and some styles and templating.

XAML Ribbon Bar

You can run this XAML file (click here or on image above) to see how far I got with this approach. I tried hard to get the curvy tab headers and to remove the line between the tab and the page but couldn’t come up with a solution in XAML alone that didn’t involve fixing the tab header width. I took the button images out so the file could run stand-alone.

Once all the styles are defined the code to actually mark up the Ribbon Bar is identical to marking up a TabControl with embedded Toolbars – something like:

       <TabControl Name="tabControl1" SelectedIndex="3">
<TabItem Name="tabDream" Header="Dream">
<ToolBarTray>
<ToolBar Header="Holidays" >
<Button HorizontalAlignment="Left">
<StackPanel Orientation="Vertical">
<Image Source="Add.png" Width="24"/>
<TextBlock>New Holiday</TextBlock>
</StackPanel>
</Button>
</ToolBar>
...

Next I’ll tidy up the XAML, its a straight brain dump at the moment and went through several iterations of trial and error – there’s bound to be some unused portions in there still. I’ve also made a very simple Decorator for the TabHeader – but more on that later…