Ribbon ApplicationMenu AuxilaryPane Size - c#

How do I change the size of the AuxilaryPane in the WPF Ribbon ApplicationMenu? I have added a recent file list to that area but it is getting truncated. Ideally I'd like the auxilary pane to fill the screen like it does for Word/Excel.
My code:
<r:Ribbon.ApplicationMenu>
<r:RibbonApplicationMenu>
<r:RibbonApplicationMenu.AuxiliaryPaneContent>
<StackPanel>
<TextBlock Text="Recent Files" />
<Separator />
<ItemsControl ItemsSource="{Binding RecentFiles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<r:RibbonApplicationMenuItem Header="{Binding ShortPath}"
Command="{Binding DataContext.OpenRecentFileCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding LongPath}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</r:RibbonApplicationMenu.AuxiliaryPaneContent>
</r:RibbonApplicationMenu>
</r:Ribbon.ApplicationMenu>

Based on the answers in this thread I found it easiest to subclass RibbonApplicationMenu and set Width of the third column to Auto.
public class CustomRibbonApplicationMenu : System.Windows.Controls.Ribbon.RibbonApplicationMenu
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
System.Windows.DependencyObject obj = this.GetTemplateChild("PART_AuxiliaryPaneContentPresenter");
System.Windows.Controls.ContentPresenter c = obj as System.Windows.Controls.ContentPresenter;
((System.Windows.Controls.Grid)((System.Windows.Controls.Border)c.Parent).Parent).ColumnDefinitions[2].Width = System.Windows.GridLength.Auto;
}
}
Now you just need to change your Ribbon xaml from
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu>
to
<Ribbon.ApplicationMenu>
<ctrl:CustomRibbonApplicationMenu>

If you are looking for a very quick fix to increase the height, you can simply add some useless RibbonApplicationMenuItems to pad out the box (and not have to modify MS source code).
<ribbon:Ribbon.ApplicationMenu>
<ribbon:RibbonApplicationMenu>
<ribbon:RibbonApplicationMenu.Items>
<ribbon:RibbonApplicationMenuItem Name="saveSettings" Header="Save Settings" />
<ribbon:RibbonApplicationMenuItem IsEnabled="False"/> <!--USELESS-->
<ribbon:RibbonApplicationMenuItem IsEnabled="False"/> <!--USELESS-->
</ribbon:RibbonApplicationMenu.Items>
<ribbon:RibbonApplicationMenu.AuxiliaryPaneContent >
<StackPanel Orientation="Vertical" >
<GroupBox>
<Label Content="System Settings" />
</GroupBox>
<StackPanel Orientation="Horizontal">
</StackPanel>
</StackPanel>
</ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
</ribbon:RibbonApplicationMenu>
</ribbon:Ribbon.ApplicationMenu>

I've searched a solution for the same problem.
There is no direct property to modify this.
An Example of creating such property can be found at
msdn
here's the main solution:
Change the source code of the Ribbon Library. MS has provided the source code of the Ribbon Library: http://www.microsoft.com/downloads/en/details.aspx?FamilyID=2bfc3187-74aa-4154-a670-76ef8bc2a0b4
Download the source code and open it, in the MicrosoftRibbonForWPFSourceAndSamples\RibbonControlsLibrary\Microsoft\Windows\Controls\Ribbon\RibbonApplicationMenu.cs, add one Dependency Property:
public double MinMenuHeight
{
get { return (double)GetValue(MinMenuHeightProperty); }
set { SetValue(MinMenuHeightProperty, value); }
}
public static readonly DependencyProperty MinMenuHeightProperty =
DependencyProperty.Register("MinMenuHeight", typeof(double), typeof(RibbonApplicationMenu), new UIPropertyMetadata(0.0));
In the MicrosoftRibbonForWPFSourceAndSamples\RibbonControlsLibrary\Themes\Generic.xaml, line 7519, add the XAML code:
<Border x:Name="PopupBorder" MinHeight="{TemplateBinding MinMenuHeight}" BorderBrush="{Binding RelativeSource={RelativeSource AncestorType={x:Type ribbon:RibbonMenuButton}}, Path=Ribbon.BorderBrush}" Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type ribbon:RibbonMenuButton}}, Path=Ribbon.Background}" BorderThickness="1" CornerRadius="2">
<Grid>
</Grid>
</Border>
Only add the first two rows of the given xaml

