Home » Programming » WPF » WPF Ribbon » WPF Ribbon (CTP) – Part 4 – Resizing Groups

WPF Ribbon (CTP) – Part 4 – Resizing Groups

Resizing Groups

Continuing with the example from the end of part 3 of the tutorial, I will show you how groups resizing is done, how you can change the order the groups resize and also how you can prevent a group from collapsing.

Entire Tutorial:

Summary

 

  • Step 1:The Default Group Resizing
  • Step 2: Specifying the Group Size Reduction Order
  • Step 3: Preventing a RibbonGroup from resizing and collapsing
  • Step 4:Resizing Issues

Step 1:The Default Group Resizing

Now that we have a lot of RibbonGroups on the RibbonTab, we can specify the resize order. So, let’s suppose that we have a lot of RibbonGroups with no GroupSizeDefinitions set, like this:

<ribbon:RibbonTab Name="RibbonTabFull" Label="Full Ribbon Tab" >
   <ribbon:RibbonGroup Name="ButtonsGroup" HasDialogLauncher="False" Command="{StaticResource ButtonsGroupCommand}">
      <ribbon:RibbonButton Command="{StaticResource OkButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource AddButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource CloseButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource DeleteButtonCommand}"></ribbon:RibbonButton>
    </ribbon:RibbonGroup>
    <ribbon:RibbonGroup Name="ToggleButtonsGroup" Command="{StaticResource ToggleButtonsGroupCommand}">
       <ribbon:RibbonToggleButton IsChecked="True" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="False" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="True" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="False" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="{x:Null}" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="DropDownButtonsGroup" Command="{StaticResource DropDownButtonsGroupCommand}">
        <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
        <ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton>
        <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
        <ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton.Items>
 </ribbon:RibbonDropDownButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="SplitButtonsGroup" Command="{StaticResource SplitButtonsGroupCommand}">
       <ribbon:RibbonSplitButton Command="{StaticResource SplitButtonCommand}" >
          <ribbon:RibbonSplitButton.Items></ribbon:RibbonSplitButton.Items>
       </ribbon:RibbonSplitButton>
       <ribbon:RibbonSplitButton Command="{StaticResource SplitButtonCommand}" >
          <ribbon:RibbonSplitButton.Items></ribbon:RibbonSplitButton.Items>
       </ribbon:RibbonSplitButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="CheckboxesGroup" Command="{StaticResource CheckboxGroupCommand}">
       <ribbon:RibbonCheckBox IsChecked="True" IsThreeState="False" Command="{StaticResource CheckBox1Command}"></ribbon:RibbonCheckBox>
       <ribbon:RibbonCheckBox IsChecked="False" IsThreeState="False" Command="{StaticResource CheckBox2Command}"></ribbon:RibbonCheckBox>
       <ribbon:RibbonCheckBox IsEnabled="False" Command="{StaticResource CheckBox3Command}">  </ribbon:RibbonCheckBox>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="TextGroup" Width="120" Command="{StaticResource TextGroupCommand}">
      <ribbon:RibbonControlGroup>
         <ribbon:RibbonTextBox Command="{StaticResource TextBoxCommand}"></ribbon:RibbonTextBox>
      </ribbon:RibbonControlGroup>
      <ribbon:RibbonControlGroup>
         <ribbon:RibbonLabel Content="Font Size:"></ribbon:RibbonLabel>
         <ribbon:RibbonComboBox SelectedIndex="0" IsDropDownOpen="False" IsEditable="True">
            <ribbon:RibbonComboBox.Items>
                <ribbon:RibbonComboBoxItem Content="10"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="11"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="12"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="13"></ribbon:RibbonComboBoxItem>
            </ribbon:RibbonComboBox.Items>
          </ribbon:RibbonComboBox>
       </ribbon:RibbonControlGroup>
    </ribbon:RibbonGroup>
 </ribbon:RibbonTab>

By default, the controls will tend to be large and yet maintain a pleasant layout and fit inside the ribbon width. If we do not specify any resize order, we will get the default behaviour: the groups will start to resize from right to left, until they are all collapsed.

Step 2: Specifying the Group Size Reduction Order

But, if we specify the

<ribbon:RibbonTab Name="RibbonTabFull" Label="Full Ribbon Tab" GroupSizeReductionOrder="ButtonsGroup, ButtonsGroup, ToggleButtonsGroup, ToggleButtonsGroup, DropDownButtonsGroup, SplitButtonsGroup, CheckboxesGroup, TextGroup">

then we will get a different behaviour:

Step 3: Preventing a RibbonGroup from resizing and collapsing

Well, what if you don’t want a group to resize? That’s simple, you just have to specify the GroupSizeDefinition for all its inner controls. Let’s apply this change to the first group in the Full Ribbon Tab:

<ribbon:RibbonTab Name="RibbonTabFull" Label="Full Ribbon Tab" >
   <ribbon:RibbonGroup Name="ButtonsGroup" HasDialogLauncher="False" Command="{StaticResource ButtonsGroupCommand}">
      <ribbon:RibbonGroup.GroupSizeDefinitions>
         <ribbon:RibbonGroupSizeDefinitionCollection>
             <ribbon:RibbonGroupSizeDefinition>
                  <!-- size definition for Ok Button -->
                  <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="False"/>
                  <!-- size definition for Add Button -->
                  <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="True"/>
                  <!-- size definition for Close Button -->
                  <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="True"/>
                   <!-- size definition for Delete Button -->
                   <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="True"/>
              </ribbon:RibbonGroupSizeDefinition>
          </ribbon:RibbonGroupSizeDefinitionCollection>
      </ribbon:RibbonGroup.GroupSizeDefinitions>
      <ribbon:RibbonButton Command="{StaticResource OkButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource AddButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource CloseButtonCommand}"></ribbon:RibbonButton>
      <ribbon:RibbonButton Command="{StaticResource DeleteButtonCommand}"></ribbon:RibbonButton>
    </ribbon:RibbonGroup>
    <ribbon:RibbonGroup Name="ToggleButtonsGroup" Command="{StaticResource ToggleButtonsGroupCommand}">
       <ribbon:RibbonToggleButton IsChecked="True" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="False" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="True" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="False" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
       <ribbon:RibbonToggleButton IsChecked="{x:Null}" Command="{StaticResource ToggleButtonCommand}" ></ribbon:RibbonToggleButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="DropDownButtonsGroup" Command="{StaticResource DropDownButtonsGroupCommand}">
        <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
        <ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton>
        <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
        <ribbon:RibbonDropDownButton.Items></ribbon:RibbonDropDownButton.Items>
 </ribbon:RibbonDropDownButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="SplitButtonsGroup" Command="{StaticResource SplitButtonsGroupCommand}">
       <ribbon:RibbonSplitButton Command="{StaticResource SplitButtonCommand}" >
          <ribbon:RibbonSplitButton.Items></ribbon:RibbonSplitButton.Items>
       </ribbon:RibbonSplitButton>
       <ribbon:RibbonSplitButton Command="{StaticResource SplitButtonCommand}" >
          <ribbon:RibbonSplitButton.Items></ribbon:RibbonSplitButton.Items>
       </ribbon:RibbonSplitButton>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="CheckboxesGroup" Command="{StaticResource CheckboxGroupCommand}">
       <ribbon:RibbonCheckBox IsChecked="True" IsThreeState="False" Command="{StaticResource CheckBox1Command}"></ribbon:RibbonCheckBox>
       <ribbon:RibbonCheckBox IsChecked="False" IsThreeState="False" Command="{StaticResource CheckBox2Command}"></ribbon:RibbonCheckBox>
       <ribbon:RibbonCheckBox IsEnabled="False" Command="{StaticResource CheckBox3Command}">  </ribbon:RibbonCheckBox>
   </ribbon:RibbonGroup>
   <ribbon:RibbonGroup Name="TextGroup" Width="120" Command="{StaticResource TextGroupCommand}">
      <ribbon:RibbonControlGroup>
         <ribbon:RibbonTextBox Command="{StaticResource TextBoxCommand}"></ribbon:RibbonTextBox>
      </ribbon:RibbonControlGroup>
      <ribbon:RibbonControlGroup>
         <ribbon:RibbonLabel Content="Font Size:"></ribbon:RibbonLabel>
         <ribbon:RibbonComboBox SelectedIndex="0" IsDropDownOpen="False" IsEditable="True">
            <ribbon:RibbonComboBox.Items>
                <ribbon:RibbonComboBoxItem Content="10"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="11"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="12"></ribbon:RibbonComboBoxItem>
                <ribbon:RibbonComboBoxItem Content="13"></ribbon:RibbonComboBoxItem>
            </ribbon:RibbonComboBox.Items>
          </ribbon:RibbonComboBox>
       </ribbon:RibbonControlGroup>
    </ribbon:RibbonGroup>
 </ribbon:RibbonTab>

And the RibbonTab will look like the next image. You can clearly see that the ribbon’s scrollbar appeared, which denotes the fact that the tab cannot resize any groups, thus our RibbonGroup will maintain its size and layout and will not be resized nor collapsed when the window shricks:

Why wouldn’t I want a RibbonGroup to collapse? That is a tricky quation, because sometimes when it’s collapsed, the layout of the inner controls change. Take for example the DropDownMenu with MenuItems inside.

If not collapsed, the menu would look like this:

But when collapsed, the menu changes its appearance:

How can we solve this problem?

  1. We can use RibbonMenuItems instead of MenuItems, because they keep the menu appearance even when collapsed
  2. We make the RibbonGroup not resizable, like this:
<ribbon:RibbonGroup Name="DropDownButtonsGroup" Command="{StaticResource DropDownButtonsGroupCommand}">
   <ribbon:RibbonGroup.GroupSizeDefinitions>
      <ribbon:RibbonGroupSizeDefinitionCollection>
          <ribbon:RibbonGroupSizeDefinition>
               <!-- size definition for the first DropDownButton -->
               <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="False"/>
               <!-- size definition for the second DropDownButton -->
               <ribbon:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" IsImageVisible="True"/>
           </ribbon:RibbonGroupSizeDefinition>
        </ribbon:RibbonGroupSizeDefinitionCollection>
   </ribbon:RibbonGroup.GroupSizeDefinitions>
   <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
       <ribbon:RibbonDropDownButton.Items>
            <ribbon:RibbonMenuItem Header="Header" Background="LightBlue" Foreground="Blue" IsHitTestVisible="False"></ribbon:RibbonMenuItem>
             <ribbon:RibbonMenuItem Header="Option 1 - stays open on click" StaysOpenOnClick="True"></ribbon:RibbonMenuItem>
             <ribbon:RibbonMenuItem IsCheckable="True" IsChecked="True"  Header="Option 2 - checkable"></ribbon:RibbonMenuItem>
             <ribbon:RibbonSeparator></ribbon:RibbonSeparator>
             <ribbon:RibbonMenuItem Header="Option 3 - icon">
                 <ribbon:RibbonMenuItem.Icon>
                      <Image Width="16" Height="16" Source="images/positive-icon16.png"></Image>
                 </ribbon:RibbonMenuItem.Icon>
              </ribbon:RibbonMenuItem>
              <ribbon:RibbonMenuItem IsSubmenuOpen="True" Header="Option 4 - submenu">
                   <ribbon:RibbonMenuItem Header="Option 4.1"></ribbon:RibbonMenuItem>
                   <ribbon:RibbonMenuItem Header="Option 4.2"></ribbon:RibbonMenuItem>
              </ribbon:RibbonMenuItem>
          </ribbon:RibbonDropDownButton.Items>
       </ribbon:RibbonDropDownButton>
    <ribbon:RibbonDropDownButton Command="{StaticResource DropDownButtonCommand}" >
        <ribbon:RibbonDropDownButton.Items>
            <MenuItem Header="Header" Background="LightBlue" Foreground="Blue" IsHitTestVisible="False"></MenuItem>
             <MenuItem Header="Option 1 - stays open on click" StaysOpenOnClick="True"></MenuItem>
             <MenuItem IsCheckable="True" IsChecked="True"  Header="Option 2 - checkable"></MenuItem>
              <ribbon:RibbonSeparator></ribbon:RibbonSeparator>
              <MenuItem Header="Option 3 - icon">
                  <MenuItem.Icon>
                        <Image Width="16" Height="16" Source="images/positive-icon16.png"></Image>
                   </MenuItem.Icon>
               </MenuItem>
               <MenuItem IsSubmenuOpen="True" Header="Option 4 - submenu">
                   <MenuItem Header="Option 4.1"></MenuItem>
                   <MenuItem Header="Option 4.2"></MenuItem>
               </MenuItem>
            </ribbon:RibbonDropDownButton.Items>
        </ribbon:RibbonDropDownButton>
    </ribbon:RibbonGroup>

And this time, the DropDownButton with MenuItems keeps it appearance:

Step 4: Resizing Issues

Sometimes, if you have multiple RibbonTabs which have a lot of RibbonGroups, when you switch from tab with the larges width of RibbonGroups to the smaller one, you will get such an exception: ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

Stack Trace:

at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.get_Item(Int32 index)
at System.Collections.ObjectModel.Collection`1.get_Item(Int32 index)
at Microsoft.Windows.Controls.Ribbon.RibbonGroup.OnApplyTemplate()
at System.Windows.FrameworkElement.ApplyTemplate()
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.StackPanel.MeasureOverride(Size constraint)
at Microsoft.Windows.Controls.Ribbon.RibbonGroupsContainer.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
at System.Windows.Controls.ScrollContentPresenter.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)
at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at WPFRibbonExample.App.Main() in I:BlogWPFRibbonPart 2CodeWPFRibbonExampleWPFRibbonExampleobjDebugApp.g.cs:line 0
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

Unfortunately, I don’t know any workaroud for this issue, but let’s hope it will be fixed in V1

Source Code

You can download the source code for parts 2,3 and 4 of the tutorial from here.

Posted in WPF Ribbon and tagged as , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.