Click here to Skip to main content
Email Password   helpLost your password?

intro.png

Introduction

The Odyssey RibbonBar is the next WPF control to be added to the Odyssey Control Library, available on CodePlex.

Background

After the BreadcrumbBar, ExplorerBar, and OutlookBar, which I previously introduced at CodeProject, the Odyssey RibbonBar is the next control included in the Odyssey library. I'm aware of Microsoft's WPF RibbonBar which is available as a fully functional preview and will be added to .NET 4.0. So, you may wonder why I wrote my own RibbonBar anyway. The answer is, it's fun and interesting to do this. And, I also plan to build a Silverlight version. Since the RibbonBar is a set of various controls, it would fill a whole book to explain every detail, so I reduced it to the most important issues of the RibbonBar. The demo application contains the XAML for a RibbonBar that demonstrates most of the features. I did not create a demo that does something useful, nor does it have useful button images or labels. The images I use for the demo are the free images available at www.glyfx.com. The RibbonBar contains tabs that contain groups. Each group can contain any possible control. However, dynamic sizing is only applied when it contains IRibbonControls. Currently, there are the following IRibbonControls:

The RibbonBar hosts an ApplicationMenu and a QuickAccessToolbar which can be placed at the top or at the bottom, as well as a collection of RibbonTabItems:

<odc:RibbonTabItem Title="Contextual Tabs">
    <odc:RibbonGroup Title="Select">
        <odc:RibbonButton Content="Context #1" 
            LargeImage="img/paste32.png" 
            odc:RibbonBar.MinSize="Large" 
            Click="Context1Click" />
        <odc:RibbonButton Content="Context #2" 
            LargeImage="img/mail32.png" 
            odc:RibbonBar.MinSize="Large" 
            Click="Context2Click" />
        <odc:RibbonButton Content="Off" 
            LargeImage="img/delete32.png" 
            odc:RibbonBar.MinSize="Large" 
            Click="ContextOffClick" />
    </odc:RibbonGroup>
</odc:RibbonTabItem>

Each of these controls can have up to three possible RibbonSizes: Large, Medium, and Small, except RibbonGallery which can have unlimited sizes. While buttons usually have three possible sizes. The RibbonBar automatically detects the optimal size depending on the width of the RibbonBar, which I call AutoReduction.

The reduction works in the following way:

First, all groups are assumed to have an unlimited width. If the sum of widths of all groups exceeds the available width, each group gets reduced by one level, beginning from the last group up to the first, until the sum finally fits into the width. This is repeated unless no group can get reduced. The number of levels a group can be reduced depends on the controls inside a group. If there is any IRibbonControl inside, the group can have four reduction levels: Large, Medium, Small, and Collapsed.

reduction.png

RibbonGroup

RibbonGroup.png

A RibbonGroup can host any kind of control, but IRibbonControls are preferred to enable different sizing of the groups.

<odc:RibbonButtonGroup>
    <odc:RibbonButton SmallImage="img/home16.png"/>
    <odc:RibbonButton SmallImage="img/history16.png"/>
    <odc:RibbonButton SmallImage="img/favorites16.png"/>
    <odc:RibbonButton SmallImage="img/mail16.png"/>
</odc:RibbonButtonGroup>

A group is reduced in the following way: All controls inside a group are grouped into buckets. Each new bucket begins when there is a control which is not reducible and has a height that is greater than the possible height to fit into three rows. After the buckets are determined, the layout depends on the number of controls inside a bucket. If there are three controls, then all of them are reduced to medium or small, so that they fit into one column. If there are four, the first remains large and fills one complete column, while the other three will fill another column. With five elements, the first two will remain large and each of it will occupy one column, while the remaining three will fit into the last column in three different rows. If there are six, the first three are placed into the first column in three different rows, while the last three are placed into the second column.

reduction2.png

reduction3.pngreduction4.png

It is possible to modify this reduction. You can attach a Reduction property to a control which describes the size of the control for each group level. Using the Reduction property also allows to modify the group to have various possible levels:

<odc:RibbonButton Content="Button 4" LargeImage="img/paste32.png" 
   SmallImage="img/paste16.png" 
   odc:RibbonBar.Reduction="Large,Large,Large,Medium"/>

customreduction.png

You can also set the MinSize and MaxSize attached properties to a control to force it to have a size not bigger than MaxSize and/or not smaller than MinSize:

<odc:RibbonButton Content="Windows 7" odc:RibbonBar.MinSize="Large" 
    SmallImage="img/save16.png" LargeImage="img/Save32.png" Click="Win7Click"/>
<odc:RibbonButton Content="Office Blue" odc:RibbonBar.MinSize="Medium" 
    SmallImage="img/home16.png" LargeImage="img/home32.png" Click="OfficeBlueClick"/>
 
<odc:RibbonButton odc:RibbonBar.MaxSize="Medium" Content="Home" 
    SmallImage="img/home16.png" LargeImage="img/home32.png"/>
