WPF 窗口位于处于最大化状态的顶部/左侧任务栏下方 [英] WPF window is under top/left placed taskbar in Maximized state

查看:28
本文介绍了WPF 窗口位于处于最大化状态的顶部/左侧任务栏下方的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发 WPF 应用程序,但我遇到的问题是,当 WindowStyle=NoneWindowState = WindowState.Maximized 应用程序位于顶部或左侧放置的任务栏下方时.>

当任务栏位于底部或右侧时,一切正常.

我知道窗口的 LeftTop 属性,但它们在 Maximized 状态下被忽略.

还有 Microsoft.Windows.Shell.WindowСhrome 可以拖动、双击最大化和恢复、捕捉和取消捕捉.(需要添加为dll引用)

我想实现我的应用程序不会隐藏或进入任务栏下,并且可以正常使用 WindowСhrome 提供的行为.

主窗口.xaml

 <WindowChrome.WindowChrome><WindowChrome CaptionHeight="{Binding ActualHeight,ElementName=topBarGrid}"/></WindowChrome.WindowChrome><Grid x:Name="mainGrid" Background="Yellow"><Grid.RowDefinitions><RowDefinition Height="自动"></RowDefinition><RowDefinition Height="*"></RowDefinition></Grid.RowDefinitions><Grid x:Name="topBarGrid" Grid.Row="0" ><Border BorderBrush="黑色" BorderThickness="1" ><DockPanel x:Name="panelForWindowControls"VerticalAlignment="拉伸"DockPanel.Dock="右"LastChildFill="假"><按钮名称="buttonExit"宽度=43"高度=28"Margin="0" Click="buttonExit_Click"DockPanel.Dock="Right" Content="x"WindowChrome.IsHitTestVisibleInChrome="True"/><按钮名称="buttonMax"宽度=43"高度=28"边距=0"点击=buttonMax_Click"DockPanel.Dock="Right" Content="[]"WindowChrome.IsHitTestVisibleInChrome="True"/><按钮名称="buttonMin"宽度=43"高度=28"边距=0"点击=buttonMin_Click"DockPanel.Dock="右" 内容="_"WindowChrome.IsHitTestVisibleInChrome="True"/></DockPanel></边框></网格><Grid x:Name="bodyGrid" Grid.Row="1"><Button Content="FullScreen" x:Name="FullScreenButton"高度="50" 宽度="200"水平对齐=中心"垂直对齐=中心"Click="FullScreenButton_Click"/></网格></网格></窗口>

