Search code examples
wpfxamlwpf-controlsribboncontrolslibrary

WPF RibbonWindow doesn't show Grip to resize window


I have a RibbonWindow where my WindowStyle is set to None, so what I can't understand is what have happened to the Grip to resize the window?! Even if my controls have their Margin set to 0 in the Bottom part of them will be hidden... It's a strange behaviour.

But if I change the bottom Margin of the controls it's ok, but the Grip can't be seen anyway, probably because part of the client area is hidden...

I have to say, if a have a WPF Window, this doesn't happen, it only happens with the RibbonWindow. And I am using the RibbonWindow because the Ribbon has other look in the proper window.

So what can I do to solve the problem with the Grip?

Some of my code...

<rib:RibbonWindow x:Class="MyApp.Views.MainView"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:rib="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
                  AllowsTransparency="True"
                  Background="Transparent"
                  Height="750"
                  ResizeMode="CanResizeWithGrip"
                  Width="1000"
                  WindowStartupLocation="CenterScreen"
                  WindowStyle="None">

    <Grid Margin="0, 0, 0, 20">
        <Border Background="Black"
                CornerRadius="5"
                Opacity="0.5"/>
    </Grid>
</rib:RibbonWindow>

Thanks in advance!


Solution

  • This was intriguing to debug. Turns out that the style for the window has a bug: if the system is defined to have IsGlassEnabled == true (in Win7 Aero theme makes it true) then the window relies on the Microsoft.Windows.Shell.WindowChrome (from Microsoft.Windows.Shell assembly) to draw the border and the top buttons of the window; and this WindowChrome has its GlassFrameThickness property set to 8,30,8,8 in combination with NonClientFrameEdges set to Bottom.

    What happens is that because of AllowsTransparency == true the glass border is transparent but the window still "cuts" its thickness from the overall window size because the WindowChrome defines NonClientFrameEdges="Bottom", thus cutting the ResizeGrip from view.

    You can see this if you (frantically) drag the window over your screen - you'll see the ResizeGrip flickering.

    To solve this we need to define a new WindowChrome with NonClientFrameEdges="None" (or GlassFrameThickness = 0 or both) and assign it to the window only when IsGlassEnabled == true && AllowsTransparency == true (I'm using application resources defined in App.xaml and defining only NonClientFrameEdges="None"):

    1. Add these namespaces to App.xaml:
        xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
        xmlns:ribbonPrimitives="clr-namespace:Microsoft.Windows.Controls.Ribbon.Primitives;assembly=RibbonControlsLibrary"
        xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
    
    2. Add these resources:
        <ribbonPrimitives:RibbonWindowSmallIconConverter x:Key="RibbonWindowSmallIconConverter" />
        <shell:WindowChrome x:Key="WindowChromeWithGlassAndTransparency"
                            NonClientFrameEdges="None" />
        <Style x:Key="MyStyle"
               TargetType="{x:Type ribbon:RibbonWindow}">
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsGlassEnabled, Source={x:Static shell:SystemParameters2.Current}}"
                                   Value="True" />
                        <Condition Binding="{Binding AllowsTransparency, RelativeSource={RelativeSource Mode=Self}}"
                                   Value="False" />
                    </MultiDataTrigger.Conditions>
                    <Setter Property="shell:WindowChrome.WindowChrome"
                            Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ribbon:Ribbon}, ResourceId=WindowChromeAeroWithGlass}}" />
                </MultiDataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsGlassEnabled, Source={x:Static shell:SystemParameters2.Current}}"
                                   Value="True" />
                        <Condition Binding="{Binding AllowsTransparency, RelativeSource={RelativeSource Mode=Self}}"
                                   Value="True" />
                    </MultiDataTrigger.Conditions>
                    <Setter Property="shell:WindowChrome.WindowChrome"
                            Value="{DynamicResource WindowChromeWithGlassAndTransparency}" />
                </MultiDataTrigger>
                <DataTrigger Binding="{Binding Path=IsGlassEnabled, Source={x:Static shell:SystemParameters2.Current}}"
                             Value="True">
                    <!--This is the original setter of the chrome that makes all the trouble-->
                    <!--<Setter Property="shell:WindowChrome.WindowChrome"
                        Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ribbon:Ribbon}, ResourceId=WindowChromeAeroWithGlass}}" />-->
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ribbon:RibbonWindow}">
                                <Grid>
                                    <Border Name="PART_ClientAreaBorder"
                                            Background="{TemplateBinding Control.Background}"
                                            BorderBrush="{TemplateBinding Control.BorderBrush}"
                                            BorderThickness="{TemplateBinding Control.BorderThickness}"
                                            Margin="{Binding Path=WindowNonClientFrameThickness, Source={x:Static shell:SystemParameters2.Current}}" />
                                    <Border BorderThickness="{Binding Path=(shell:WindowChrome.WindowChrome).ResizeBorderThickness, RelativeSource={RelativeSource TemplatedParent}}">
                                        <Grid>
                                            <Image Name="PART_Icon"
                                                   shell:WindowChrome.IsHitTestVisibleInChrome="True"
                                                   HorizontalAlignment="Left"
                                                   VerticalAlignment="Top"
                                                   Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon, Converter={StaticResource RibbonWindowSmallIconConverter }}"
                                                   Width="{Binding Path=SmallIconSize.Width, Source={x:Static shell:SystemParameters2.Current}}"
                                                   Height="{Binding Path=SmallIconSize.Height, Source={x:Static shell:SystemParameters2.Current}}" />
                                            <AdornerDecorator>
                                                <ContentPresenter Name="PART_RootContentPresenter" />
                                            </AdornerDecorator>
                                            <ResizeGrip Name="WindowResizeGrip"
                                                        shell:WindowChrome.ResizeGripDirection="BottomRight"
                                                        HorizontalAlignment="Right"
                                                        VerticalAlignment="Bottom"
                                                        Visibility="Collapsed"
                                                        IsTabStop="False" />
                                        </Grid>
                                    </Border>
                                </Grid>
                                <ControlTemplate.Triggers>
                                    <Trigger Value="{x:Null}"
                                             Property="Icon">
                                        <Setter TargetName="PART_Icon"
                                                Property="Source"
                                                Value="/RibbonControlsLibrary;component/Images/GlassyDefaultSystemIcon.png" />
                                    </Trigger>
                                    <Trigger Property="WindowState"
                                             Value="Maximized">
                                        <Setter TargetName="PART_Icon"
                                                Property="Margin"
                                                Value="0,2,0,0" />
                                    </Trigger>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="ResizeMode"
                                                       Value="CanResizeWithGrip" />
                                            <Condition Property="WindowState"
                                                       Value="Normal" />
                                        </MultiTrigger.Conditions>
                                        <Setter TargetName="WindowResizeGrip"
                                                Property="Visibility"
                                                Value="Visible" />
                                    </MultiTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    
    3. In your window use the new style:
        <rib:RibbonWindow ....
                          Style="{StaticResource MyStyle}">
    
            ....
        </rib:RibbonWindow>
    

    The DataTrigger is almost as is from the original style, with only one modification: I've commented the setter for the WindowChrome.

    The MultiDataTriggers are my addition. They check the value of AllowsTransparency property and apply the right WindowChrome: the original if the value is false and one with NonClientFrameEdges="None" (you can also use GlassFrameThickness = 0 instead) if the value is true.

    NOTE: this solution removes the ability to resize the window using the edges, resizing of the window is done only by the ResizeGrip.