WPF窗口隐藏水平和垂直大小调整鼠标指针图标

时间:2018-09-24 13:09:43

标签: wpf winapi aspect-ratio window-resize

调整WPF窗口的尺寸时,我需要保持其宽高比。 我发现解决长宽比的唯一方法是使用以下WINAPI代码,并将Window注册到Window_SourceInitialized事件处理程序中的WindowAspectRatio类。

Window_SourceInitialized事件处理程序。

private void Window_SourceInitialized(object sender, EventArgs e)
    {
        ratio = WindowAspectRatio.Register((Window)sender);
        double scaleOfScreen = 0.5;

        double w = (double)WpfScreen.GetScreenFrom(this).WorkingArea.Width;
        double h = (double)WpfScreen.GetScreenFrom(this).WorkingArea.Height;
        if (w < h)
        {
            Application.Current.MainWindow.Width = w * scaleOfScreen;
            Application.Current.MainWindow.Height = w * scaleOfScreen / ratio;
        }
        else
        {
            Application.Current.MainWindow.Height = h * scaleOfScreen;
            Application.Current.MainWindow.Width = h * ratio * scaleOfScreen;
        }

        this.InvalidateVisual();
    }

WindowAspectRatio类

 internal class WindowAspectRatio
{
    private double ratio_;
    private Window windowToTrack_;

    private WindowAspectRatio(Window window)
    {
        ratio_ = window.Width / window.Height;
        windowToTrack_ = window;
        ((HwndSource)HwndSource.FromVisual(window)).AddHook(DragHook);
    }

    public static double Register(Window window)
    {
        return new WindowAspectRatio(window).ratio_;
    }

    internal enum WM
    {
        WINDOWPOSCHANGING = 0x0046,
    }

    [Flags()]
    public enum SWP
    {
        NoMove = 0x2,
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct WINDOWPOS
    {
        public IntPtr hwnd;
        public IntPtr hwndInsertAfter;
        public int x;
        public int y;
        public int cx;
        public int cy;
        public int flags;
    }

    private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
    {
        if ((WM)msg == WM.WINDOWPOSCHANGING)
        {
            WINDOWPOS position = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));

            if ((position.flags & (int)SWP.NoMove) != 0 ||
                HwndSource.FromHwnd(hwnd).RootVisual == null) return IntPtr.Zero;


            double screenW = (double)WpfScreen.GetScreenFrom(windowToTrack_).WorkingArea.Width;
            double screenH = (double)WpfScreen.GetScreenFrom(windowToTrack_).WorkingArea.Height;

            if (position.cy >= screenH)
                position.cy = (int)screenH;

            position.cx = (int)(position.cy * ratio_);

            if (position.cx >= screenW)
            {
                position.cx = (int)screenW;
                position.cy = (int)(position.cx / ratio_);
            }

            Marshal.StructureToPtr(position, lParam, true);
            handeled = true;
        }

        return IntPtr.Zero;
    }

这很好。通过拖动“窗口”角调整窗口大小时,它可以完美工作并保持宽高比。

如果我将鼠标指针移到窗口的底部边缘,在那儿可以调整窗口的高度,那么拖动该边缘也可以正常工作。调整窗口大小,并保持宽高比。

问题是当将鼠标指针移到左侧窗口边缘时,我得到了调整鼠标指针图标的大小,但是当尝试调整窗口大小时,该窗口现在只是水平移动了。

当将鼠标指针移到右侧窗口边缘时,我也会获得调整鼠标指针图标的大小,但是当尝试调整大小时,什么也没有发生。

对我来说,如果尝试在水平方向(按左边缘或右边缘)或仅垂直方向调整大小时,只能阻止调整大小的鼠标指针图标出现,那么只调整角落的大小就可以了。 (按底部边缘),只需抓住窗口的一角即可调整大小。因此,问题是,是否有可能以某种方式防止水平和垂直调整鼠标指针图标的大小,但仍然可以通过抓住拐角同时允许(垂直和水平)调整大小?

或者,如果有人对如何使用上述代码解决水平调整大小的问题没有按预期工作的想法有所了解,那么

2 个答案:

答案 0 :(得分:0)

