Odyssey RibbonBar






4.99/5 (79投票s)
一个 WPF RibbonBar 控件。
引言
Odyssey RibbonBar 是 Odyssey 控件库中即将添加的下一个 WPF 控件,该库可在 CodePlex 上获得。
背景
继我之前在 CodeProject 上介绍过的 BreadcrumbBar、ExplorerBar 和 OutlookBar 之后,Odyssey RibbonBar 是 Odyssey 库中包含的下一个控件。我知道 Microsoft 的 WPF RibbonBar 已经作为功能齐全的预览版可用,并将添加到 .NET 4.0 中。因此,您可能会想,我为什么还要自己编写 RibbonBar 呢?答案是,这样做很有趣也很有意义。而且,我也计划构建一个 Silverlight 版本。由于 RibbonBar 是一组各种控件,解释每一个细节可能需要整整一本书,所以我将其重点放在 RibbonBar 最重要的问题上。演示应用程序包含 RibbonBar 的 XAML,展示了其大部分功能。我没有创建一个有用的演示,也没有使用有用的按钮图像或标签。我在演示中使用的是 www.glyfx.com 网站上提供的免费图片。RibbonBar 包含选项卡,每个选项卡包含组。每个组都可以包含任何可能的控件。但是,只有当组包含 IRibbonControl
时,才会应用动态大小调整。目前,有以下 IRibbonControl
:
RibbonButton
RibbonToggleButton
RibbonDropDownButton
RibbonSplitButton
RibbonComboBox
RibbonTextBox
RibbonButtonGroup
RibbonGallery
RibbonSeparator
RibbonBar
包含一个 ApplicationMenu
和一个 QuickAccessToolbar
,它们可以放置在顶部或底部,以及一个 RibbonTabItem
集合。
<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>
这些控件中的每一个都可以有三种可能的 RibbonSize
:Large
、Medium
和 Small
,除了 RibbonGallery
可以有无限大小。而按钮通常有三种可能的大小。RibbonBar
会根据 RibbonBar
的宽度自动检测最佳大小,我称之为自动缩减。
缩减的工作方式如下:
首先,假设所有组都具有无限宽度。如果所有组的宽度之和超过可用宽度,则每个组将从最后一个组开始,逐级缩小,直到总宽度最终适合可用宽度。此过程会重复进行,直到没有组可以再缩小为止。组可以缩小的级别数取决于组内的控件。如果组内有任何 IRibbonControl
,则该组可以有四个缩减级别:Large
、Medium
、Small
和 Collapsed
。
RibbonGroup
RibbonGroup
可以容纳任何类型的控件,但倾向于使用 IRibbonControl
以便实现组的不同大小调整。
<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>
组的缩减方式如下:组内的所有控件被分成若干个“桶”。当出现一个不可缩减且高度大于三行可容纳高度的控件时,一个新的“桶”开始。确定“桶”后,布局取决于“桶”内的控件数量。如果有三个控件,则它们都会缩小到中等或小尺寸,以便它们可以放在一列中。如果有四个控件,第一个控件将保持大尺寸并占据一整列,而其余三个将占据另一列。如果有五个控件,前两个将保持大尺寸,每个占据一列,而剩余的三个将在最后一列中分成三行。如果有六个控件,前三个将放在第一列中,分成三行,而最后三个将放在第二列中。
可以修改此缩减方式。您可以将 Reduction
属性附加到控件上,该属性描述控件在每个组级别的尺寸。使用 Reduction
属性还可以修改组以具有各种可能的级别。
<odc:RibbonButton Content="Button 4" LargeImage="img/paste32.png"
SmallImage="img/paste16.png"
odc:RibbonBar.Reduction="Large,Large,Large,Medium"/>
您还可以将 MinSize
和 MaxSize
附加属性设置到控件上,以强制其大小不超过 MaxSize
和/或不小于 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"/>
此外,您还可以通过为 RibbonTabItem
指定 ReductionOrder
属性来修改组的缩减顺序。ReductionOrder
是一个组名列表。当 TabItem
需要缩减组时,它会从列表中第一个组开始,而不是从其 Group
集合中最后一个组开始。
<odc:RibbonTabItem Title="Tab 1" ReductionOrder="grp2,grp1,grp3,grp4,grp3a">
RibbonGroup
也可以在右下角包含一个启动器按钮。一旦单击此按钮,就会触发一个 ExecuteLauncher
RoutedEvent
。
<odc:RibbonGroup Title="Skin" Image="img/home16.png" IsDialogLauncherVisible="True"
ExecuteLauncher="RibbonGroup_LaunchDialog">
C# 代码
private void RibbonGroup_LaunchDialog(object sender, RoutedEventArgs e)
{
MessageBox.Show("Launcher");
}
IRibbonButton
RibbonButton
、RibbonToggleButton
、RibbonDropDownButton
和 RibbonSplitButton
实现 IRibbonButton
,因此它们具有三种不同的视觉状态。如果设置为大尺寸,则按钮的文本会分成两行(如果可能),这意味着文本至少包含一个空格。在中等模式下,按钮的文本只有一行,在小尺寸模式下,按钮的文本会被隐藏。每个按钮都可以附加 LargeImage
和 SmallImage
属性。LargeImage
在按钮处于大尺寸时显示,SmallImage
在按钮处于中等或小尺寸时显示。这些属性是从 RibbonBar
附加的,并指定 ImageSource
。
<odc:RibbonButton Content="Windows 7" odc:RibbonBar.MinSize="Large"
SmallImage="img/save16.png" LargeImage="img/Save32.png" Click="Win7Click"/>
LargeImage
以 32x32 像素显示,而 SmallImage
以 16x16 像素显示。因此,ImageSource
应该具有这些尺寸。但是,图像会被缩放到适当的大小。您可以使用 RibbonButton
的 ImageStretch
附加属性来修改图像的缩放方式。
/// <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
画廊控件派生自 ListBox
,并包含 RibbonThumbnail
。RibbonThumbnail
显示任何尺寸的图像。根据图像的大小,画廊在内嵌画廊中有不同的行。
<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>
RibbonGallery
还有一个 HotItem
属性,它是一个依赖项属性,用于指定当前鼠标指针所在的缩略图,因此您可以结合 HotThumbnailChanged
RoutedEvent
实现预览。演示应用程序通过 XAML 中的绑定演示了 HotItem
的用法。
<TextBlock Text="{Binding HotItem, ElementName=gallery,
Converter={StaticResource ThumbnailConverter}}"/>
RibbonGallery
可以有各种缩减级别。您可以为每个级别指定列数。
<odc:RibbonGallery x:Name="gallery2" odc:RibbonGallery.Stretch="None"
odc:RibbonGallery.ReductionColumns="12,11,10,9,8,7,6,5,4,3">
由于 RibbonGallery
派生自 ListBox
,它允许使用 GroupStyle
对下拉列表进行分组。我不会详细介绍,因为这是一个标准功能,但结合 ItemsPanel
、DropDownHeader
和 DropDownFooter
,您可以以无限的方式修改下拉列表框。
RibbonDropDownButton 和 RibbonSplitButton
这两种控件都提供下拉列表以显示其他项目。它们派生自 HeaderedItemsControl
,因此可以使用数据源作为项目。项目可以是任何类型,以实现丰富多样的下拉列表构建,但通常包含 RibbonMenuItem
。
<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>
还可以为下拉列表指定 DropDownHeader
和 DropDownFooter
,以及 DropDownHeaderTemplate
和 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 和 RibbonTextBox
它们分别派生自 ComboBox
和 TextBox
,并具有附加的 Image
和 Title
属性,用于指定 16x16 的图像和控件的文本。这些控件可以有两种不同的尺寸。中等尺寸显示 Image
、Title
和控件本身;小尺寸仅显示 Image
和控件。与 RibbonDropDownButton
和 RibbonSplitButton
一样,RibbonCombobox
也具有 DropDownHeader
和 DropDownFooter
属性以及它们的模板属性。
RibbonComboBox
的项目通常是 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
RibbonSeparator
的作用是将 RibbonBar.Reduction
附加到其上,这样如果其尺寸被确定为 RibbonSize.Small
,就有可能隐藏 Separator
。
皮肤
RibbonBar
目前有四种不同的皮肤。
- Office 蓝色 (标准)
- Office 银色
- Office 黑色
- Windows 7
您可以使用 SkinManager
随时为应用程序设置皮肤。
private void Win7Click(object sender, RoutedEventArgs e)
{
SkinManager.SkinId = SkinId.Windows7;
}
private void OfficeBlueClick(object sender, RoutedEventArgs e)
{
SkinManager.SkinId = SkinId.OfficeBlue;
}
但是,您也可以创建自己的 ResourceDictionary
,覆盖启用了皮肤设置的 ComponentResourceKeys
。这将在后面介绍。
ApplicationMenu
RibbonApplicationMenu
是一个独立的控件。它派生自 ItemsControl
,用于容纳 RibbonApplicationMenuItem
。RibbonApplicationMenuItem
和 RibbonMenuItem
之间的区别仅在于尺寸。一旦 RibbonApplicationMenuItem
拥有子项,它们就会在一个弹出容器中打开,该容器会填充 RecentList 内容的空间,如下所示。
ApplicationMenu
的 RecentList
属性的类型为 object
,因此它可以包含任何控件来构建最近使用的项目列表。
QuickAccessToolbar
RibbonQAToolbar
派生自 ItemsControl
,通常包含 IRibbonButton
和/或 RibbonMenuItem
。取决于这两个组,项目将显示在工具栏中(如果它是 IRibbonButton
类型),或者显示在单击右侧下拉按钮时出现的下拉菜单中。
<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>
您可以将工具栏放置在顶部或底部。
您可以设置 RibbonBar.ToolbarPlacement
属性来指定放置位置。
private void ShowBelowClick(object sender, RoutedEventArgs e)
{
ribbonBar.ToolbarPlacement = QAPlacement.Bottom;
}
上下文选项卡集
上下文选项卡集包含附加的 RibbonTabItem
,它们仅在需要时出现,通过设置 RibbonBar
的 ContextualTabSet
属性。
private void Context2Click(object sender, RoutedEventArgs e)
{
ribbonBar.ContextualTabSet = ribbonBar.ContextualTabSets[1];
}
RibbonContextualTabSet
可以包含各种 RibbonTabItem
。
<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
用于将 IRibbonButton
分组,并用组边框进行可视化。如果放置在 RibbonButtonGroup
中,按钮唯一允许的大小是 RibbonSize.Small
,这意味着只显示 SmallImage
。
<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
这用作 RibbonButtonGroup
的容器。它可以有两种可能的状态:2rows
和 3rows
。
在 2rows
模式下,RibbonButtonGroup
的顺序就是集合中的顺序。而在 3rows
模式下,顺序取决于组的宽度。该算法通过首先确定三个最大的组并将它们放置在第一列来优化顺序,其中第一行包含最大的组,第三行包含最小的组。现在,对于第二列,行的顺序与前一列相反。
RibbonWindow
最后,关于 RibbonWindow
,它派生自 Window
类。您一定注意到 QuickAccessToolbar (QAT) 和上下文选项卡集出现在窗口的标题栏内。这可以通过使用 RibbonWindow
而不是 Window
类来实现。RibbonWindow
会劫持 Windows 消息,以便在窗口的非客户区域进行绘制。如果 Aero 已启用且 IsGlassEnabled
设置为 true
,它还会使用 DwmExtendFrameIntoClientArea
为标题栏设置玻璃效果。
在这种情况下,右侧的 Windows 按钮是原始的 Windows 按钮。但是,如果禁用玻璃效果,则窗口必须自己绘制按钮。
内部
如果 RibbonButton
是大尺寸,其文本会分成两行(如果包含空格)。这是通过对每一行使用 ValueConverter
和 ContentControl
/TextBlock
对来实现的。如果您问为什么不是两个 TextBlock
,答案是,这样就可以使用任何类型作为按钮的内容,而不仅仅是 string
。ValueConverter
会识别这一点,并在要转换的值不是 string
时返回原始值作为第一行。代码部分如下:
<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>
C# 代码
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.");
}
}
皮肤
对于皮肤设置,我使用 ComponentResourceKeys
来更改控件的外观。ComponentResourceKey
类似于 ResourceKey
,除了它包含一个类类型和一个标识符。定义组件资源键如下所示:
<SolidColorBrush
x:Key="{ComponentResourceKey odc:Skins, PopupContainerBgBrush}" Color="White"/>
在 XAML 中使用该键如下所示:
<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>
由于模板使用 DynamicResource
而不是 StaticResource
,因此资源可以随时修改。RibbonManager
会加载一个 ResourceDictionary
并将其添加到应用程序的 MergedDictionaries
中。
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;
}
}
ApplicationMenu
按钮的重叠:当您打开应用程序菜单时,您会注意到菜单按钮仍然在弹出控件的顶部,您可能会想这是如何实现的。诀窍是使用两个按钮:一个在 Ribbon
上,一个在 Popup
上。当 Popup
打开时,它上的按钮需要放置在与 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);
}
}
在单独的面板中托管 ItemsControl
的项目:如果您尝试更改派生自 ItemsControl
的项目的父项,将会出现一个异常,即该控件已有一个逻辑父项。诀窍是将其添加到重写 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);
}
当该面板在模板中使用时(意味着 TemplatedParent != null
),它会告诉元素集合没有逻辑父项。因此,将控件添加到此面板是可能的,因为逻辑父项不会改变。
未实现/将在未来版本中提供
- 扩展工具提示和启动器按钮的工具提示。
- Alt 键快捷方式。
- 上下文本框
RibbonSplitButton
的IsChecked
和IsCheckable
属性。
Odyssey RibbonBar 将迁移到 Odyssey 控件库,可在 www.codeplex.com/odyssey 获得。