<odc:RibbonButton odc:RibbonBar.MaxSize="Medium" Content="Paste" 
    SmallImage="img/paste16.png" LargeImage="img/paste32.png"/>

Furthermore, you can also modify the reduction order of the groups, by specifying the ReductionOrder property for a RibbonTabItem. The ReductionOrder is a list of group names. When the TabItem needs to reduce groups, it starts with the first group in the list, instead of the last group in its Group collection:

<odc:RibbonTabItem Title="Tab 1" ReductionOrder="grp2,grp1,grp3,grp4,grp3a">

A RibbonGroup can also contain a Launcher button on the bottom right. As soon as this button is clicked, an ExecuteLauncher RoutedEvent is fired:

<odc:RibbonGroup Title="Skin" Image="img/home16.png" IsDialogLauncherVisible="True" 
    ExecuteLauncher="RibbonGroup_LaunchDialog">

The C# code:

private void RibbonGroup_LaunchDialog(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Launcher");
}

IRibbonButton

RibbonButton, RibbonToggleButton, RibbonDropDownButton, and RibbonSplitButton implement IRibbonButton so they have three different visual states. If set to large, the text for the button is split into two lines if possible, which means the text must contain at least one space. In medium mode, the button's text has only one line, and in small mode, the text of the button is hidden. To each button, a LargeImage and SmallImage property can be attached. The LargeImage is shown when the button is in large state, and the SmallImage, when the button is in medium or small state. These properties are attached from RibbonBar and specify an ImageSource:

<odc:RibbonButton Content="Windows 7" odc:RibbonBar.MinSize="Large" 
   SmallImage="img/save16.png" LargeImage="img/Save32.png" Click="Win7Click"/>

While LargeImage is visualized with 32x32 pixels, SmallImage is visualized with 16x16 pixels. So the ImageSource should have these sizes. However, the image is scaled to the appropriate size. You can modify how the image is scaled with the ImageStretch attached property of RibbonButton:

/// <summary>
/// Gets or sets how to stretch an image inside an IRibbonButton
/// This is an attached dependency property.
/// </summary>
public static Stretch GetImageStretch(DependencyObject obj)
{
    return (Stretch)obj.GetValue(ImageStretchProperty);
}
 
public static void SetImageStretch(DependencyObject obj, Stretch value)
{
    obj.SetValue(ImageStretchProperty, value);
}
 
// Using a DependencyProperty as the backing store for ImageStretch.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageStretchProperty =
DependencyProperty.RegisterAttached("ImageStretch", 
    typeof(Stretch), typeof(RibbonButton), 
    new UIPropertyMetadata(Stretch.Uniform));

RibbonGallery

RibbonGallery.png

The gallery is derived from ListBox and hosts RibbonThumbnails. A RibbonThumbnail displays images of any size. Depending on the size of the image, the gallery has different rows in the in-ribbon gallery.

<odc:RibbonGallery x:Name="gallery" odc:RibbonGallery.Stretch="None" DropDownColumns="5">
    <odc:RibbonGallery.ThumbnailSize>
        <Size Width="36" Height="36"/>
    </odc:RibbonGallery.ThumbnailSize>
    <odc:RibbonThumbnail x:Name="thumb1" 
       ImageSource="img/cut16.png" odc:RibbonGallery.Stretch="None"/>
    <odc:RibbonThumbnail x:Name="thumb2" ImageSource="img/delete16.png"/>
    <odc:RibbonThumbnail ImageSource="img/favorites16.png"/>
    <odc:RibbonThumbnail ImageSource="img/folder16.png"/>
    <odc:RibbonThumbnail ImageSource="img/history16.png"/>
    <odc:RibbonThumbnail ImageSource="img/home16.png"/>
    <odc:RibbonThumbnail ImageSource="img/mail16.png"/>
    <odc:RibbonThumbnail ImageSource="img/paste16.png"/>
    <odc:RibbonThumbnail ImageSource="img/props16.png"/>
    <odc:RibbonThumbnail ImageSource="img/save16.png"/>
    <odc:RibbonThumbnail ImageSource="img/search16.png"/>
    <odc:RibbonThumbnail ImageSource="img/undo16.png"/>
</odc:RibbonGallery>

The RibbonGallery also has the HotItem property which is a dependency property which specifies the thumbnail that is currently under the mouse pointer, so you can implement a preview together with the HotThumbnailChanged RoutedEvent. The demo application demonstrates the usage of HotItem using a binding in the XAML:

<TextBlock Text="{Binding HotItem, ElementName=gallery, 
                 Converter={StaticResource ThumbnailConverter}}"/>

The RibbonGallery can have various reduction levels. You can specify the number of columns for every level:

<odc:RibbonGallery x:Name="gallery2" odc:RibbonGallery.Stretch="None" 
    odc:RibbonGallery.ReductionColumns="12,11,10,9,8,7,6,5,4,3">

Since the RibbonGallery is derived from ListBox, it allows grouping for the drop down list using GroupStyle. I won't go into the details since this is a standard feature, but together with ItemsPanel, DropDownHeader, and DropDownFooter, you can modify the dropdown listbox in unlimited ways.