MainWindow.xaml.cs

 使用 System.Windows;命名空间 WpfAppTestFullScreen{///<总结>///MainWindow.xaml 的交互逻辑///</总结>公共部分类 MainWindow : 窗口{公共主窗口(){初始化组件();MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight;MaxWidth = SystemParameters.MaximizedPrimaryScreenWidth;}私有无效 FullScreenButton_Click(对象发送者,RoutedEventArgs e){如果(WindowState == WindowState.Maximized){WindowState = WindowState.Normal;}别的{WindowState = WindowState.Maximized;}}私有无效buttonMax_Click(对象发送者,RoutedEventArgs e){如果(WindowState == WindowState.Maximized){WindowState = WindowState.Normal;}别的{WindowState = WindowState.Maximized;}}私有无效buttonMin_Click(对象发送者,RoutedEventArgs e){WindowState = (WindowState == WindowState.Minimized) ?WindowState.Normal : WindowState.Minimized;}私有无效buttonExit_Click(对象发送者,RoutedEventArgs e){}}}

问题截图如下:

解决方案

更好的方法是使用 User32.dll
中的几个本地方法(摘自 此博客发布)

工作原理:

  • 为 WindowMessages 添加一个钩子 (WindowProc) 以便您可以从系统接收窗口消息.<块引用>

    每个窗口都有一个关联的窗口过程——一个处理所有发送或发送到类的所有窗口的消息的函数.窗口外观和行为的所有方面都取决于窗口过程对这些消息的响应.

  • 您将收到信号 (0x24 = WM_GETMINMAXINFO) 窗口大小即将改变<块引用>

    当窗口的大小或位置即将改变时发送到窗口.应用程序可以使用此消息来覆盖窗口的默认最大化大小和位置,或其默认的最小或最大跟踪大小.

  • 您将获得窗口当前所在的监视器 (MonitorFromWindow) 并更新边界.<块引用>

    应用程序可以通过设置此结构的成员来覆盖默认值.

  • 系统会处理剩下的事情.

因此,无论窗口如何调整大小(拖动到屏幕顶部、Windows 键 + 箭头、最大化按钮等),它都会保持在工作区的边界内.

原生方法和类型:
(只需将此类复制到您的项目中)

public static class Native{[DllImport("user32")]内部静态 extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);[DllImport("user32")]内部静态外部 IntPtr MonitorFromWindow(IntPtr handle, int flags);public static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam, int minWidth, int minHeight){MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));//调整最大化的大小和位置以适合正确显示器的工作区域int MONITOR_DEFAULTONEAREST = 0x00000002;IntPtr 监视器 = Native.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);如果(监视器!= IntPtr.Zero){Native.MONITORINFO monitorInfo = new Native.MONITORINFO();Native.GetMonitorInfo(monitor, monitorInfo);Native.RECT rcWorkArea = monitorInfo.rcWork;Native.RECT rcMonitorArea = monitorInfo.rcMonitor;mmi.ptMaxPosition.x = Math.Abs​​(rcWorkArea.left - rcMonitorArea.left);mmi.ptMaxPosition.y = Math.Abs​​(rcWorkArea.top - rcMonitorArea.top);mmi.ptMaxSize.x = Math.Abs​​(rcWorkArea.right - rcWorkArea.left);mmi.ptMaxSize.y = Math.Abs​​(rcWorkArea.bottom - rcWorkArea.top);mmi.ptMinTrackSize.x = minWidth;mmi.ptMinTrackSize.y = minHeight;}Marshal.StructureToPtr(mmi, lParam, true);}///<总结>///POINT 又名 POINTAPI///</总结>[结构布局(LayoutKind.Sequential)]公共结构点{///<总结>///点的 x 坐标.///</总结>公共整数 x;///<总结>///点的 y 坐标.///</总结>公共信息;///<总结>///构造一个坐标点 (x,y).///</总结>公共点(int x,int y){this.x = x;this.y = y;}}[结构布局(LayoutKind.Sequential)]公共结构 MINMAXINFO{公共点 ptReserved;公共点 ptMaxSize;公共点 ptMaxPosition;公共点 ptMinTrackSize;公共点 ptMaxTrackSize;};///<总结>///</总结>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]公开课 MONITORINFO{///<总结>///</总结>public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));///<总结>///</总结>公共 RECT rcMonitor = 新 RECT();///<总结>///</总结>公共 RECT rcWork = 新 RECT();///<总结>///</总结>公共 int dwFlags = 0;}///<总结>Win32 </summary>[StructLayout(LayoutKind.Sequential, Pack = 0)]公共结构 RECT{///<总结>Win32 </summary>公共 int 左;///<总结>Win32 </summary>公共国际顶级;///<总结>Win32 </summary>公共信息权;///<总结>Win32 </summary>公共 int 底部;///<总结>Win32 </summary>公共静态只读 RECT Empty = new RECT();///<总结>Win32 </summary>公共整数宽度{get { return Math.Abs​​(right - left);}//BIDI OS 需要的 Abs}///<总结>Win32 </summary>公共整数高度{得到 { 返回底部 - 顶部;}}///<总结>Win32 </summary>公共 RECT(int 左,int 顶部,int 右,int 底部){this.left = 左;this.top = 顶部;this.right = 正确;this.bottom = 底部;}///<总结>Win32 </summary>公共 RECT(RECT rcSrc){this.left = rcSrc.left;this.top = rcSrc.top;this.right = rcSrc.right;this.bottom = rcSrc.bottom;}///<总结>Win32 </summary>public bool IsEmpty{得到{//BUGBUG : 在 Bidi OS (希伯来语阿拉伯语) left >对返回左 >= 右 ||顶部 >= 底部;}}///<总结>返回此结构体的用户友好表示 </summary>公共覆盖字符串 ToS​​tring(){if (this == RECT.Empty) { return "RECT {Empty}";}return "RECT { left :" + left + "/top : " + top + "/right : " + right + "/bottom : " + bottom + " }";}///<总结>确定 2 个 RECT 是否相等(深度比较)</summary>公共覆盖 bool 等于(对象 obj){if (!(obj is Rect)) { return false;}return (this == (RECT)obj);}///<summary>返回这个结构的HashCode(不保证是唯一的)</summary>公共覆盖 int GetHashCode(){return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();}///<总结>确定 2 个 RECT 是否相等(深度比较)</summary>公共静态布尔运算符 ==(RECT rect1, RECT rect2){return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);}///<总结>确定 2 个 RECT 是否不同(深度比较)公共静态布尔运算符 !=(RECT rect1, RECT rect2){返回 !(rect1 == rect2);}}}

您的窗口:

公共部分类 MainWindow : Window{公共主窗口(){SourceInitialized += Window_SourceInitialized;初始化组件();}void Window_SourceInitialized(对象发送者,EventArgs e){IntPtr handle = new WindowInteropHelper(this).Handle;HwndSource.FromHwnd(handle)?.AddHook(WindowProc);}private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool处理){开关(味精){案例 0x0024:Native.WmGetMinMaxInfo(hwnd, lParam, (int)MinWidth, (int)MinHeight);处理 = 真;休息;}返回 (IntPtr)0;}私有无效 FullScreenButton_Click(对象发送者,RoutedEventArgs e){WindowState = WindowState == WindowState.Maximized ?WindowState.Normal : WindowState.Maximized;}私有无效buttonMax_Click(对象发送者,RoutedEventArgs e){WindowState = WindowState == WindowState.Maximized ?WindowState.Normal : WindowState.Maximized;}私有无效buttonMin_Click(对象发送者,RoutedEventArgs e){WindowState = WindowState == WindowState.Minimized ?WindowState.Normal : WindowState.Minimized;}私有无效buttonExit_Click(对象发送者,RoutedEventArgs e){关闭();}}

I am working on WPF application and I faced problem that when WindowStyle=Noneand WindowState = WindowState.Maximized the application goes under top or left placed taskbar.

When taskbar is placed bottom or right all works normal.

I know about Left and Top properties of window, but they are ignored in Maximized state.

Also there is Microsoft.Windows.Shell.WindowСhrome that gives ability to drag, double click to maximize and restore, snap and unsnap. (it is need to be added as dll reference)

I want to achieve that my application don't hide or go under taskbar and works correctly with behavior that WindowСhrome provide.

MainWindow.xaml

    <Window x:Class="WpfAppTestFullScreen.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        WindowStyle="None"
        Title="MainWindow" Height="350" Width="525"
        Left="100" Top="100">
    <WindowChrome.WindowChrome>
        <WindowChrome CaptionHeight="{Binding ActualHeight,ElementName=topBarGrid}"/>
    </WindowChrome.WindowChrome>
    <Grid x:Name="mainGrid" Background="Yellow">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid x:Name="topBarGrid" Grid.Row="0" >
            <Border BorderBrush="Black" BorderThickness="1" >
                <DockPanel x:Name="panelForWindowControls"
                           VerticalAlignment="Stretch"
                           DockPanel.Dock="Right"
                           LastChildFill="False"
                           >
                    <Button Name="buttonExit"
                            Width="43" Height="28"
                            Margin="0" Click="buttonExit_Click"
                            DockPanel.Dock="Right" Content="x"
                            WindowChrome.IsHitTestVisibleInChrome="True"
                     />
                    <Button Name="buttonMax"
                            Width="43" Height="28"
                            Margin="0" Click="buttonMax_Click"
                            DockPanel.Dock="Right" Content="[]"
                            WindowChrome.IsHitTestVisibleInChrome="True"
                            />

                    <Button Name="buttonMin" 
                            Width="43" Height="28"
                            Margin="0" Click="buttonMin_Click"
                            DockPanel.Dock="Right" Content="_"
                            WindowChrome.IsHitTestVisibleInChrome="True"
                            />
                </DockPanel>
            </Border>
        </Grid>
        <Grid x:Name="bodyGrid"  Grid.Row="1">
            <Button Content="FullScreen" x:Name="FullScreenButton" 
                Height="50" Width="200" 
                HorizontalAlignment="Center"  VerticalAlignment="Center"
                Click="FullScreenButton_Click" />
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

    using System.Windows;


namespace WpfAppTestFullScreen
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight;
            MaxWidth = SystemParameters.MaximizedPrimaryScreenWidth;
        }


        private void FullScreenButton_Click(object sender, RoutedEventArgs e)
        {
            if (WindowState == WindowState.Maximized)
            {
                WindowState = WindowState.Normal;
            }
            else
            {
                WindowState = WindowState.Maximized;
            }
        }

        private void buttonMax_Click(object sender, RoutedEventArgs e)
        {
            if (WindowState == WindowState.Maximized)
            {
                WindowState = WindowState.Normal;
            }
            else
            {
                WindowState = WindowState.Maximized;
            }
        }

        private void buttonMin_Click(object sender, RoutedEventArgs e)
        {
            WindowState = (WindowState == WindowState.Minimized) ? WindowState.Normal : WindowState.Minimized;
        }
        private void buttonExit_Click(object sender, RoutedEventArgs e)
        {

        }

    }
}

Here is screenshot of problem:

解决方案

A better approach would be to use a couple of native methods from the User32.dll
(taken from this blog post)

How it works:

  • Add a hook for WindowMessages (WindowProc) so you can receive window messages from the system.

    Every window has an associated window procedure — a function that processes all messages sent or posted to all windows of the class. All aspects of a window's appearance and behavior depend on the window procedure's response to these messages.

  • You will get signaled (0x24 = WM_GETMINMAXINFO) that the window size is about to be changed

    Sent to a window when the size or position of the window is about to change. An application can use this message to override the window's default maximized size and position, or its default minimum or maximum tracking size.

  • You get the monitor that the window is currently located on (MonitorFromWindow) and update the bounds.

    An application can override the defaults by setting the members of this structure.

  • The system will take care of the rest.