This is an old thread, yes, and it has some good ideas, but I wasn't satisfied.
My problem was slightly different in that I needed the ApplicationMenu to expand only enough to fit any control that was placed in the auxiliary pane.
Eventually I dug deep and found a solution I was happy with. It doesn't solve the "fill the screen" problem, but I'm hoping this will help others who land here, looking for a solution to a problem similar to mine. Sorry if it looks like I'm trying to hijack the thread. I don't intend to.
Essentially I solved the fixed width and height problem by changing the ribbon style:
Open the ribbon assembly in JetBrains DotPeek
Open Resources/System.Windows.Controls.Ribbon.g.resources/Themes/generic.baml
Copy the entire resource dictionary into a .xaml file in your project. You may be able to get away with using only a part of it, but I decided to take the whole thing.
At this point you may be asking, "Why not just use VS or Blend or ShowMeTheTemplate instead of DotPeek?" All of these tools failed miserably on the ribbon assembly. Don't know why. They didn't say. One of the problems with using DotPeek is that some of the namespace references will need adjusting, but it's not too difficult, so I won't go into details here.
So, now that you have all the styles and templates, go look for the offending markup.
First, fix the width:
Look for the grid whose third column definition is a static value of 300. You can search for <ColumnDefinition Width="300"/>. There is only one.
Change the "300" to "Auto".
Then fix the height:
Look for the definition of PART_SubmenuPlaceholder Border. You can search for x:Name="PART_SubmenuPlaceholder". It is about 50 lines below the change you did for the width.
That Border binds the Height property to the ActualHeight property of the "MainPaneBorder" control: Height="{Binding ElementName=MainPaneBorder, Path=ActualHeight}".
Remove this Height definition.
Now that you've modified the style, just add this resource dictionary to your xaml and it should apply itself to the ribbon.

When I came across this answer (while searching for an answer to my own, slightly different question), I wasn't too excited about actually modifying Microsoft code.
As a result, I rather preferred to subclass it, and get the hold of the necessary UI element using base.GetTemplateChild on the relevant "PART_...".
I suppose you can follow a similar approach to achieve what you need.
My example is here.
Hope this helps.
P.S. If you happen to find a way to determine the necessary width of the AuxiliaryPanel, please let me know - I would like to see if that's applicable to the menu's width as well.