RibbonDropDownButton and RibbonSplitButton

RibbonDropDownButton.pngRibbonSplitButton.png

Both controls offer a drop down list to expose additional items. They are derived from Items HeaderedItemsControl so it is possible to use a data source for the items. An item can be any type, to enable a rich variety to build the drop down list, but usually it would contain RibbonMenuItems:

<odc:RibbonDropDownButton odc:RibbonBar.MinSize="Medium" Content="Drop Down" 
        SmallImage="img/folder16.png" LargeImage="img/folder32.png">
    <odc:RibbonMenuItem Header="Enable Glass" Image="img/search16.png" IsCheckable="True" 
        IsChecked="{Binding IsGlassEnabled,ElementName=window}"/>
    <odc:RibbonMenuItem Header="Item 2" Image="img/cut16.png"/>
    <odc:RibbonMenuItem Header="Item 3" Image="img/cut16.png"/>
</odc:RibbonDropDownButton>

It's also possible to specify a DropDownHeader and DropDownFooter for the drop down list, together with DropDownHeaderTemplate and DropDownFooterTemplate:

<odc:RibbonDropDownButton.DropDownHeader>
    <TextBlock Text="Custom Header" Background="Orange"/>
    </odc:RibbonDropDownButton.DropDownHeader>
    <odc:RibbonDropDownButton.DropDownFooter>
        <TextBlock Text="Custom Footer" Background="Lime"/>
    </odc:RibbonDropDownButton.DropDownFooter>
</odc:RibbonDropDownButton>

RibbonComboBox and RibbonTextBox

RibbonComoBox.png

They are derived from ComboBox and TextBox, and have additional Image and Title properties to specify a 16x16 image and a text for the control. These controls can have two different sizes. A medium size with Image, Title, and the control itself visible, and a small size with only Image and the control. Like RibbonDropDownButton and RibbonSplitButton, RibbonCombobox also has a DropDownHeader and DropDownFooter property together with their template properties.

The RibbonComboBox Items usually are RibbonComboBoxItems:

<odc:RibbonComboBox Title="ComboBox" Image="img/history16.png" ContentWidth="100">
    <odc:RibbonComboBoxItem Content="Item 1" Image="img/mail16.png" />
    <odc:RibbonComboBoxItem Content="Item 2" Image="img/props16.png"/>
    <odc:RibbonComboBoxItem Content="Item 3"/>
    <odc:RibbonComboBoxItem Content="Item 4"/>
</odc:RibbonComboBox>

RibbonSeparator

The purpose of RibbonSeparator is to attach RibbonBar.Reduction on to it, so it might be possible to hide the Separator if its size is determined to be RibbonSize.Small.

Skins

The RibbonBar currently has four different skins:

Window 7 / Vista Skin

VistaTheme.png

Black Skin

BlackSkin2.png

You can set the skin for the application at any time using SkinManager:

private void Win7Click(object sender, RoutedEventArgs e)
{
    SkinManager.SkinId = SkinId.Windows7;
}
 
private void OfficeBlueClick(object sender, RoutedEventArgs e)
{
    SkinManager.SkinId = SkinId.OfficeBlue;
}

However, you can also create your own ResourceDictionary that overrides the ComponentResourceKeys that enables skinning. This is described later.

ApplicationMenu

The RibbonApplicationMenu is a separate control. It is derived from ItemsControl to host RibbonApplicationMenuItems. The difference between RibbonApplicationMenuItem and RibbonMenuItem is only the size. As soon as a RibbonApplicationMenuItem has sub items, they are opened in a popup container that fills the space of the RecentList content, like shown below:

ApplicationMenu.png

The RecentList property of the ApplicationMenu is of type object so it can contain any control to build the list of recent items.

QuickAccessToolbar

QAToolbar.png

The RibbonQAToolbar is derived from ItemsControl and usually contains IRibbonButtons and/or RibbonMenuItems. Depending on these two groups, the item is either visible in the toolbar if it is of type IRibbonButton, or in the drop down menu that appears when you click the drop down button on the right:

<odc:RibbonQAToolBar>
    <odc:RibbonButton SmallImage="img/save16.png"/>
    <odc:RibbonButton SmallImage="img/undo16.png"/>
    <odc:RibbonButton SmallImage="img/delete16.png"/>
    <odc:RibbonButton SmallImage="img/folder16.png"/>
    <odc:RibbonToggleButton Content="Enable Glass" 
        odc:RibbonBar.MinSize="Large" SmallImage="img/search32.png" 
        IsChecked="{Binding IsGlassEnabled, ElementName=window}"/> 
    <odc:RibbonButton SmallImage="img/props16.png"/>
    <odc:RibbonMenuItem Image="img/props16.png" 
       Header="Show Below the Ribbon" Click="ShowBelowClick"/>
    <odc:RibbonMenuItem Image="img/props16.png" 
       Header="Show Above the Ribbon" Click="ShowAboveClick"/>
    <Separator/>
    <odc:RibbonMenuItem Header="Minimize Ribbon" 
        IsCheckable="True" 
        IsChecked="{Binding CanMinimize, ElementName=ribbonBar, Mode=TwoWay}"/> 
