SandRibbon for WPF User Guide

Ribbon Controls

Back to Table of Contents

Control Layout

Most of the ribbon controls you use will be placed within a ribbon group. We have already looked at how to set up a ribbon full of tabs, which are in turn filled with groups. A group lays out its child controls horizontally from left to right, similar to a standard StackPanel except there is always spacing between items. For many situations, this simple horizontal flow of items will be sufficient for your layouts. Of course, you can include standard WPF layout containers too. For example, if you wanted to replicate the Clipboard group layout from Word you would include a button directly in your group followed by a vertically-oriented StackPanel containing a further three buttons.

We offer a few extra layout panels in SandRibbon, two of which we'll discuss later because they are only relevant to dynamic resizing scenarios. You may wish to use StripPanel though, which is very similar to StackPanel only it also supports an ItemSpacing property which automatically applies spacing between child elements. It supports horizontal and vertical orientation.

Buttons

The Button class in SandRibbon is distinct from the Button class in WPF. It displays text and an image next to each other, rather than arbitrary content. It supports three standard sizes: small, medium and large. The sizes dictate the size of the image, whether text is displayed and how the image is aligned next to the text. A large button has a 32x32 image with the text displayed underneath. A medium button (the default size) has a 16x16 image with the text displayed at the side. A small button just has a 16x16 image.

The source of the image for a SandRibbon button can be a bitmap image, a vector image or indeed anything else that can be applied to the Source property of an Image control.

Here's an example. To create the Clipboard group from Microsoft Word we want four buttons, one large, three small and contained within a StackPanel. We'll use images which are already added to our project in an Icons folder.

<sr:RibbonGroup Header="Clipboard">
	<sr:Button Text="Paste" NormalSize="Large" Image="/Icons/paste.png" />
	<StackPanel>
		<sr:Button Text="Cut" Image="/Icons/cut.png" />
		<sr:Button Text="Copy" Image="/Icons/copy.png" />
		<sr:Button Text="Format Painter" Image="/Icons/color.png" />
	</StackPanel>
</sr:RibbonGroup>

Or, in the designer, drag a SandRibbon button control from the toolbox into your group. Set its Text and Image properties appropriately for the Paste button. Then drag a StackPanel from the toolbox into the group. Into this, drag three more button controls and set their Text, Image and NormalSize properties appropriately. The designer will probably have added a bunch of attributes to the StackPanel in order to give it some size when empty; all of these can now be removed.

Buttons raise their Activate event when they are activated by the user. They also have an ActivateMode property, which can be set to Press, Release (default) or Repeat. This specifies at what point the button will be activated while the user is clicking on it with the mouse. If set to Press, the Activate event will be fired as soon as the mouse button is pressed. If set to Release, the button will have to be pressed and then released before the Activate event is fired, which is normal behaviour for a button. If set to Repeat, the Activate event will continue to fire while the mouse button is down - like a scrollbar button.

The ability for buttons to fall into three distinct sizes is useful and normally this mode of usage will be adequate. If you require precise control over button layout, though, you can set AutoSize to false. You then have the ImageSize and LabelAlignment properties to determine the size of the image and how it is shown relative to the text.

Buttons can be checked, and you can use their IsChecked property to get or set this value. Alternatively, you can use the AutoCheck property to define when SandRibbon will change this property for you. Set it to Toggle to have a simple on and off toggle, or Radio so that the button is checked and all its neighbouring buttons unchecked when it is activated.

Although functional buttons can be created just by setting text and image and responding to their Activate events, you should consider using the WPF commanding pattern to control them. A discussion of what commands are and how they work is beyond the scope of this documentation, but SandRibbon fully supports binding its buttons to commands. If you want to create a Cut button, for example, you can simply create a button and bind it to the ApplicationCommands.Cut command. The button will then automatically be fully functional, its text will be populated with the local language, and it will only be enabled when the Cut command is applicable. In this way, you do not even have to write any code, and the only button property you need to specify (other than the command) is its image.

<sr:Button Command="ApplicationCommands.Cut" Image="/Icons/cut.png" />

Popups

A SandRibbon popup is an entity that can be attached to buttons, galleries, menu items or in any situation where a control offers a means to show more information or children in a popup.

