Reputation: 12998
The WPF ribbon (System.Windows.Controls.Ribbon) includes a number of controls which you can add to your ribbon having "dropdown" style behavior where clicking the control's main button shows you a new area.
Examples: RibbonMenuButton, RibbonSplitButton, RibbonMenuButton, RibbonGallery, etc.
However, as far as I can see all of them are designed to show you a list of things from which the user makes a selection. But instead, is there any way to display a 'panel' area that is non-selectable, upon which other controls could be placed?
As an example, here is a screenshot from MS Outlook:
The upper red area is NOT itself a selection in a list. Instead it has a custom control (the table size-picker thing).
But the blue items ARE selectable items which function like a traditional menu.
Its the red area I'm interested in understanding.
(I don't know if Outlook was coded using the WPF Ribbon, and that doesn't matter at all - I'm just using it as an illustration of what I'm looking for.)
Note - I'm not trying to replicate this Outlook table-picker specifically, its just an example of the ways in which you might use a non-selectable 'panel' area within the dropdown region.
Upvotes: 2
Views: 1487
Reputation: 71
I had been looking at similar documentation and eventually developed this solution through trial and error, on Visual Studio 2022.
I got the RibbonSplitButton
to produce an area where I could dynamically load buttons, by using the following XAML code...
<RibbonSplitButton x:Name="btnColor" Height="21" Width="40" Margin="-139,-9,139,9" SmallImageSource="images/font-color.png" ToolTip="Font Color" >
<RibbonMenuItem x:Name="itmDefaultColor">
<RibbonMenuItem.Header>
<StackPanel Orientation="Horizontal">
<Rectangle x:Name="rctDefaultColor" Width="13" Height="13" Fill="Transparent" Stroke="Black" Margin="-25,0,15,0"/>
<TextBlock Text="Default Color"/>
</StackPanel>
</RibbonMenuItem.Header>
</RibbonMenuItem>
<RibbonGallery x:Name="galAllColorSwatches" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<RibbonGallery.Resources>
<Style TargetType="Button">
<EventSetter Event="UIElement.PreviewMouseLeftButtonDown" Handler="Button_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewKeyDown" Handler="Button_PreviewKeyDown" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="White"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</RibbonGallery.Resources>
<RibbonGalleryCategory Header="Custom Colors" FontWeight="Bold" />
<RibbonGallery x:Name="galCustomColors" MinWidth="130" Width="130" MaxWidth="130" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<RibbonGallery.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"></WrapPanel>
</ItemsPanelTemplate>
</RibbonGallery.ItemsPanel>
</RibbonGallery>
<RibbonGalleryCategory Header="Basic Colors" FontWeight="Bold" />
<RibbonGallery x:Name="galBasicColors" MinWidth="130" Width="130" MaxWidth="130" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<RibbonGallery.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"></WrapPanel>
</ItemsPanelTemplate>
</RibbonGallery.ItemsPanel>
</RibbonGallery>
</RibbonGallery>
<RibbonMenuItem x:Name="itmColor" ImageSource="images/font-color.png" Header="Pick Color..."/>
</RibbonSplitButton>
The end result resembles the following image, but requires code to insert the color swatches...
Note there is an outer wrapper RibbonGallery
named galAllColorSwatches
, and that this element has resources set up to respond to specific events when the mouse rolls over buttons that get added dynamically by code, and also to respond to button click events. You could also manually construct all your buttons, and you could potentially add other types of controls in a RibbonGallery.
Creating and inserting a button by code can be accomplished like this in Visual Basic...
' Prepares a button for the color swatch...
Dim [YOUR_BUTTON] As New Button
' Works with the button...
With [YOUR_BUTTON]
.Name = "btnSwatch"
.Content = [INSERT_YOUR_CONTENT_HERE]
.Margin = New Thickness(0, 0, 0, 0)
.Padding = New Thickness(0, 0, 0, 0)
.ToolTip = strColor
.Tag = strColor
End With
' Adds the item to the custom colors...
[YOUR_RIBBONGALLERY_NAME].Items.Add([YOUR_BUTTON])
...but you could also use binding.
If the above code is nested in a For..Next
or similar loop, it can produce a large number of generic buttons like the grid in the image you supplied.
All of the RibbonGalleryCategory
elements are siblings of the RibbonGallery
elements inside the outer wrapper. The inner RibbonGallery
elements each contain RibbonGallery.ItemsPanel
and ItemsPanelTemplate
that helps define the layout for their contents. My example uses WrapPanel
elements to achieve the grid-like layout, but you could potentially add any other compatible element there.
The layout of the content in the WrapPanel
is controlled by the maximum and minimum width settings of the RibbonGallery
elements, and the width settings of the buttons that are dynamically inserted, so you would need to customize those dimensions based on what you intend to achieve.
The top and bottom of the XAML code each have a standard RibbonMenuItem
element. If you nest menu items inside each other, they should produce the standard nested menu with the > indicator. The top item uses a dynamic color swatch that can be loaded with a specific color.
If you construct any RibbonSplitButton
like this, and it has an event handler, you may need to use e.Handled = True
in the event handler to prevent the menu items and menu buttons from triggering the events in the RibbonSplitButton
and vice-versa.
Upvotes: 1
Reputation: 11
For posterity, it's not currently possible. It appears that the RibbonMenuButton uses a private Popup to display the dropdown content.
Source: https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/System.Windows.Controls.Ribbon/Microsoft/Windows/Controls/Ribbon/RibbonMenuButton.cs Lines 1309 and 1335.
Upvotes: 0
Reputation: 11
Microsoft RibbonMenuButton / RibbonSplitButton wont support custom controls in dropdown.
Even if we manage to do that by changing the Content of RibbonMenuItem / RibbonGalleryItem, we will still get selection decoration around those controls on mouse hover
Best way is to use the combination of RibbonToggleButton and Popup Control as below
Then you can place whatever custom control you want inside that popup Control
<StackPanel Orientation="Vertical">
<RibbonToggleButton
x:Name="yAxis"
Label="Y Axis"
SmallImageSource="..\Images\ChartYAxis16.png"
LargeImageSource="..\Images\ChartYAxis32.png"
RibbonTwoLineText.HasTwoLines="True"
RibbonTwoLineText.PathData="M 0 0 L 2.5 3 L 5 0 Z">
</RibbonToggleButton>
<Popup
IsOpen="{Binding IsChecked, ElementName=yAxis}">
<mycontrols:AnyControl/>
</Popup>
</StackPanel>
Of course you may need to handle the dropdown closing programmatically by unchecking the togglebutton whenever user clicks outside the togglebutton or dropdown popup
Upvotes: 1
Reputation: 12276
You can put anything you like inside a ribbonmenubutton.
For example:
<Ribbon>
<RibbonMenuButton Label="Button One">
<Grid Height="100" Width="200">
<TextBlock VerticalAlignment="Top" Text="AAAA"/>
<TextBlock VerticalAlignment="Bottom" Text="ZZZZ"/>
</Grid>
</RibbonMenuButton>
</Ribbon>
You'd want to extract and change the ribbonmenubutton template to avoid the gap at the left.
Upvotes: 0