</odc:RibbonQAToolBar>

You can place the toolbar either at the top or at the bottom:

PlacementBtm.png

You can set the RibbonBar.ToolbarPlacement property to specify the placement:

private void ShowBelowClick(object sender, RoutedEventArgs e)
{
    ribbonBar.ToolbarPlacement = QAPlacement.Bottom;
}

Contextual Tab Sets

ContextualTabs2.png

Contextual Tab Sets contain additional RibbonTabItems that only appear on demand by setting the ContextualTabSet property of the RibbonBar:

private void Context2Click(object sender, RoutedEventArgs e)
{
    ribbonBar.ContextualTabSet = ribbonBar.ContextualTabSets[1];
}

A RibbonContextualTabSet can contain various RibbonTabItems:

<odc:RibbonBar.ContextualTabSets>
    <odc:RibbonContextualTabSet Title="Context #1" Color="Red">
        <odc:RibbonTabItem Title="Gallery">
            <odc:RibbonGroup Title="Large Gallery" Image="img/undo16.png" >
                <odc:RibbonGallery x:Name="gallery2" odc:RibbonGallery.Stretch="None" 
                    odc:RibbonGallery.ReductionColumns="12,11,10,9,8,7,6,5,4,3">
                <odc:RibbonGallery.ThumbnailSize>
                    <Size Width="68" Height="68"/>
                </odc:RibbonGallery.ThumbnailSize>
                <odc:RibbonThumbnail ImageSource="img/cut32.png" />
                <odc:RibbonThumbnail ImageSource="img/delete32.png"/>
                <odc:RibbonThumbnail ImageSource="img/favorites32.png"/>
                <odc:RibbonThumbnail ImageSource="img/folder32.png"/>
                <odc:RibbonThumbnail ImageSource="img/history32.png"/>
                <odc:RibbonThumbnail ImageSource="img/home32.png"/>
                <odc:RibbonThumbnail ImageSource="img/mail32.png"/>
                <odc:RibbonThumbnail ImageSource="img/paste32.png"/>
                <odc:RibbonThumbnail ImageSource="img/props32.png"/>
                <odc:RibbonThumbnail ImageSource="img/save32.png"/>
                <odc:RibbonThumbnail ImageSource="img/search32.png"/>
                <odc:RibbonThumbnail ImageSource="img/undo32.png"/>
            </odc:RibbonGallery>
        </odc:RibbonGroup>
 
    </odc:RibbonTabItem>
    <odc:RibbonTabItem Title="Tab 1b">
 
    </odc:RibbonTabItem>
</odc:RibbonContextualTabSet>
    <odc:RibbonContextualTabSet Title="Context #2" Color="Lime">
        <odc:RibbonTabItem Title="Tab 2">
 
        </odc:RibbonTabItem>
 
    </odc:RibbonContextualTabSet>
</odc:RibbonBar.ContextualTabSets>

RibbonButtonGroup

RibbonButtonGroup.png

A RibbonButtonGroup is meant to group IRibbonButtons together which is visualized with a group border. If placed in a RibbonButtonGroup, the only allowed size of the button is RibbonSize.Small which means that only the SmallImage is displayed.

<odc:RibbonGroup Title="Button Groups" Image="img/favorites16.png" 
    odc:RibbonBar.Reduction="Large,Large,Minimized" >
    <odc:RibbonFlowGroup>
        <odc:RibbonButtonGroup>
            <odc:RibbonToggleButton SmallImage="img/cut16.png"/>
            <odc:RibbonToggleButton SmallImage="img/delete16.png"/>
            <odc:RibbonToggleButton SmallImage="img/paste16.png"/>
        </odc:RibbonButtonGroup>
        <odc:RibbonButtonGroup>
            <odc:RibbonButton SmallImage="img/home16.png"/>
            <odc:RibbonButton SmallImage="img/history16.png"/>
            <odc:RibbonButton SmallImage="img/favorites16.png"/>
            <odc:RibbonButton SmallImage="img/mail16.png"/>
            </odc:RibbonButtonGroup>
         
        <odc:RibbonButtonGroup>
            <odc:RibbonButton SmallImage="img/search16.png"/>
            <odc:RibbonDropDownButton SmallImage="img/undo16.png"/>
            <odc:RibbonButton SmallImage="img/folder16.png"/>
            <odc:RibbonSplitButton SmallImage="img/props16.png"/>
            <odc:RibbonButton SmallImage="img/save16.png"/>
        </odc:RibbonButtonGroup>
        <odc:RibbonButtonGroup>
            <odc:RibbonButton SmallImage="img/search16.png"/>
        </odc:RibbonButtonGroup>
    </odc:RibbonFlowGroup>
</odc:RibbonGroup>

RibbonFlowGroup

This is is used as a container for RibbonButtonGroups. It can have two possible states: 2rows and 3rows:

RibbonFlowGroup1.pngRibbonFlowGroup2.png

In 2rows mode, the order of the RibbonButtonGroups is the order in the collection. In 3rows mode, however, the order is determined depending on the width of a group. The algorithm optimizes the order in that way, that the RibbonFlowGroup has the smallest possible width. This is solved by first determining the three largest groups and placing them in column one where the first row has the largest group, and the third the least. Now, for the second column, the order of the row is the opposite to the former column.

RibbonWindow

Finally, some words about RibbonWindow which is derived from the Window class. You must have noticed that the QuickAccessToolbar (QAT) and the contextual tab sets appear inside the window's title bar. This is possible using the RibbonWindow instead of the Window class. RibbonWindow hijacks the Windows messages to allow painting inside the non-client area of the window. It also uses DwmExtendFrameIntoClientArea to set the glass effect to the title bar if Aero is enabled and IsGlassEnabled is set to true.

RibbonWindow1.png

In this case, the Windows buttons on the right are the original Windows buttons. However, if Glass is turned off, the window must paint its own buttons:

RibbonWnd1.png

Internals

If a RibbonButton has a large size, its text is separated into two lines if it contains a space. This is implemented by using a ValueConverter and a ContentControl/TextBlock pair for each line. If you're asking why not two TextBlocks, the answer is, so it is possible to use any type for the button's content, and not just string. The ValueConverter recognizes this and returns the original value for the first line if the value to convert is not a string. The code part looks like this:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="12"/>
        <RowDefinition Height="12"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="content2" Grid.Row="0"
        Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent},
              Converter={StaticResource twoLineTextConverter},ConverterParameter=1}"
        VerticalAlignment="Center" 
        HorizontalAlignment="Center" SnapsToDevicePixels="True" />
    <StackPanel Orientation="Horizontal" Grid.Row="2" 
        HorizontalAlignment="Center" VerticalAlignment="Bottom">
         <TextBlock Grid.Row="1"
            Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent},
                Converter={StaticResource twoLineTextConverter},ConverterParameter=2}"
                VerticalAlignment="Bottom" HorizontalAlignment="Center" 
                SnapsToDevicePixels="True"/>
 
        <Image 
            Source="{DynamicResource {ComponentResourceKey odc:Skins, DownArrowImage}}" 
                Margin="2,0,2,0" Stretch="None" 
                SnapsToDevicePixels="True"/>
    </StackPanel>
</Grid>

The C# code:

public class TwoLineTextConverter : IValueConverter
{
#region IValueConverter Members
 
/// <summary>
/// Splits a string into two lines which have almost the same length if possible.
/// </summary>
/// <param name="value">The string to split. If this type
/// is not a string, the value is returned directly.</param>
/// <param name="parameter">Specifies the line number to return.
/// The value must be either (int)1 or (int)2.</param>
/// <returns>The first or second line of the string, otherwise value.</returns>
public object Convert(object value, Type targetType, object parameter, 
                      System.Globalization.CultureInfo culture)
{
    int line;
    try
    {
        line = int.Parse(parameter as string);
    }
    catch
    {
        throw new ArgumentException("parameter must be either 1 or 2.");
    }
    string s = value as string;
    if (s == null) return line == 1 ? value : null;
 
    int l = SplitIn2Lines(s);
    if (l == 0) return line == 1 ? s : null;
 
    switch (line)
    {
        case 1: return s.Substring(0, l).Trim();
        case 2: return s.Substring(l + 1).Trim();
        default: throw new ArgumentException("parameter must be either 1 or 2.");
    }
}

The Skins

For skinning, I use ComponentResourceKeys to change the appearance of a control. A ComponentResourceKey is similar to a ResourceKey except that it contains a class type and an identifier. Defining a component resource key looks like this:

<SolidColorBrush 
  x:Key="{ComponentResourceKey odc:Skins, PopupContainerBgBrush}" Color="White"/>

And using the key in a XAML looks like this:

<Border 
    Background="{DynamicResource {ComponentResourceKey odc:Skins, PopupContainerBgBrush}}" 
    BorderBrush="{DynamicResource {ComponentResourceKey odc:Skins, 
        {ComponentResourceKey odc:Skins, RibbonBorderBrush}}}" 
    Padding="1" CornerRadius="2" BorderThickness="1" >
    <DockPanel>
        <Border x:Name="title" DockPanel.Dock="Top" Height="24" Background="#FFDDE7EE" 
            BorderBrush="{DynamicResource {ComponentResourceKey odc:Skins, 
                {ComponentResourceKey odc:Skins, RibbonBorderBrush}}}" 
            BorderThickness="0,0,0,1" Padding="4">
            <ContentControl Content="{TemplateBinding SubMenuTitle}" 
                VerticalAlignment="Center" HorizontalAlignment="Center"/>
        </Border>
        <ScrollViewer CanContentScroll="True" VerticalScrollBarVisibility="Auto" 
            HorizontalScrollBarVisibility="Disabled">
            <ItemsPresenter ScrollViewer.VerticalScrollBarVisibility="Visible" 
                ScrollViewer.CanContentScroll="True"/>
        </ScrollViewer>
    </DockPanel>