在此期间,我设法通过将WINAPI代码更改为以下内容来防止仅调整所有水平/垂直大小。如果可以防止鼠标指针图标在窗口的底部,左侧和右侧显示调整大小的图标,那么我将有一个可接受的解决方案。

    private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
    {
        if ((WM)msg == WM.WINDOWPOSCHANGING)
        {
            WINDOWPOS position = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));

            if ((position.flags & (int)SWP.NoMove) != 0 ||
                HwndSource.FromHwnd(hwnd).RootVisual == null) return IntPtr.Zero;


            double screenW = (double)WpfScreen.GetScreenFrom(windowToTrack_).WorkingArea.Width;
            double screenH = (double)WpfScreen.GetScreenFrom(windowToTrack_).WorkingArea.Height;


            if (Math.Abs(position.cx - windowToTrack_.Width) < Math.Abs(position.cy - windowToTrack_.Height))
            {
                if (position.cx >= screenW)
                    position.cx = (int)screenW;
                position.cy = (int)(position.cx / ratio_);
                if (position.cy >= screenH)
                    position.cy = (int)screenH;
                position.cx = (int)(position.cy * ratio_);
            }
            else
            {
                if (
                    (position.cx < windowToTrack_.Width &&
                    position.cy == windowToTrack_.Height )
                    ||
                    (position.cx > windowToTrack_.Width &&
                    position.cy == windowToTrack_.Height )
                    )
                {
                    handeled = true;
                    position.x = (int)windowToTrack_.Left;
                    position.y = (int)windowToTrack_.Top;
                    position.cx = (int)windowToTrack_.Width;
                    position.cy = (int)windowToTrack_.Height;
                    Marshal.StructureToPtr(position, lParam, true);
                    return IntPtr.Zero;
                }

                if (position.cy >= screenH)
                    position.cy = (int)screenH;
                position.cx = (int)(position.cy * ratio_);
                if (position.cx >= screenW)
                    position.cx = (int)screenW;
                position.cy = (int)(position.cx / ratio_);
            }

            Marshal.StructureToPtr(position, lParam, true);
            handeled = true;
        }

        return IntPtr.Zero;
    }
}

答案 1 :(得分:0)

好的,我找到了一个简单的解决方案。有人也基于WINAPI方法解决了相同的问题,该方法允许所有边框调整尺寸并保持宽高比。 下面的解决方案必不可少的部分。

public partial class MainWindow : Window
    {
        private double _aspectRatio;
        private bool? _adjustingHeight = null;

        public MainWindow()
        {
            InitializeComponent();
            this.SourceInitialized += Window_SourceInitialized;
        }

        private void Window_SourceInitialized(object sender, EventArgs ea)
        {
            HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
            hwndSource.AddHook(DragHook);

            _aspectRatio = this.Width / this.Height;
        }



        internal enum SWP
        {
            NOMOVE = 0x0002
        }
        internal enum WM
        {
            WINDOWPOSCHANGING = 0x0046,
            EXITSIZEMOVE = 0x0232,
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct WINDOWPOS
        {
            public IntPtr hwnd;
            public IntPtr hwndInsertAfter;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public int flags;
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetCursorPos(ref Win32Point pt);

        [StructLayout(LayoutKind.Sequential)]
        internal struct Win32Point
        {
            public Int32 X;
            public Int32 Y;
        };

        public static Point GetMousePosition()
        {
            Win32Point w32Mouse = new Win32Point();
            GetCursorPos(ref w32Mouse);
            return new Point(w32Mouse.X, w32Mouse.Y);
        }

        private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch ((WM)msg)
            {
                case WM.WINDOWPOSCHANGING:
                    {
                        WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));

                        if ((pos.flags & (int)SWP.NOMOVE) != 0)
                            return IntPtr.Zero;

                        Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
                        if (wnd == null)
                            return IntPtr.Zero;

                        // determine what dimension is changed by detecting the mouse position relative to the 
                        // window bounds. if gripped in the corner, either will work.
                        if (!_adjustingHeight.HasValue)
                        {
                            Point p = GetMousePosition();

                            double diffWidth = Math.Min(Math.Abs(p.X - pos.x), Math.Abs(p.X - pos.x - pos.cx));
                            double diffHeight = Math.Min(Math.Abs(p.Y - pos.y), Math.Abs(p.Y - pos.y - pos.cy));

                            _adjustingHeight = diffHeight > diffWidth;
                        }

                        if (_adjustingHeight.Value)
                            pos.cy = (int)(pos.cx / _aspectRatio); // adjusting height to width change
                        else
                            pos.cx = (int)(pos.cy * _aspectRatio); // adjusting width to heigth change

                        Marshal.StructureToPtr(pos, lParam, true);
                        handled = true;
                    }
                    break;
                case WM.EXITSIZEMOVE:
                    _adjustingHeight = null; // reset adjustment dimension and detect again next time window is resized
                    break;
            }

            return IntPtr.Zero;
        }

    }