如何以增量调整WPF窗口的大小?

时间:2021-07-06 19:00:11

Is it possible to make a Window such that when it's resized in height by the user, it increments and decrements by 10? Sort of like snapping resize.

是否可以制作一个窗口,以便当用户调整高度时,它会递增和递减10?有点像对齐调整大小。

6 个解决方案

#1


4  

Here is an example of how this can be done:

以下是如何完成此操作的示例:

using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace DeleteMeWPF {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow {
        public MainWindow() {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e) {
            base.OnSourceInitialized(e);

            IntPtr handle = new WindowInteropHelper(this).Handle;
            HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(this.WindowProc));
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        private const int WM_SIZING = 0x0214;

        private const int WMSZ_BOTTOM = 6;
        private const int WMSZ_BOTTOMLEFT = 7;
        private const int WMSZ_BOTTOMRIGHT = 8;
        private const int WMSZ_LEFT = 1;
        private const int WMSZ_RIGHT = 2;
        private const int WMSZ_TOP = 3;
        private const int WMSZ_TOPLEFT = 4;
        private const int WMSZ_TOPRIGHT = 5;

        private const int SnappingIncrement = 100;
        private const int SnappingThresholdWidth = 300;
        private const int SnappingThresholdHeight = 400;

        private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            switch (msg) {
                case WM_SIZING:
                    RECT bounds = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));

                    int width = bounds.right - bounds.left;
                    int height = bounds.bottom - bounds.top;

                    switch (wParam.ToInt32()) {
                        case WMSZ_BOTTOM:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_BOTTOMLEFT:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_BOTTOMRIGHT:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_LEFT:
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_RIGHT:
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOP:
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOPLEFT:
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOPRIGHT:
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;

                    }
                    Marshal.StructureToPtr(bounds, lParam, false);
                    break;
            }

            return IntPtr.Zero;
        }
    }
}

This uses increments of 100 to really illustrate the "snapping" effect. In addition, you can adjust the snapping thresholds, which ensure the snapping only takes effect when the size is above a given width/height.

这使用100的增量来真正说明“捕捉”效果。此外,您可以调整捕捉阈值,以确保捕捉仅在大小高于给定宽度/高度时生效。

#2


3  

The old Win32 Listbox had a setting to prevent that. Note that that was solving the problem from the other end.

旧的Win32 Listbox有一个设置来防止这种情况。请注意,这是从另一端解决问题。

But take a look at a few established TreeViews first. Like Visual Studio, Tools Options.

但首先看几个已建立的TreeViews。与Visual Studio,工具选项一样。

I don't think you should be modifying the behaviour of std UI controls this way. Users will only be frustrated if your TV acts 'differently'. They won't call it 'better'.

我不认为你应该这样修改std UI控件的行为。如果您的电视节目“不同”,用户只会感到沮丧。他们不会称之为“更好”。

#3


3  

It may not be very nice to have the window become a size the user didn't want... what would happen if they maximize the window, and it's not a multiple of 10? Would you be able to keep the other window borders in the same place if they dragged the top edge of the window?

让窗口变成用户不想要的尺寸可能不是很好......如果它们最大化窗口会发生什么,它不是10的倍数?如果它们拖动窗口的顶部边缘,您是否能够将其他窗口边框保持在同一位置?

I'd instead focus on the TreeView: Make a container that implements MeasureOverride, and have the implementation call the base.MeasureOverride, and when it gets that result, rounds the values down (always down) to a multiple of 10. The rest of your window contents will get a varying amount of space, depending on the size of the window, but just make sure you've got something in your layout that will stretch to take up the extra 0-9 pixels.

我将重点放在TreeView上:创建一个实现MeasureOverride的容器,并让实现调用base.MeasureOverride,当它获得该结果时,将值向下舍入(总是向下)到10的倍数。其余的您的窗口内容将获得不同的空间,具体取决于窗口的大小,但只需确保您的布局中有一些东西将拉伸以占用额外的0-9像素。

#4


1  

Well, normally you would want to handle the Resize/Resizing event and then round up or down the size to a multiple of ten or something of the sort.

好吧,通常你会想要处理调整大小/调整大小事件,然后将大小向上或向下舍入到十的倍数或类似的东西。

However in WPF you apparently have to resort to a hack using behaviors.

但是在WPF中你显然不得不诉诸于使用行为的黑客攻击。

See this article as it describes what you have to do to access the resizing event.

请参阅此文章,因为它描述了访问调整大小事件所需执行的操作。

#5


1  

You could possibly add a SizeChangedEventHandler to the SizeChanged event. Then inside your delegate method, change the size manually.

您可以将SizeChangedEventHandler添加到SizeChanged事件中。然后在您的委托方法中,手动更改大小。

Though I think it would be hard to know how much it has changed unless you record it somewhere.

虽然我认为除非你把它记录在某个地方,否则很难知道它有多大变化。

#6


0  

Well, I was about to say : "In a esay way, and by not writing dirty-twisty code-behind ? Not to my knowledge".

好吧,我正要说:“以某种方式,并且不写脏兮兮的代码落后?不是我的知识”。

... I'll stick to that xD