</Border>

Since the template use DynamicResource instead of StaticResource, the resource can be modified at any time. The RibbonManager loads a ResourceDictionary and adds it to the MergedDictionaries of the application:

private static void ApplySkin(SkinId skin)
{
    var dict = Application.Current.Resources.MergedDictionaries;
    dict.Remove(OfficeBlack);
    dict.Remove(OfficeSilver);
    dict.Remove(WindowsSeven);
    switch (skin)
    {
        case SkinId.OfficeBlack:
            dict.Add(OfficeBlack);
            break;
 
        case SkinId.OfficeSilver:
            dict.Add(OfficeSilver);
            break;
 
        case SkinId.Windows7:
            dict.Add(WindowsSeven);
            break;
    } 
}

The overlapping of the ApplicationMenu button: when you open up the application menu, you'll notice that the menu button is still at the top of the popup control, and you may wonder how this is possible. Well, the trick is to use two buttons: one at the Ribbon and one at the Popup. When the Popup opens, the button on it needs to be placed to have the same screen location as the button on the Ribbon:

protected virtual void OnPopupOpened(object sender, EventArgs e)
{
    AdjustApplicationButtons();
    IsOpen = true;
}
 
/// <summary>
/// Ensures that both ApplicationMenu buttons are at the same screen location:
/// </summary>
private void AdjustApplicationButtons()
{
    if (appButtonClone != null && appButton != null)
    { 
        Point p = appButton.PointToScreen(new Point());
        Point p2 = appButtonClone.PointToScreen(new Point());
 
        double dx = p2.X - p.X;
        double dy = p2.Y - p.Y;
        appButtonClone.Visibility = 
          dy >= -20 ? Visibility.Visible : Visibility.Hidden;
        Thickness t = appButtonClone.Margin;
        appButtonClone.Margin = new Thickness(t.Left-dx, t.Top-dy, 0.0, 0.0);
    }
}

Hosting items from ItemsControl in a separate panel: If you try to change the parent of an Item from a derived ItemsControl, there would be an exception, that the control already has a logical parent. The trick is to add it to a custom panel that overrides CreateUIElementCollection:

/// <summary>
/// Overriding this to enable a templated parent to add or remove children to 
/// this panel within OnMeasureOverride or somewhere else.
/// </summary>
protected override UIElementCollection 
  CreateUIElementCollection(System.Windows.FrameworkElement logicalParent)
{
    return new UIElementCollection(this, 
              (base.TemplatedParent == null) ? logicalParent : null);
}

When the panel is used in a template, which means TemplatedParent != null, it tells the element collection to have no logical parent. Thus, adding a control to this panel is possible, since the logical parent will not change.

Not Implemented/ To be Available on Future Releases

The Odyssey RibbonBar will be migrated to the available Odyssey Control Library available at www.codeplex.com/odyssey.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralWorks nicely with Caliburn actions!
Nic_Roche
15:03 10 Mar '10  
Great work Thomas.

I just finished a small POC and it appears that your RibbonToggleButton (and I assume other items) works well with Caliburn actions, whereas MS RibbonControlLibrary not so much...

This helps, thankyou.

Nic
GeneralMissing label text on buttons
kimic
9:32 1 Mar '10  
Hi, the ribbon environment looks very nice.
i was trying to use it, but i have run into this problem:
if i place a ribbon button to the ribbon button group on new blank ribbonwindow, i set the icon and content text, yet in the design and in the runtime the caption text on the button is not visible.
i have tried to size the button, but the most i have managed is that if i change small size to medium, the caption text is visible in design, but if i hit reload F6 or try runtime, the button caption disappears.
same result is if i use the ribbon button without icon image.
maybe i am missing in the parent window settings but i think the button caption text should be visible at all times unless the size is set to small

sample code:
<odc:RibbonButtonGroup>
<odc:RibbonButton Name="bOk" Content="Ok" Width="80" Height="25" SmallImage="../img/save16.png" LargeImage="../img/save32.png" odc:RibbonBar.MinSize="Medium" odc:RibbonBar.Size="Medium" />
<odc:RibbonButton Name="bCancel" Content="Cancel" Width="80" Height="25" SmallImage="../img/delete16.png" LargeImage="../img/delete32.png" odc:RibbonBar.MinSize="Medium" odc:RibbonBar.Size="Medium" />
</odc:RibbonButtonGroup>
GeneralRe: Missing label text on buttons
kimic
10:02 1 Mar '10  
ok so i downloaded the latest source for odyssey and replaced the ribboncontrolgroup with ribbonwrappanel and it works ok now
GeneralThank you
gs_Bakshi@hotmail.com
14:43 25 Feb '10  
I started using your control and it works pretty well. I don't know if you are following any updates to this thread but wanted to show my gratitude.

I have a question though.
I'd like to create my RibbonTabs as a dataBound collection. Is there any easy way of doing it?

