WPF WindowChrome: Edges of maximized Window are out of the screen

…衆ロ難τιáo~ 提交于 2020-06-22 08:34:25

问题


I use WindowChrome to customize a Window. When I maximize the Window, then the edges are out of the screen. I use the following code to fix this:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <WindowChrome.WindowChrome>
        <WindowChrome CaptionHeight="50" CornerRadius="0" GlassFrameThickness="0" NonClientFrameEdges="None" ResizeBorderThickness="5" UseAeroCaptionButtons="False" />
    </WindowChrome.WindowChrome>

    <Grid>  
        <Grid.Style>
            <Style TargetType="{x:Type Grid}">
                <Setter Property="Margin" Value="0" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
                        <Setter Property="Margin" Value="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Style>
        <Border BorderThickness="2" BorderBrush="Blue" Background="Yellow" />
    </Grid>

</Window>

My problem: How can I get the correct number of pixels, so that the edges are not out of the screen.

SystemParameters.WindowResizeBorderThickness contains not the correct value.


回答1:


The WindowChrome will basically overlap by the size of ResizeBorderThickness when maximized.

If you want your Window completely visible while maximized just use the WindowChrome ResizeBorderThickness (5px) as Margin in your Grid style:

<Setter Property="Margin" Value="5" />

Otherwise if you want your Border's BorderThickness not to be visible while the window is maximized, you should use your Border's BorderThickness (2px) as Margin in addition to the WindowChrome ResizeBorderThickness (5px) in your Grid style. Then the Margin would be 7px.




回答2:


Example of how to increase border thickness when a window is maximised. Otherwise, due to oddities of WindowChrome, part of the border will disappear.

This example also strips out the standard window header, so you have to add your own minimize/maximize/close buttons.

<Window ResizeMode="CanResizeWithGrip"
        WindowStyle="SingleBorderWindow">
    <!-- Remove window header and border. Use with ResizeMode="CanResizeWithGrip" and WindowStyle="SingleBorderWindow". -->
    <WindowChrome.WindowChrome>
        <WindowChrome     
            CaptionHeight="1"  
            CornerRadius ="0"
            ResizeBorderThickness="4"         
            GlassFrameThickness="0">
        </WindowChrome>
    </WindowChrome.WindowChrome>            
    <Border BorderThickness="1">     
        <Border.Style>
            <Style TargetType="{x:Type Border}">
                <Style.Triggers>
                    <!-- Add to avoid border disappearing when window is maximised -->
                    <DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType=Window}}" 
                                 Value="Maximized">
                        <Setter Property="Margin" Value="10"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType=Window}}" 
                                 Value="Normal">
                        <Setter Property="Margin" Value="0"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Border.Style>
        <Grid>
           <!-- Window XAML here. -->
        <Grid>
     </Border>
 </Window>



回答3:


Why is this happening?

Raymond Chen has explained this in his note Why are the dimensions of a maximized window larger than the monitor?:

The extra eight pixels that hang off the screen are the window borders. When you maximize a window, the window manager arranges things so that the client area of the window fills the width of your work area, and it plus the caption bar fills the height of the work area. After all, you want to see as much of your document as possible; there’s no need to show you the window borders that aren’t doing you any good. (It leaves the caption bar on the screen for obvious reasons.)

How to handle it?

You have chosen the right approach. You basically have to add a border (or specify a margin) around your window each time it maximizes. And SystemParameters.WindowResizeBorderThickness is also the right property, but ... it has a bug.

For me, it gives 4px border instead of correct 8px for 100% display scale (can be changed in Display Settings) and approximately 3px instead of correct 7px for 175%.

Why I'm so sure it's a bug and not just the wrong property?

Internally SystemParameters.WindowResizeBorderThickness uses GetSystemMetrics call to obtain device pixels of the border. It queries for CXFRAME (32) and CYFRAME(33) indexes, and then converts it into logical pixels.

Now check this old bug report:

Take the following lines of code:

    int cx = ::GetSystemMetrics(SM_CXSIZEFRAME);
    int cy = ::GetSystemMetrics(SM_CYSIZEFRAME);
    int cp = ::GetSystemMetrics(SM_CYCAPTION);