......我会坚持那个xD

#1


4  

Here is an example of how this can be done:

以下是如何完成此操作的示例:

using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace DeleteMeWPF {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow {
        public MainWindow() {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e) {
            base.OnSourceInitialized(e);

            IntPtr handle = new WindowInteropHelper(this).Handle;
            HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(this.WindowProc));
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        private const int WM_SIZING = 0x0214;

        private const int WMSZ_BOTTOM = 6;
        private const int WMSZ_BOTTOMLEFT = 7;
        private const int WMSZ_BOTTOMRIGHT = 8;
        private const int WMSZ_LEFT = 1;
        private const int WMSZ_RIGHT = 2;
        private const int WMSZ_TOP = 3;
        private const int WMSZ_TOPLEFT = 4;
        private const int WMSZ_TOPRIGHT = 5;

        private const int SnappingIncrement = 100;
        private const int SnappingThresholdWidth = 300;
        private const int SnappingThresholdHeight = 400;

        private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            switch (msg) {
                case WM_SIZING:
                    RECT bounds = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));

                    int width = bounds.right - bounds.left;
                    int height = bounds.bottom - bounds.top;

                    switch (wParam.ToInt32()) {
                        case WMSZ_BOTTOM:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_BOTTOMLEFT:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_BOTTOMRIGHT:
                            if (height > SnappingThresholdHeight)
                                bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_LEFT:
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_RIGHT:
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOP:
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOPLEFT:
                            if (width > SnappingThresholdWidth)
                                bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;
                        case WMSZ_TOPRIGHT:
                            if (width > SnappingThresholdWidth)
                                bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
                            if (height > SnappingThresholdHeight)
                                bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
                            break;

                    }
                    Marshal.StructureToPtr(bounds, lParam, false);
                    break;
            }

            return IntPtr.Zero;
        }
    }
}

This uses increments of 100 to really illustrate the "snapping" effect. In addition, you can adjust the snapping thresholds, which ensure the snapping only takes effect when the size is above a given width/height.

这使用100的增量来真正说明“捕捉”效果。此外,您可以调整捕捉阈值,以确保捕捉仅在大小高于给定宽度/高度时生效。

#2


3  

The old Win32 Listbox had a setting to prevent that. Note that that was solving the problem from the other end.

旧的Win32 Listbox有一个设置来防止这种情况。请注意,这是从另一端解决问题。

But take a look at a few established TreeViews first. Like Visual Studio, Tools Options.

但首先看几个已建立的TreeViews。与Visual Studio,工具选项一样。

I don't think you should be modifying the behaviour of std UI controls this way. Users will only be frustrated if your TV acts 'differently'. They won't call it 'better'.

我不认为你应该这样修改std UI控件的行为。如果您的电视节目“不同”,用户只会感到沮丧。他们不会称之为“更好”。

#3


3  

It may not be very nice to have the window become a size the user didn't want... what would happen if they maximize the window, and it's not a multiple of 10? Would you be able to keep the other window borders in the same place if they dragged the top edge of the window?

让窗口变成用户不想要的尺寸可能不是很好......如果它们最大化窗口会发生什么,它不是10的倍数?如果它们拖动窗口的顶部边缘,您是否能够将其他窗口边框保持在同一位置?

I'd instead focus on the TreeView: Make a container that implements MeasureOverride, and have the implementation call the base.MeasureOverride, and when it gets that result, rounds the values down (always down) to a multiple of 10. The rest of your window contents will get a varying amount of space, depending on the size of the window, but just make sure you've got something in your layout that will stretch to take up the extra 0-9 pixels.

我将重点放在TreeView上:创建一个实现MeasureOverride的容器,并让实现调用base.MeasureOverride,当它获得该结果时,将值向下舍入(总是向下)到10的倍数。其余的您的窗口内容将获得不同的空间,具体取决于窗口的大小,但只需确保您的布局中有一些东西将拉伸以占用额外的0-9像素。

#4


1  

Well, normally you would want to handle the Resize/Resizing event and then round up or down the size to a multiple of ten or something of the sort.

好吧,通常你会想要处理调整大小/调整大小事件,然后将大小向上或向下舍入到十的倍数或类似的东西。

However in WPF you apparently have to resort to a hack using behaviors.

但是在WPF中你显然不得不诉诸于使用行为的黑客攻击。

See this article as it describes what you have to do to access the resizing event.

请参阅此文章,因为它描述了访问调整大小事件所需执行的操作。

#5


1  

You could possibly add a SizeChangedEventHandler to the SizeChanged event. Then inside your delegate method, change the size manually.

您可以将SizeChangedEventHandler添加到SizeChanged事件中。然后在您的委托方法中,手动更改大小。

Though I think it would be hard to know how much it has changed unless you record it somewhere.

虽然我认为除非你把它记录在某个地方,否则很难知道它有多大变化。

#6


0  

Well, I was about to say : "In a esay way, and by not writing dirty-twisty code-behind ? Not to my knowledge".

好吧,我正要说:“以某种方式,并且不写脏兮兮的代码落后?不是我的知识”。

... I'll stick to that xD

......我会坚持那个xD