While attempting to achieve the above, I changed the Tabs collection of RibbonBar from Collection to ObservableCollection and changed it to a DependencyProperty. It was able to dataBind but the problem is that now the TabHeaders do not appear anymore.

Any help would be appreciated.

Thank you so much.
General[My vote of 5] great look
icetea94
3:50 24 Jan '10  
Hi,

the first free ribbon library I discovered that looks like the "real" office ribbon.
Thanks for sharing it!

icetea
QuestionResizeMode="NoResize" - Not Working
Chris5150
9:41 9 Jan '10  
Hi All,
In my RibbonWindow XMAL I have the following

ResizeMode="NoResize"

But my windows still show the minimize and maximize controls. If I change my code back to the standard Window type the minimize and maximize controls are properly removed.

Has anyone been able to get the min/max to hide with RibbonWindow type windows?

Thanks
GeneralRaster images
Georgyy Kozlov
10:50 1 Jan '10  
Thomas, I'm using only vector graphics in my app, however my vector images looks as bitmaps inside your ribbon, I don't know why but they looks bad only in your control. I use the same images anywhere in application and they looks perfect, however in ribbon they looks similar to low resolution bitmaps.

All your demos uses only bitmaps however in WPF world much more better to use vector images, it was designed to use powerful DirectX engine to render vector graphics. I understand that it's very difficult to draw professional vector images, however I have done this work, and looks you are using some strange methods to stretch or reduce images. I can imagine that you use bitmap related features. If you are interesting in detailed discussion, feel free to send emails to me. Thanks in advance.

The exception prove the rule

QuestionContent binding via XAML
Georgyy Kozlov
5:49 1 Jan '10  
Hi Thomas,

Your library looks very promising, and I'm interesting in using of it in my WPF application.
All features are quite useful however I can't bind the content dynamically using declarative programming (XAML). So I don't want to use codebehind or static content, I want to bind content using ItemSource and data templates. I've downloaded the latest version of your library and looks such this feature isn't supported in current version. Are you planning to support binding to the content?

I use the below code to create ribbon gallery dynamically.
<ribbon:RibbonGroup Title="Gadgets">
<ribbon:RibbonGallery DropDownColumns="3" ThumbnailSize="40, 40" ribbon:RibbonGallery.Stretch="Uniform">
<ribbon:RibbonGallery.ItemsSource>
<x:Array Type="sys:String" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</x:Array>
</ribbon:RibbonGallery.ItemsSource>
<ribbon:RibbonGallery.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{DynamicResource RemoveFolder_icon}" Width="40" Height="40"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ribbon:RibbonGallery.ItemTemplate>
</ribbon:RibbonGallery>
</ribbon:RibbonGroup>

And unfortunately it doesn't work.

The exception prove the rule

GeneralWindow Freezing/Failing to Redraw [modified]
md5sum
12:06 21 Oct '09  
I'm having an issue with the RibbonWindow failing to redraw sometimes. this makes the application "hang", using ~45% of my CPU, but not actually doing anything. Minimizing and maximizing the window forces a redraw, but that isn't really an acceptable solution for my users. Is anyone else experiencing this, or have any information on it? I'm fairly certain that the UserControl I'm using is not what's hanging, because changing the window to a normal Window instead of a RibbonWindow fixes the problem entirely. I'm making the new windows from code, with a single UserControl on them, as follows:

// Make a new window.
wndViewer = new RibbonWindow();
wndViewer.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0x31, 0x31, 0x31));
wndViewer.WindowState = WindowState.Maximized;
wndViewer.Width = 1024;
wndViewer.Height = 768;

// Make a grid for the window.
Grid grd = new Grid();

// Put the ribbon bar in the grid.
RibbonBar rb = new RibbonBar();
rb.Height = 30;
grd.Children.Add(rb);

// Put a label in the grid for a title.
Label l = new Label();
l.Content = "Window Title";
l.Foreground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0x9E, 0xBC, 0xE4));
l.HorizontalAlignment = HorizontalAlignment.Left;
l.VerticalAlignment = VerticalAlignment.Top;
grd.Children.Add(l);

// Add a MyUserControl to the grid
MyUserControl vw = new MyUserControl();
vw.Date = ((Item)sender).Date.ToString("MM/dd/yyyy");
vw.Name = ((Item)sender).Name;
vw.Id = ((Item)sender).Id;
vw.Margin = new Thickness(3, 30, 3, 3);
vw.StudyItem = ((Item)sender).Study;
grd.Children.Add(vw);

// Put the grid in the window
wndViewer.Content = grd;

// Find the main window of the application and set it as the new window's owner.
DependencyObject dpParent = this.Parent;
do {
dpParent = LogicalTreeHelper.GetParent(dpParent);
} while (dpParent != null &amp;&amp; dpParent.GetType().Name != "Main");
wndViewer.Owner = (Window)dpParent;

// Stop a refreshing timer I have running
refreshTimer.Stop();

// Show the window.
wndViewer.ShowDialog();