So, no matter how the Window is resized (drag to top of screen, windows key + arrows, maximize button, etc.) it will stay within the boundary of your workarea.

Native Methods and Types:
(just copy this class into your project)

public static class Native
{

    [DllImport("user32")]
    internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

    [DllImport("user32")]
    internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

    public static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam, int minWidth, int minHeight)
    {
        MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Adjust the maximized size and position to fit the work area of the correct monitor
        int MONITOR_DEFAULTTONEAREST = 0x00000002;
        IntPtr monitor = Native.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

        if (monitor != IntPtr.Zero)
        {

            Native.MONITORINFO monitorInfo = new Native.MONITORINFO();
            Native.GetMonitorInfo(monitor, monitorInfo);
            Native.RECT rcWorkArea = monitorInfo.rcWork;
            Native.RECT rcMonitorArea = monitorInfo.rcMonitor;
            mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
            mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
            mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
            mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
            mmi.ptMinTrackSize.x = minWidth;
            mmi.ptMinTrackSize.y = minHeight;
        }

        Marshal.StructureToPtr(mmi, lParam, true);
    }


    /// <summary>
    /// POINT aka POINTAPI
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        /// <summary>
        /// x coordinate of point.
        /// </summary>
        public int x;
        /// <summary>
        /// y coordinate of point.
        /// </summary>
        public int y;

        /// <summary>
        /// Construct a point of coordinates (x,y).
        /// </summary>
        public POINT(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MINMAXINFO
    {
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
    };

    /// <summary>
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class MONITORINFO
    {
        /// <summary>
        /// </summary>            
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

        /// <summary>
        /// </summary>            
        public RECT rcMonitor = new RECT();

        /// <summary>
        /// </summary>            
        public RECT rcWork = new RECT();

        /// <summary>
        /// </summary>            
        public int dwFlags = 0;
    }


    /// <summary> Win32 </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public struct RECT
    {
        /// <summary> Win32 </summary>
        public int left;
        /// <summary> Win32 </summary>
        public int top;
        /// <summary> Win32 </summary>
        public int right;
        /// <summary> Win32 </summary>
        public int bottom;

        /// <summary> Win32 </summary>
        public static readonly RECT Empty = new RECT();

        /// <summary> Win32 </summary>
        public int Width
        {
            get { return Math.Abs(right - left); }  // Abs needed for BIDI OS
        }
        /// <summary> Win32 </summary>
        public int Height
        {
            get { return bottom - top; }
        }

        /// <summary> Win32 </summary>
        public RECT(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }


        /// <summary> Win32 </summary>
        public RECT(RECT rcSrc)
        {
            this.left = rcSrc.left;
            this.top = rcSrc.top;
            this.right = rcSrc.right;
            this.bottom = rcSrc.bottom;
        }

        /// <summary> Win32 </summary>
        public bool IsEmpty
        {
            get
            {
                // BUGBUG : On Bidi OS (hebrew arabic) left > right
                return left >= right || top >= bottom;
            }
        }
        /// <summary> Return a user friendly representation of this struct </summary>
        public override string ToString()
        {
            if (this == RECT.Empty) { return "RECT {Empty}"; }
            return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
        }

        /// <summary> Determine if 2 RECT are equal (deep compare) </summary>
        public override bool Equals(object obj)
        {
            if (!(obj is Rect)) { return false; }
            return (this == (RECT)obj);
        }

        /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
        public override int GetHashCode()
        {
            return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
        }


        /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
        public static bool operator ==(RECT rect1, RECT rect2)
        {
            return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
        }

        /// <summary> Determine if 2 RECT are different(deep compare)</summary>
        public static bool operator !=(RECT rect1, RECT rect2)
        {
            return !(rect1 == rect2);
        }
    }
}

Your Window:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        SourceInitialized += Window_SourceInitialized;

        InitializeComponent();
    }

    void Window_SourceInitialized(object sender, EventArgs e)
    {
        IntPtr handle = new WindowInteropHelper(this).Handle;
        HwndSource.FromHwnd(handle)?.AddHook(WindowProc);
    }

    private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case 0x0024:
                Native.WmGetMinMaxInfo(hwnd, lParam, (int)MinWidth, (int)MinHeight);
                handled = true;
                break;
        }

        return (IntPtr)0;
    }

    private void FullScreenButton_Click(object sender, RoutedEventArgs e)
    {
        WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
    }

    private void buttonMax_Click(object sender, RoutedEventArgs e)
    {
        WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
    }

    private void buttonMin_Click(object sender, RoutedEventArgs e)
    {
        WindowState = WindowState == WindowState.Minimized ? WindowState.Normal : WindowState.Minimized;
    }

    private void buttonExit_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }
}

这篇关于WPF 窗口位于处于最大化状态的顶部/左侧任务栏下方的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