The RibbonPopup class represents a popup. They are often assigned to buttons. Once a popup is assigned to a button, the button will usually display a dropdown indicator which can be clicked to show the popup. For an example, let's create a font color button that when clicked, reveals a list of colors to choose from. The popup is assigned to the Popup property of a button, but since we have designated that as the content property, you do not have to assign it explicitly in XAML - you just specify your popup as the child of the button.

<sr:Button Image="/Icons/fontcolor.png" DropDownDisplay="Separate">
	<sr:RibbonPopup>
		<sr:ColorPicker>
			<Color A="255" R="255" G="255" B="255" />
			<Color A="255" R="0" G="0" B="0" />
			<Color A="255" R="255" G="0" B="0" />
			<Color A="255" R="0" G="255" B="0" />
			<Color A="255" R="0" G="0" B="255" />
		</sr:ColorPicker>
	</sr:RibbonPopup>
</sr:Button>

To add a popup to a button in the designer, right-click the button and choose Add Popup. A default popup will be created containing a menu with some menu items.

Note the use of the DropDownDisplay property. This sets how the button makes its popup available. The default value is Integral, which adds the dropdown indicator next to the button and clicking anywhere on the button will show the dropdown, i.e. the only point of the button is to display that dropdown. In the example above, we specified Separate, which means that clicking on the normal area of the button will still activate the button, and only clicking in the area with the dropdown indicator will show the popup. Other values are Hidden, which hides the indicator but still shows the popup when the button is clicked, and None, where the popup is not available at all.

Sometimes you may wish to customize the content of a popup as it is opened. The BeforeOpen event allows just this. It is fired just before the popup opens and you can change the contents of the popup, or even cancel the opening of the popup by setting e.Cancel to true.

Menus, Menu Items and ContextPopups

Most applications will use menus, and SandRibbon menus mimic the menus seen in Office. Unlike regular WPF menus, you need to explicitly define a menu within a popup. This is because SandRibbon allows you to host any kind of control in its popups, not just menu items: your popups might host headings, color pickers or galleries. The Menu control is very simple - it has no properties and is designed simply to contain MenuItem and Separator controls.

The MenuItem class is very similar to the Button class. It is, after all, designed to either be clicked or display a popup full of child items. It has the same Text, Image and ImageSize properties, but no AutoSize property. The size of the image is always determined by its ImageSize property, which defaults to 16x16. It can be checked with the IsChecked property. When a MenuItem has a popup, it displays a dropdown indicator. Normally clicking anywhere on the menu item will show its popup, but you can use the IsSplit property to have the menu item split, so that clicking on its body will activate it but clicking on the dropdown indicator will show the popup. This is similar to the DropDownDisplay property on Button.

You can also choose to show some descriptive text in a menu item. When descriptive text is specified it is justified underneath the main text. Typically this kind of menu item is only used within the application popup, with a large image.

For an example we will create a simple menu system launched from a button, that allows selection of objects.

<sr:Button Text="Show Menu">
	<sr:RibbonPopup>
		<sr:Menu>
			<sr:MenuItem Text="Machinery" />
			<sr:MenuItem Text="Livestock" />
			<sr:MenuItem Text="Vegetables" />
			<sr:MenuItem Text="Fruit">
				<sr:RibbonPopup>
					<sr:Menu>
						<sr:MenuItem Text="Oranges" />
						<sr:MenuItem Text="Apples" />
						<sr:MenuItem Text="Bananas" />
						<sr:MenuItem Text="Melons" />
					</sr:Menu>
				</sr:RibbonPopup>
			</sr:MenuItem>
		</sr:Menu>
	</sr:RibbonPopup>
</sr:Button>

Unfortunately the Visual Studio 2010 designer does not allow designing within popups, so you will have to fall back to specifying SandRibbon menu systems with XAML, but this is almost as easy as using the designer.

Note that like a Button, a MenuItem can also be bound to a WPF command. When bound, its text and enabled state come from the command automatically, and activating the menu item activates the command. If you are not binding to commands you can still consume the Activate event yourself.

Context Popups

Context popups are just like context menus, only because they're part of SandRibbon they can do much more than just show menu items. You generally defined a ContextPopup as part of your Window's resources, so it can be referred to later on as you bind it to the controls on which it should appear. Because context popups are defined in resources, there is no designer experience for them and they must be written in XAML. To define a simple context menu displaying clipboard commands you might do the following:

<Window.Resources>
	<ResourceDictionary>
		<sr:ContextPopup x:Key="editContextMenu">
			<sr:Menu>
				<sr:MenuItem Text="C_ut" Command="ApplicationCommands.Cut" />
				<sr:MenuItem Text="_Copy" Command="ApplicationCommands.Copy" />
				<sr:MenuItem Text="_Paste" Command="ApplicationCommands.Paste" />
			</sr:Menu>
		</sr:ContextPopup>
	</ResourceDictionary>
</Window.Resources>

To attach your context menu to a control, use the attached ContextPopup.Popup property. You can still use the BeforePopup event of your popup to customize the display of the popup at the last moment before it is shown.

<RichTextBox sr:ContextPopup.Popup="{StaticResource editContextMenu}">
</RichTextBox>

Galleries

Galleries are a very powerful user interface concept new to Office 2007. They are basically a scrollable list of buttons, where the host application can draw the button contents dynamically. The idea is that each button shows the result of clicking it visually, rather than a static icon that indicates its purpose. Galleries are always used in popups.

Because galleries are scrollable, you should always specify the ViewportWidth and ViewportHeight properties. These control the size of the viewport, within which all items are scrolled. The size of the items themselves is controlled with the ItemWidth and ItemHeight properties. You will usually specify values for all these properties, although you could stick with the defaults or allow each item to size itself. However, it is faster (and more visually consistent) to specify a constant size for items.

The host application normally takes responsibility for drawing the contents of gallery buttons. It can do this easily by simply assigning an image to their Image property, or go the advanced route by responding to the DrawButton event of the gallery. In this event handler you are passed the button that needs to be drawn, a DrawingContext for performing drawing and the size of the area in which to draw. This technique assumes some basic knowledge of how to use DrawingContext to draw WPF primitives - this is documented well in MSDN.

When you click the dropdown indicator next to the Underline button in Word, a popup that includes a gallery is shown. The gallery contains a number of items designed to show you the various styles of underline you can use. This is an example of duplicating that gallery with native drawing:

<sr:Button Image="/Icons/underline.png" DropDownDisplay="Separate">
	<sr:RibbonPopup>
		<sr:Gallery ViewportWidth="157" ViewportHeight="184" ItemWidth="156" ItemHeight="26"
		DrawButton="DrawUnderlineGalleryButton">
			<sr:GalleryButton />
			<sr:GalleryButton />
			<sr:GalleryButton />
			<sr:GalleryButton />
			<sr:GalleryButton />
			<sr:GalleryButton />
			<sr:GalleryButton />
		</sr:Gallery>
	</sr:RibbonPopup>
</sr:Button>

This is how you define a gallery, its viewport size and item size, and give it some items. If you ran your program at this point all the items would of course be blank. We still need to write some code for the DrawUnderlineGalleryMethod, that we have specified should draw the buttons. For this example we will draw a simple line, no matter which button is being drawn. In practice you would draw differently depending on which button was being drawn.

private void DrawUnderlineGalleryButton(object sender, DrawGalleryButtonEventArgs e)
{
	Pen pen = new Pen(Brushes.Black, 1.5);
	e.DrawingContext.DrawLine(pen, new Point(e.Bounds.X + 5, e.Bounds.Y + e.Bounds.Height / 2),
		new Point(e.Bounds.Right - 5, e.Bounds.Y + e.Bounds.Height / 2));
}

Popups, or more specifically galleries, can optionally be user-resizable. For a popup to be resizable it must contain a gallery, and have its ResizeMode property set. You can choose for a gallery to be resizable vertically only, or both horizontally and vertically. Galleries that contain many small items that are wrapped so there are many per line benefit from being resizable in both dimensions, whereas a list of items laid out vertically (like our underlines) is best left resizable vertically only. Once a popup has been designated as resizable it gains a gripper at the bottom.

The items in galleries can be divided into groups by setting the GroupItems property on the Gallery to true. When grouping is active, each GroupButton within the gallery must have its GroupName property set. SandRibbon sorts the items into their groups and displays headers in the list.