You can download the Microsoft Ribbon for WPF Source Code (http://www.microsoft.com/en-us/download/details.aspx?id=11877) and add a DependencyProperty Width/Height to ApplicationMenu or just do it 'quick and dirty' like in my example:
MainWindow.xaml
public partial class MainWindow : RibbonWindow
{
private Size DefaultApplicationMenuSize;
public MainWindow()
{
InitializeComponent();
}
private void RibbonWindow_Loaded(object sender, RoutedEventArgs e)
{
var grid = (myRibbon.ApplicationMenu.Template.FindName("MainPaneBorder", myRibbon.ApplicationMenu) as Border).Parent as Grid;
/* before the first opening of the menu the size is NaN, so you have to measure size and use the DesiredSize */
grid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.DefaultApplicationMenuSize = new Size(grid.ColumnDefinitions[2].Width.Value, grid.DesiredSize.Height);
}
private void RibbonApplicationMenuItem_MouseEnter(object sender, MouseEventArgs e)
{
Button b=new Button();
b.Content = "my epic button";
b.Width = 500;
b.Height = 500;
b.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
SetApplicationMenuSize(b.DesiredSize);
this.ribbonContentPresenter.Content = b;
}
private void RibbonApplicationMenuItem_MouseLeave(object sender, MouseEventArgs e)
{
SetApplicationMenuSize(DefaultApplicationMenuSize);
this.ribbonContentPresenter.Content = null;
}
private void SetApplicationMenuSize(Size size)
{
var grid = (myRibbon.ApplicationMenu.Template.FindName("MainPaneBorder", myRibbon.ApplicationMenu) as Border).Parent as Grid;
/* you can modify the width of the whole menu */
//grid.Width = size.Width;
/* or just the size of RibbonApplicationMenu.AuxiliaryPaneContent */
grid.ColumnDefinitions[2].Width = new GridLength(size.Width);
grid.Height = size.Height;
}
}
MainWindow.xaml.cs
<ribbon:RibbonWindow x:Class="WpfRibbonApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
Title="MainWindow"
x:Name="RibbonWindow"
Width="640" Height="480"
Loaded="RibbonWindow_Loaded">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ribbon:Ribbon x:Name="myRibbon">
<ribbon:Ribbon.ApplicationMenu>
<ribbon:RibbonApplicationMenu SmallImageSource="Images\SmallIcon.png">
<ribbon:RibbonApplicationMenuItem Header="Hello _Ribbon"
ImageSource="Images\LargeIcon.png"/>
<ribbon:RibbonApplicationMenuItem Header="HoverTest"
ImageSource="Images\LargeIcon.png"
MouseEnter="RibbonApplicationMenuItem_MouseEnter"
MouseLeave="RibbonApplicationMenuItem_MouseLeave"
StaysOpenOnClick="True" />
<ribbon:RibbonApplicationMenu.FooterPaneContent>
<ribbon:RibbonButton Label="What ever" HorizontalAlignment="Right"/>
</ribbon:RibbonApplicationMenu.FooterPaneContent>
<ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
<ribbon:RibbonContentPresenter Name="ribbonContentPresenter" />
</ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
</ribbon:RibbonApplicationMenu>
</ribbon:Ribbon.ApplicationMenu>
</ribbon:Ribbon>
</Grid>
</ribbon:RibbonWindow>
have a nice day

Related

Access template properties which are stored in resources

I have a derived devexpress grid control which sets some template for the indicator row:
<dxg:GridControl.Resources>
<sys:Double x:Key="{dxgt:TableViewThemeKey ResourceKey=IndicatorWidth, ThemeName=Office2016White}">300</sys:Double>
<DataTemplate x:Key="{dxgt:RowIndicatorThemeKey ResourceKey=RowTemplate, ThemeName=Office2016White}">
<Grid Name="IndicatorGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" Name="IndicatorColumnRowNumber" SharedSizeGroup="RowNumberGroup" />
<ColumnDefinition Width="200" Name="IndicatorColumnDescription" SharedSizeGroup="DescriptionGroup" />
<ColumnDefinition Width="200" Name="IndicatorColumnSource" SharedSizeGroup="SourceGroup" />
</Grid.ColumnDefinitions>
//...
</DataTemplate>
</dxg:GridControl.Resources>
Right now the width of the indicator column is fixed to 300. Now I want the width of the row being calculated by the columns defined in the second part. I know I can access the indicator width of the view via the code behind as well but I'm not able to access the template controls in the code behind
var view = ((TableView)this.View);
view.IndicatorWidth = IndicatorColumnRowNumber.Width /* can not be found */ + ...
Since they live in the resources in the xaml. As I understand this is also not supposed to happen. What is the best way to implement this? Maybe extract the definitions in the code behind?
Edit:
Here is a picture of the table
I am not 100% sure what you are trying to achieve but if you want to access your resource in code behind you can go about it like this:
XAML:
<Grid x:Name="MyGrid">
<Grid.Resources>
<DataTemplate x:Key="MyResource">
<TextBlock Text="Hello"></TextBlock>
</DataTemplate>
</Grid.Resources>
</Grid>
Code behind:
public MainWindow()
{
InitializeComponent();
var resource = MyGrid.Resources["MyResource"];
var dataTemplate = (DataTemplate) resource;
}
You have additional tools to obtain any defined resources.
The methods FrameworkElement.FindResource and FrameworkElement.TryFindResource will search for resources from the element you specified and up the visual tree until the main application and including any themes you have setup. From the MS reference:
FrameworkElement.FindResource documentation
Also notice that the Resources dictionary and the FindResource methods accept an object key (not a string). This suits your case since your key is a RowIndicatorThemeKey object. You can instantiate such an object and set the ResourceKey property to the "RowTemplate" value and search for your resource like that.
In general solutions for problems such as yours can be achieved without requiring code behind though, if you provide some more information perhaps we can find a solution based on bindings.

WPF ContextMenu = {x:Null} but still shows menu inside ContentControl

I need disable standard ContextMenu of TextBox. I've created a new WPF project and added the following:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Window>
But this is what i get :
The following code works fine :
<Grid>
<TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</Grid>
Why is this happening?
Update.
According to the accepted answer I've created a class derived from TextBox in order to be able to show parents ContextMenu.
public class TextBoxNoMenu: TextBox
{
public TextBoxNoMenu()
{
ContextMenu = null;
}
}
Why is this happening?
This is an interesting case of a control's behavior changing depending on where/how a property is set.
TextBox provides its own context menu by default. The only time it won't do this is when you explicitly set the local value of ContextMenu to null. This is what happens in your simple example where the TextBox is directly within in the Grid.
However, when you set a property inside a template, you're not actually setting a local value; you're setting a "parent template" value. If you inspect the value with DependencyPropertyHelper.GetValueSource(), you'll see the base value source is ParentTemplate instead of Local. Thus, the menu still gets overridden.
See Dependency Property Value Precedence for more information about the different kinds of dependency property value sources.
#OmegaMan's suggestion of assigning a 'hidden' context menu seems to work pretty well.
Note that while you mayhave disabled the ContextMenu on TextBox, if it's in another control, you may actually be seeing the ContextMenu of such a wrapper. Try Snooping it to see more specifically this sort of behaviour.
Note also that many of the default Control Templates throughout WPF can cause issues such as these by adding their own child objects. Seeing the default template for TextBox uses a Border and then <ScrollViewer Margin="0" x:Name="PART_ContentHost" />, you're likely seeing the ContextMenu of a child object if TextBox.
This seems to be a running issue where X:Null does not 'turn off' the default context menu. A better way would be to change it's visiblity:
<TextBox.ContextMenu>
<ContextMenu Visibility="Collapsed"/>
</TextBox.ContextMenu>
I had a similar issue, but I was generating my controls programmatically, and my parent control is a dockpanel. Based on the accepted answer, I decided to set the null value in the code behind.
<Grid>
<DockPanel>
<TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DockPanel>
</Grid>
and then
private void Window_Loaded(object sender, RoutedEventArgs e)
{
txtBox.ContextMenu = null;
}
EDIT: I felt this was kind of a haphazard answer, as it doesn't fully or directly solve this question. I did some digging and if you implement the method found in the answer to This Question you can find the textbox in the code-behind.
So, if you have this
<Grid>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
Then you should be able to find your textbox by name (txtBox in this case) and set the context menu to null
TextBox myTextBox = FindChild<TextBox>(Application.Current.MainWindow, "txtBox");
myTextBox.ContextMenu = null;
Personally I'd prefer this to creating a new class with inheritance, but whatever works for you. This still doesn't answer "Why is this happening?" but I think the accepted answer does a good job of that.

Programmatically moving TextBlocks in WPF

I have been reading some tutorials on XAML but it does not help me. I have an empty application window and I need to create 30 TextBoxes in 3 rows.
Being used on the win forms, I thought I would figure it out - well, I did not. I cannot seem to find a way how to create them on certain coordinates.
You first want to place a Canvas control on your screen, then you can populate it with TextBoxes placed at whatever Canvas.Left and Canvas.Top position you want.
That said though, WPF has a much better layout/arrangement system than WinForms, and trying to use it like it's WinForms means you'll miss out on a lot of what makes WPF so great, and you'll be making things a lot harder on yourself.
The WPF way of doing the same thing would be to use an ItemsControl, and a collection of objects that each contain data that the UI needs to to know for display purposes.
First you would create a class to represent each TextBox
public class MyClass
{
public string Text { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
Note: This class should implement INotifyPropertyChanged if you want to change the properties at runtime and have the UI automatically update.
Then make a list of this class, and bind it to an ItemsControl
<ItemsControl ItemsSource="{Binding ListOfMyClass}" />
Then you'd want to overwrite the ItemsPanelTemplate to be a Canvas (the best WPF panel for positioning items according to an X,Y position)
<ItemsControl ItemsSource="{Binding ListOfMyClass}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Next overwrite the ItemTemplate to draw each item using a TextBlock
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
And add an ItemContainerStyle that binds Canvas.Left and Canvas.Top properties to X,Y properties on your object
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
And this will take a List of MyClass objects, and render them to the screen inside a Canvas, with each item positioned at the specified X,Y coordinates.
With all that being said, are you sure this is what you want? WPF has much better layout panels than WinForms, and you don't have to position every element according to an X,Y coordinate if you don't want to.
For a quick visual introduction of WPF's Layouts, I'd recommend this link : WPF Layouts - A Visual Quick Start
Also since it sounds like you're new to WPF and coming from a WinForms background, you may find this answer to a related question useful : Transitioning from Windows Forms to WPF
WPF layout involves choosing a layout container and placing your controls in it. There are several different containers:
The Grid container is a powerful tool for laying out your form in rows and columns. You have complete control over the size of each cell, and you can have rows or columns "span" each other.
The DockPanel container allows you to "dock" controls to the edges of your window or the center. You'd use it to layout a window with smart icon bars, ribbons, status windows, and toolboxes, like Visual Studio itself.
The StackPanel container can be used to stack controls either on top of each other or next to each other
The UniformGrid container is a less powerful version of the container that keeps all cells the same size.
The Canvas container allows you to specify the X & Y coordinates of your controls.
There are one or two others but these are the ones I've used.
The bad thing about laying out a form using X & Y coordinates is that the form does not handle resizing well. This can be exacerbated when you support globalization, as the labels and such for a string may be a lot longer in a foreign language. The best example off the top of my head is Spanish. A lot of English phrases, when translated to Spanish, are a lot longer.
The Grid container gives you the most control over layout. Columns can automatically size themselves to the longest string in the column, while the rest of the columns adjust themselves as necessary, again automatically. You don't have to write one line of code to get that effect; it's all there in the Grid control out of the box.
If you insist on laying out your form the Winforms way, use a Canvas. But you're not going to get the benefit of using the more advanced layout facilities in the other containers, especially the Grid control. I use that almost exclusively in my forms.
EDIT
Using layout controls other than Canvas means that you think about layout differently in WPF than in WinForms. You work at a higher conceptual level and leave the details about figuring out where on the screen a particular control will be displayed to WPF. You also don't have things like the WinForms Anchor property in WPF, which always seemed kind of a hack to me.
The WPF was designed to offer a power and rich framework for designer which make it a different from the classic winforms. You can achieve what want by adding your TextBox control to a canvas and changing the attached property following is a full example illustrating this:
MainWindow
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Name="mainCanvas" Margin="31,-10,-31,10">
<TextBox Name="myTextBox" Canvas.Left="131" Canvas.Top="109" Height="84" Width="135"></TextBox>
<Button Content="Button" Height="62" Canvas.Left="271" Canvas.Top="69" Width="91" Click="Button_Click"/>
</Canvas>
</Grid>
</Window>
Code Behind
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myTextBox.SetValue(Canvas.LeftProperty,(double)myTextBox.GetValue(Canvas.LeftProperty)+50.0);
}
}
}
If you want to position the TextBoxes in a grid-way, use Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
...
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" />
<TextBox Grid.Row="0" Grid.Column="2" />
...
<TextBox Grid.Row="1" Grid.Column="0" />
<TextBox Grid.Row="1" Grid.Column="1" />
<TextBox Grid.Row="1" Grid.Column="2" />
...
</Grid>