When compiled with Visual Studio 2010, on my Windows7 machine this yields: cx == 9, cy == 9 and cp == 27.

If I compile the very same code with Vision Studio 2012 RC on the same machine, this yields: cx == 5, cy == 5 and cp == 27.

And the answer:

Posted by Microsoft on 7/12/2012 at 11:03 AM Thank you for your bug submission. The issue you reported appears to be a Windows issue, and we have forwarded the bug to them.

But there's some good news from this question: GetSystemMetrics() returns different results for .NET 4.5 & .NET 4.0

For .NET 4.5 and later you can just add SM_CXPADDEDBORDER (92) value to both CXFRAME (32) and CYFRAME(33) to get the right values.

What's the final solution?

Use the fixed property:

public static class SystemParametersFix
{
    public static Thickness WindowResizeBorderThickness
    {
        get
        {
            float dpix = GetDpi(GetDeviceCapsIndex.LOGPIXELSX);
            float dpiy = GetDpi(GetDeviceCapsIndex.LOGPIXELSY);

            int dx = GetSystemMetrics(GetSystemMetricsIndex.CXFRAME);
            int dy = GetSystemMetrics(GetSystemMetricsIndex.CYFRAME);

            // this adjustment is needed only since .NET 4.5 
            int d = GetSystemMetrics(GetSystemMetricsIndex.SM_CXPADDEDBORDER);
            dx += d;
            dy += d;

            var leftBorder = dx / dpix;
            var topBorder = dy / dpiy;

            return new Thickness(leftBorder, topBorder, leftBorder, topBorder);
        }
    }

    [DllImport("user32.dll")]
    private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("user32.dll")]
    private static extern IntPtr GetDC(IntPtr hwnd);

    [DllImport("gdi32.dll")]
    private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

    private static float GetDpi(GetDeviceCapsIndex index)
    {
        IntPtr desktopWnd = IntPtr.Zero;
        IntPtr dc = GetDC(desktopWnd);
        float dpi;
        try
        {
            dpi = GetDeviceCaps(dc, (int)index);
        }
        finally
        {
            ReleaseDC(desktopWnd, dc);
        }
        return dpi / 96f;
    }

    private enum GetDeviceCapsIndex
    {
        LOGPIXELSX = 88,
        LOGPIXELSY = 90
    }

    [DllImport("user32.dll")]
    private static extern int GetSystemMetrics(GetSystemMetricsIndex nIndex);

    private enum GetSystemMetricsIndex
    {
        CXFRAME = 32,
        CYFRAME = 33,
        SM_CXPADDEDBORDER = 92
    }
}



回答4:


Based on the OP's original example, they were almost there...

When the window is maximised, Windows seems to ignore the ResizeBorderThickness value. Using <Setter Property="Margin" Value="7" /> seems to work, but this value may need to be changed depending on the operating system (I tested this on Windows 10).

I recommend making a couple of small tweaks (see code below), such as adding WindowStyle="None" and ResizeMode="CanResize" to Window, as well as moving the Style out into either Window.Resources, Application.Resources or even a ResourceDictionary, changing the style's TargetType to "{x:Type Panel}" and using a key name (eg: x:Key="WindowMainPanelStyle"), as this will prevent the style automatically being applied to any child Grid elements, as well as allowing the style to be used with any elements that inherit from Panel (such as Border, DockPanel, Grid, StackPanel, etc).

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    WindowStyle="None"
    ResizeMode="CanResize">

<WindowChrome.WindowChrome>
    <WindowChrome CaptionHeight="50" ResizeBorderThickness="5" />
</WindowChrome.WindowChrome>

<Window.Resources>
    <Style TargetType="{x:Type Panel}" x:Key="WindowMainPanelStyle">
        <Setter Property="Margin" Value="0" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
                <Setter Property="Margin" Value="7" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid Style="{StaticResource WindowMainPanelStyle}">
    <Border BorderThickness="2" BorderBrush="Blue" Background="Yellow" />
</Grid>

</Window>


来源:https://stackoverflow.com/questions/36725769/wpf-windowchrome-edges-of-maximized-window-are-out-of-the-screen

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!