WPF Window - Only allow horizontal resize

2020-07-01 12:23发布

I want to only allow my WPF window to be resized horizontally. How best can I achieve this?

8条回答
叛逆
2楼-- · 2020-07-01 12:33

You could try databinding to the size of the window, and then setting the size back to the old value whenever the vertical dimension is changed.

查看更多
Rolldiameter
3楼-- · 2020-07-01 12:33

If you want to use the MinHeight and MaxHeight approach but still allow the window to automatically size itself to fit the size of its content:

To allow automatic content sizing don't use the Loaded event. Use the ContentRendered event instead.

查看更多
女痞
4楼-- · 2020-07-01 12:35

It's kind of a pain. Basically you need to set up a hook function to process windows messages. Then you would catch the WM_SIZING (0x0214) message and modify the parameters so that the horizontal dimension could not be changed.

Pete Brown also has some great info on this topic on his blog.

查看更多
姐就是有狂的资本
5楼-- · 2020-07-01 12:42

The following solution allows you to leave SizeToContent intact,
doesn't require Win32 Interop or code behind in your Window:

It does rely on Reactive Extensions

In the Package Manager run:

Install-Package System.Reactive

Then you start by creating a new Behavior:

public class VerticalResizeWindowBehavior : Behavior<UIElement>
{
    public static readonly DependencyProperty MinHeightProperty = DependencyProperty.Register("MinHeight", typeof(double), typeof(VerticalResizeWindowBehavior), new PropertyMetadata(600.0));
    public double MinHeight
    {
        get { return (double)GetValue(MinHeightProperty); }
        set { SetValue(MinHeightProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        var window = Window.GetWindow(AssociatedObject);
        var mouseDown = Observable.FromEventPattern<MouseButtonEventArgs>(AssociatedObject, "MouseLeftButtonDown")
                                    .Select(e => e.EventArgs.GetPosition(AssociatedObject));

        var mouseUp = Observable.FromEventPattern<MouseButtonEventArgs>(AssociatedObject, "MouseLeftButtonUp")
                                .Select(e => e.EventArgs.GetPosition(AssociatedObject));

        var mouseMove = Observable.FromEventPattern<MouseEventArgs>(AssociatedObject, "MouseMove")
                                    .Select(e => e.EventArgs.GetPosition(AssociatedObject));

        var q = from start in mouseDown
                from position in mouseMove.TakeUntil(mouseUp)
                select new { X = position.X - start.X, Y = position.Y - start.Y };

        mouseDown.Subscribe(v => AssociatedObject.CaptureMouse());
        mouseUp.Subscribe(v => AssociatedObject.ReleaseMouseCapture());

        q.ObserveOnDispatcher().Subscribe(v =>
        {
            var newHeight = window.Height + v.Y;
            window.Height = newHeight < MinHeight ? MinHeight : newHeight;                
        });
    }
}

Then you add a UIElement at the bottom of your Window and apply the Behavior:

<Border Background="Gray" 
        Height="10" 
        Cursor="SizeNS"
        Grid.ColumnSpan="2">
    <i:Interaction.Behaviors>
        <b:VerticalResizeWindowBehavior MinHeight="600"/>
    </i:Interaction.Behaviors>
</Border>

Set the following Properties on your Window:

ResizeMode="NoResize"
SizeToContent="Width"

Note: In this example, the user is allowed to resize Vertically but not Horizontally
You can easily change the code to allow the opposite, or add properties
to make it configurable.

查看更多
等我变得足够好
6楼-- · 2020-07-01 12:43

If you set the MinHeight and MaxHeight attributes of the window to the desired height the window will have a fixed height

查看更多
地球回转人心会变
7楼-- · 2020-07-01 12:50

Building on the answers from User3810621 and others, this can be turned into a reusable behavior which locks in the calculated sizes as min and/or max after the first content render:

namespace whatever.util.wpf.behaviors
{
    using System.Windows;
    using System.Windows.Interactivity;

    public class LockInitialSize : Behavior<Window>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.ContentRendered += OnContentRendered;
        }

        protected override void OnDetaching()
        {
            // possibly detached before ever rendering
            AssociatedObject.ContentRendered -= OnContentRendered;
            base.OnDetaching();
        }

        private void OnContentRendered(object s, EventArgs e)
        {
            // Just once
            AssociatedObject.ContentRendered -= OnContentRendered;

            if (MinWidth)
            {
                AssociatedObject.MinWidth = AssociatedObject.ActualWidth;
            }
            /// ... MaxWidth, MinHeight, MaxHeight
        }

        #region MinWidth Property

        public bool MinWidth
        {
            get => (bool)GetValue(MinWidthProperty);
            set => SetValue(MinWidthProperty, value);
        }

        public static readonly DependencyProperty MinWidthProperty =
            DependencyProperty.Register(nameof(MinWidth), typeof(bool), typeof(LockInitialSize));

        #endregion

        // ... MaxWidth, MinHeight, MaxHeight    
    }
}

The remaining, repetitive values are trivial and omitted for brevity.

Usage:

<Window ...
     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
     xmlns:behaviors="clr-namespace:whatever.util.wpf.behaviors;assembly=whatever"
     SizeToContent="Height"
     ...>
    <i:Interaction.Behaviors>
        <behaviors:LockInitialSize MinHeight="True" MaxHeight="True" />
    </i:Interaction.Behaviors>
查看更多
登录 后发表回答