How to align RibbonGroup to the right?

I use RibbonControlsLibrary. How to align one RibbonGroup to the right? It should be only one group in tab. All other groups should be aligned to the left.
You cannot align the RibbonGroup to the right. The Ribbon doesn't provide the ability to do this.
What you can do is to align page header items... but I don't know if it's enough for you:
DevExpress
I had the same issue, and I finally found something to do this :
I have 3 RibbonGroupBox. Groupe1 may be aligned on left, Groupe3 may be aligned on right. Groupe2 is just an empty RibbonGroupBox I inserted between Groupe1 and Groupe3.
Code XAML :
<Fluent:Ribbon DockPanel.Dock="Top" Title="{x:Static p:Resources.MiseEnBarre}" x:Name="mainRibbon">
<Fluent:RibbonTabItem x:Name="MainMenu" Header="{x:Static p:Resources.MainMenu}" SizeChanged="MainMenu_SizeChanged">
<Fluent:RibbonGroupBox x:Name="Groupe1">
<Fluent:Button x:Name="autoNest" SizeDefinition="Large" LargeIcon="img\image_bar_Nesting.png" Header="{x:Static p:Resources.MenuAutoNest}" Click="AutoNest_Click" />
<Fluent:Button x:Name="saveFile" SizeDefinition="Large" LargeIcon="img\image_save.png" Header="{x:Static p:Resources.MenuSauvegarder}" Click="Sauvegarder_Click" />
</Fluent:RibbonGroupBox>
<Fluent:RibbonGroupBox x:Name="Groupe2">
</Fluent:RibbonGroupBox>
<Fluent:RibbonGroupBox x:Name="Groupe3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=AvailableCNClist}" HorizontalAlignment="Left"/>
<TextBlock Grid.Column="1" Text="{Binding Path=AvailableCNClist2}" HorizontalAlignment="Right"/>
</Grid>
</Fluent:RibbonGroupBox>
</Fluent:RibbonTabItem>
</Fluent:Ribbon>
Then to manage the Windows redimensioning, I add on my main window the event SizeChanged="MainWindow_SizeChanged" (In the case your RibbonGroupBox dimensions could also change, just add the same event on them).
private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateAlignRibbon();
}
private void UpdateAlignRibbon()
{
Groupe2.Width = MyWindow.ActualWidth - Groupe1.ActualWidth - Groupe3.ActualWidth;
}
In my case the Groupe3 RibbonGroupBox may change of dimension, so I call UpdateAlignRibbon() from 3 points :
After Initialization of my window(including defining the GroupBoxes content)
When the MainWindow has its dimensions changed
When Groupe1 or Groupe3 have its dimensions changed
Rover, You can try add RibbonGroup between last left RibbonGroup and Right align RibbonGroup and assign size to newly added ribbon related to window size.
example <RibbonGroup Width="400"></RibbonGroup>
it's looks following image
You can sort of hack in alignment but I'd recommend against it.
<r:RibbonGroup Header="This is a Filler Header With No Functionality but to Take Up Space" Visibility="Hidden">
<s:RibbonButton2/>
<s:RibbonButton2/>
<s:RibbonButton2/>
<s:RibbonButton2/>
</r:RibbonGroup>
The customer wanted their logo on the ribbon across the top of the page, but the more you add "false" elements to the bar, the quicker "true" elements will collapse when shrinking the window size.
Try this:
<RibbonTab Header="Home" x:Name="rtabHome" FlowDirection="RightToLeft" >
<RibbonGroup Header="Group">
<TextBlock Text="Example"/>
</RibbonGroup>
</RibbonTab>
Works with FlowDirection="RightToLeft".