Ribbon Galleries

A RibbonGallery is a special kind of gallery designed to be hosted directly in a ribbon. Like a normal gallery it has viewport size and item size, but normally the viewport is constrained to the size of the ribbon itself, and the user can drop down the gallery to view the full list of items. The method for owner drawing items is exactly the same. A ribbon gallery can also condense itself automatically when space is tight - for more information see the section on dynamic resizing.

The user can only drop down a ribbon gallery when it has a popup associated with it. In the popup you might include a menu that has other options concerning the gallery. The items in the ribbon gallery are automatically moved into the popup when the popup is displayed, so even if you just specify a menu, the first item in the popup will be a gallery. While the items are hosted in the ribbon you can page up and down smoothly using the buttons beside the gallery.

To create a ribbon gallery containing shapes you might do something like this.

<sr:RibbonGallery ItemWidth="72" ItemHeight="56" Text="Shapes" Image="/Icons/users.png" DrawButton="DrawShapeGalleryButton">
	<sr:RibbonGallery.Popup>
		<sr:RibbonPopup ResizeMode="Vertical">
			<sr:Menu>
				<sr:MenuItem Text="More Options..." />
			</sr:Menu>
		</sr:RibbonPopup>
	</sr:RibbonGallery.Popup>
	<sr:GalleryButton />
	<sr:GalleryButton />
	<sr:GalleryButton />
	<sr:GalleryButton />
	<sr:GalleryButton />
	<sr:GalleryButton />
	<sr:GalleryButton />
</sr:RibbonGallery>
private void DrawShapeGalleryButton(object sender, DrawGalleryButtonEventArgs e)
{
	Pen pen = new Pen(Brushes.Black, 1.5);
	e.DrawingContext.DrawEllipse(Brushes.Orange, pen,
		new Point(e.Bounds.X + e.Bounds.Width / 2, e.Bounds.Y + e.Bounds.Height / 2),
		e.Bounds.Width / 2 - 6, e.Bounds.Height / 2 - 6);
}

Notice that we have specified text and an image for the ribbon gallery, even though text and image are not displayed by default. You should always specify text and an image, because in some situations the ribbon gallery can condense itself to button form (if dynamic resizing is enabled) and you'll need to click the button to drop down the gallery.

Combo Boxes

A ribbon combo box is simpler than the standard combo box that ships with WPF. Designed to be hosted on a ribbon, it is really just a dropdown gallery that allows one item to be selected and displayed in the box. You can also enter text directly. It is populated with instances of ComboBoxItem, which is a special type of GalleryButton (which we have already discussed in the section on galleries).

You may wish to owner draw the items in your combo boxes, but you can also just specify text as you would for any other button. Because ComboBox derives from Selector, it has the concept of a selected item within its list of items. The text for this item is displayed in the box itself and is editable if you set the IsEditable property to true. You can respond to the SelectionChanged event to respond to changes in the selection.

<sr:ComboBox DropDownHeight="150" Width="100">
	<sr:ComboBoxItem Text="Apple" />
	<sr:ComboBoxItem Text="Orange" />
	<sr:ComboBoxItem Text="Banana" />
	<sr:ComboBoxItem Text="Grape" />
	<sr:ComboBoxItem Text="Peach" />
	<sr:ComboBoxItem Text="Pineapple" />
	<sr:ComboBoxItem Text="Kiwi" />
	<sr:ComboBoxItem Text="Lemon" />
	<sr:ComboBoxItem Text="Melon" />
	<sr:ComboBoxItem Text="Grapefruit" />
	<sr:ComboBoxItem Text="Stawberry" />
	<sr:ComboBoxItem Text="Lime" />
</sr:ComboBox>

Color Pickers

The ColorPicker class is a simple primitive control that contains a list of colors. We encountered it before in the example for popups. Colors are added to its Colors collection, and it has just one property, ShowGraduations, that controls whether variations of the specified colors are also available for selection by the user. The control internally generates instances of the ColorButton class which live inside the ColorPicker.

The ColorPicked event is raised when the user chooses a color. The method handling the event is passed event arguments containing the color that was picked.

Shown on the right is a popup containing a menu, a heading and a color picker. The color picker is showing graduations.

Next: Extending the Ribbon