Thanks!

~md5sum~

modified on Wednesday, October 21, 2009 5:16 PM

GeneralWPF and WFA
everton
13:23 20 Aug '09  
Hi,Thomas.

I can use this ribbon with Windows Forms Application (WFA)? I thought of creating a formMain with WPF and other windows with WFA.

Thanks.
QuestionRibbonBar not visible inside the ControlTemplate
Dawid Łaziński
3:34 24 Jul '09  
Hi,

When put directly or indirectly inside the window contents the RibbonBar is displayed properly.

Problem arises when I make RibbonBar part of antoher control's ControlTemplate, registered throug DefaultStyleKeyProperty and included in generic.xaml

In such scenario the ribbon takes up space but remains hidden.

To be sure the control template works fine, I put the RibbonBar inside the coloured Border. The border is visible, however the RibbonBar is still missing.

Is there any known issue regarding using RibbonBar inside templates. Also is there any workaround?

I'd be greatful for any help in that matter.
GeneralRibbon Tab Alignment
Custec
6:56 9 Jul '09  
First things, you get a big 5 from me. I have one problem with the latest code from codeplex. I have only one tab which is not aligning just after the orb button as you would expect but under the most right button in the quick access bar which looks odd.
Not sure what can be causing this but apart from this bug I think this is far better than Microsofts effort! Big Grin
GeneralRe: Ribbon Tab Alignment
Custec
7:20 9 Jul '09  
After some more investigation I found that the Tab is pulled further to the right the more Quick Access buttons I add. So It only looks about in the correct place if I have no Quick Access buttons :(
GeneralBeautiful
[d3m0n]
22:53 6 Jul '09  
Great job! Easily a 5 from me, but I'd give you 50 if I could.

Cheers
[d3m0n]

Email (replace "***" with "key")

Generalhow to host in winform
aldo hexosa
21:42 21 May '09  
i am a newbie in wpf. how to host your wpf control in my winform application?
thanks
Questionmin, max and close in the title bar
Chris5150
17:01 9 May '09  
Hello,
Is there any way to disable or hide the minimize, maximise and close options in the title portion of a window that is of type RibbonWindow?

Thanks
- Chris
QuestionAny way for me to tie my other controls to the styles you use in the bar?
Chris5150
16:03 9 May '09  
This is great!
I wanted to use a status bar and a grid splitter in the rest of my app but I'm having trouble making use of the styles you illustrate. Is there some trick to getting at that information?

Thanks
-Chris
QuestionDeployment Bug
ellynest
9:15 2 May '09  
The control works fine in my development machine but when i tried to install deployed application on another machine, error message
"Error at object 'System.Windows.Setter' in markup file 'Odyssey;Component/Themes/Ribbon/RibbonBar.xaml" is displayed. I can't get out of this error, can someone help on this.

Thanks in advance.
GeneralMissing Event ?
Member 5963197
8:37 14 Mar '09  
I'm trying to generate an event when a tab is selected. Under the MS RibbonBar you have a Selected event.
Am I missing something or is that not availabele with your interpretation of a Ribbon?

MouseLeftButtonDown seems the closest but I can't get that to trigger at all.

Thanks

Mike...
GeneralRe: Missing Event ?
Thomas Gerber
13:41 8 May '09  
There is

a routed event named SelectedTabIndexChanged that you can use as shown in the example below:

            AddHandler(RibbonBar.SelectedTabIndexChangedEvent, new RoutedEventHandler(SelectedTabIndexChanged));
}

private void SelectedTabIndexChanged(object sender, RoutedEventArgs e)
{
//... do something here
}


there is currently no event handler, but I added it so it will be available in the next release on codeplex.
GeneralRe: Missing Event ?
Member 5963197
10:09 12 May '09  
Thanks for that, I'll give it a try out.
BTW Are you going to add support for password text entry boxes?
Thanks, Mike...
GeneralIt's very nice.
gWin
14:12 10 Mar '09  
You are a nice man. It's too elegant and beautiful.

Hard working has never killed your life.

Generalcan you please add a right to left support to this tool ?
Al-Nabhani
0:37 6 Mar '09  
can you please add a right to left support to this tool ? it is neccessary in my programms.


thanks alot.
GeneralRe: can you please add a right to left support to this tool ?
Thomas Gerber
2:07 6 Mar '09  
I can try, but I'm unexperienced with that.
Can you please describe what has to be changed?
GeneralNice work
Andy Lang
17:25 26 Feb '09  
This Ribbon looks so good. I found some bugs, just FYI.
1. Minimize Ribbon window, when restore the window, it location changed, looks more top then before.
2.When a ribbon window doesn't have a ribbon bar, use as a common window, the window's content will covert the caption bar.
3. RibbonButton's image not locate at the center.
4. RibbonGroup's bottom text not locate at vertical center.
5. When ribbon window width or height set to zero, it lost theme.

Above it not very big bug, hope this work more good.

My music manage tool "Music master", http://www.longs-soft.com


Last Updated 15 Feb 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010