How to implement "Mega Menus" in WPF?

I'm trying to implement "Mega Menu" style menus using WPF. To see examples of mega menus in web design, see here.
So far, I've tried creating a similar interface by using TextBlocks as the highest level of the menu, and then using the mouse hover event to display an additional window that appears positioned below the text block. This is cumbersome and inflexible, future changes would require adding/removing TextBlocks dynamically.
I have considered using the WPF Menu control, because I know the styles can be dramatically modified, but I haven't seen any way to produce multi-column layouts with the hierarchical model that the Menu control uses.
Is there a better way to do this? Am I going to have to stick with custom windows and relative positioning? Can someone point me to an example of this that has already been implemented?
Instead of using custom Windows and positioning, you could use a Popup control. Your can use the StaysOpen=false setting to have it close when the user clicks off-screen.
If you can settle for clicking a menu item instead of hovering, the following custom control will work:
[TemplatePart(Name="PART_HoverArea", Type=typeof(FrameworkElement))]
[TemplatePart(Name="PART_Popup", Type=typeof(Popup))]
public class MegaMenuItem : HeaderedContentControl
{
private FrameworkElement hoverArea;
private Popup popup;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Unhook old template
if (hoverArea != null)
{
hoverArea.PreviewMouseUp -= ShowPopupOnMouseDown;
}
hoverArea = null;
popup = null;
if (Template == null)
return;
// Hook up new template
hoverArea = (FrameworkElement)Template.FindName("PART_HoverArea", this);
popup = (Popup)Template.FindName("PART_Popup", this);
if (hoverArea == null || popup == null)
return;
hoverArea.PreviewMouseUp += ShowPopupOnMouseDown;
}
private void ShowPopupOnMouseDown(object sender, MouseEventArgs e)
{
popup.PlacementTarget = hoverArea;
popup.Placement = PlacementMode.Bottom;
popup.StaysOpen = false;
popup.IsOpen = true;
}
}
You would need a style to display it - something like this. Note the PART_ template part names:
<Style TargetType="WpfApplication14:MegaMenuItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="WpfApplication14:MegaMenuItem">
<Grid>
<Border Name="PART_HoverArea" Background="#fb9c3b" BorderBrush="White" BorderThickness="0,0,1,0">
<ContentPresenter Content="{TemplateBinding Header}" />
</Border>
<Popup
Name="PART_Popup"
PlacementTarget="{Binding ElementName=HoverArea}"
>
<Border MinWidth="100" MaxWidth="400" MinHeight="40" MaxHeight="200" Background="#0d81c3">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The XAML for your menu would then be:
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<WpfApplication14:MegaMenuItem Header="Parent 1">
<WrapPanel Margin="5">
<TextBlock Text="Put any content you want here" Margin="5" />
<TextBlock Text="Put any content you want here" Margin="5" />
<TextBlock Text="Put any content you want here" Margin="5" />
</WrapPanel>
</WpfApplication14:MegaMenuItem>
<WpfApplication14:MegaMenuItem Header="Parent 2">
<WrapPanel Margin="5">
<TextBlock Text="Put any content you want here" Margin="5" />
<TextBlock Text="Put any content you want here" Margin="5" />
<TextBlock Text="Put any content you want here" Margin="5" />
</WrapPanel>
</WpfApplication14:MegaMenuItem>
</StackPanel>
Making the menu appear on hover is much harder, because of the way Popups steal focus (you can show the menu, but you can't easily hide it if they mouse over another menu). For that a custom window might work better.
You could use a HeaderedItemsControl and swap out the Panel to suit your needs; by default it uses a StackPanel however a WrapPanel may suit you better. The pop out and mouse over behavior do not exist by default and would need to be implemented.
A more robust approach would be to leverage a custom Expander; as it provides the pop out behavior you are after and the linked to walkthrough provides the mouse over behavior.
I wonder if the Ribbon control can be retrofitted to do this? It provides tabs, labels, columns and all that.
Please use this UI design sparingly and make sure that it only opens and closes when the user specifically requests such. It's tremendously annoying when a popup mega-menu appears over a website I'm viewing, and I can't get it to close, except for when I want to click on it and it goes away.
Custom windows and relative position are essentially how the WPF Menu/MenuItem control works... but as you've found, it's non-trivial. Best bet would be to retemplate the Menu/MenuItem controls to meet your need.

Categories