Как я могу разрешить только равномерное изменение размера в окне WPF?

Я не хочу, чтобы размер моего окна менялся «только по горизонтали» или «только по вертикали». Есть ли свойство, которое я могу установить в своем окне, которое может обеспечить это, или есть изящный трюк с выделенным кодом, который я могу использовать?


person Mark Carpenter    schedule 22.12.2008    source источник
comment
Я ответил на аналогичный вопрос, как сохранить соотношение сторон окна WPF при изменении размера. См. Мой пост здесь, он основан на ответе Нира.   -  person Mike Fuchs    schedule 12.04.2013


Ответы (8)


Вы можете зарезервировать соотношение сторон содержимого с помощью WPF ViewBox с элементом управления с фиксированной шириной и высотой внутри.

Давай попробуем. Вы можете изменить атрибут «Растянуть» ViewBox, чтобы получить другие результаты.

Вот мой снимок экрана: введите описание изображения здесь

<Window x:Class="TestWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <Viewbox Stretch="Uniform">
        <StackPanel Background="Azure" Height="400" Width="300" Name="stackPanel1" VerticalAlignment="Top">
            <Button Name="testBtn" Width="200" Height="50">
                <TextBlock>Test</TextBlock>
            </Button>
        </StackPanel>
    </Viewbox>

</Window>
person Gant    schedule 22.12.2008
comment
Давайте попробуем - прекрасное описание WPF - будет ли он работать? Никто не знает! Давай попробуем. Я буду придерживаться Win32, где просто скажу операционной системе: вот мой размер, разберитесь с ним. - person Frank Krueger; 22.12.2008
comment
@Frank Krueger: в Win32 не все так просто, единственная разница в том, что вы уже знаете причуды и ожидаемое поведение. Как только вы узнаете их в WPF, вы сможете делать все, что захотите. - person Pop Catalin; 22.12.2008
comment
О нет, Фрэнк. Я действительно тестирую приведенный выше код, и он отлично работает. Может это недопонимание :) Простите за плохой английский. - person Gant; 22.12.2008
comment
Думаю приложу скриншоты. - person Gant; 22.12.2008
comment
Этот подход отчасти работает (и я использовал его некоторое время), но я не хочу, чтобы мои кнопки / флажки / текст действительно казались больше или меньше при изменении размера. - person Mark Carpenter; 12.09.2009
comment
Это приведет к единообразному изменению размера содержимого окна, а не самого окна ... - person Thomas Levesque; 12.09.2009

Вы всегда можете обработать сообщение WM_WINDOWPOSCHANGING, это позволяет вам контролировать размер и положение окна во время процесса изменения размера (в отличие от исправления вещей после того, как пользователь завершил изменение размера).

Вот как вы это делаете в WPF, я объединил этот код из нескольких источников, поэтому в нем могут быть некоторые синтаксические ошибки.

internal enum WM
{
   WINDOWPOSCHANGING = 0x0046,
}

[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 void Window_SourceInitialized(object sender, EventArgs ea)
{
   HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
   hwndSource.AddHook(DragHook);
}

private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
   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;
          }

          bool changedPos = false;

          // ***********************
          // Here you check the values inside the pos structure
          // if you want to override tehm just change the pos
          // structure and set changedPos to true
          // ***********************

          if (!changedPos)
          {
             return IntPtr.Zero;
          }

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

   return IntPtr.Zero;
}
person Nir    schedule 30.03.2009
comment
Это очень удобный способ сделать это. - person Ben Doerr; 30.06.2009
comment
Интересно, но мы не знаем, меняет ли пользователь ширину, высоту (или и то, и другое. Нам нужно знать это, чтобы увидеть, должны ли мы регулировать высоту или ширину. - person Serge Wautier; 24.09.2009
comment
Работает очень хорошо, дает более плавные результаты, чем настройки в SizeChanged / RenderSizeChanged. - person Mike Fuchs; 12.04.2013

Вот каким было мое решение.

Вам нужно будет добавить это в свой тег control / window:

Loaded="Window_Loaded"

И вам нужно будет поместить это в свой код позади:

private double aspectRatio = 0.0;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    aspectRatio = this.ActualWidth / this.ActualHeight;
}

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    if (sizeInfo.WidthChanged)
    {
        this.Width = sizeInfo.NewSize.Height * aspectRatio;
    }
    else
    {
        this.Height = sizeInfo.NewSize.Width * aspectRatio;
    }
}

Я попробовал трюк с Viewbox, и он мне не понравился. Я хотел зафиксировать границу окна определенного размера. Это было протестировано на элементе управления окном, но я предполагаю, что это будет работать и на границе.

person Ben Doerr    schedule 30.03.2009
comment
Этот подход работает, но имеет недостаток в том, что граница окна мигает (из-за отсутствия лучшего слова) во время операции изменения размера - размер сначала устанавливается на то, что пользователь свободно изменяет его размер, затем граница настраивается на переопределенный размер. - person Tomi Junnila; 10.12.2011

Вы можете попробовать воспроизвести эффект, который я часто вижу на сайтах Flash Video. Они позволяют вам расширять окно браузера как угодно, но только растягивают область презентации так, чтобы она соответствовала наименьшей высоте или ширине.

Например, если вы растянете окно по вертикали, ваше приложение не изменит размер. Было бы просто добавить черные полосы вверху и внизу области отображения и оставить вертикально по центру.

Это может быть или невозможно с WPF; Я не знаю.

person EndangeredMassa    schedule 22.12.2008

Это может быть немного поздно, но вы можете просто вставить это в свой код позади ...

Private Sub UserControl1_SizeChanged(ByVal sender As Object, ByVal e As System.Windows.SizeChangedEventArgs) Handles Me.SizeChanged
    If e.HeightChanged Then
        Me.Width = Me.Height
    Else
        Me.Height = Me.Width
    End If
End Sub
person Brian    schedule 15.09.2010

Я ожидал, что вы можете двусторонне привязать ширину к высоте с помощью преобразователя значений для сохранения соотношения сторон. Передача соотношения сторон в качестве параметра преобразователя сделает его более универсальным.

Итак, я попробовал это - сначала привязка без конвертера:

<Window 
    ...
    Title="Window1" Name="Win" Height="500" 
    Width="{Binding RelativeSource={RelativeSource self}, 
                    Path=Height, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <StackPanel>
        <TextBlock>Width:</TextBlock>
        <TextBlock Text="{Binding ElementName=Win, Path=Width}" />
        <TextBlock>Height:</TextBlock>
        <TextBlock Text="{Binding ElementName=Win, Path=Height}" />
    </StackPanel>    
</Window>

Как ни странно, привязка ведет себя так, как будто она односторонняя, а заявленная ширина окна (как показано в TextBlock) не соответствует его размеру на экране!

Возможно, эту идею стоит продолжить, но сначала нужно разобраться с этим странным поведением.

Надеюсь, это поможет!

person Daniel Paull    schedule 23.12.2008

В примере кода:

if (sizeInfo.WidthChanged)     
{         
    this.Width = sizeInfo.NewSize.Height * aspectRatio;    
}     
else     
{         
    this.Height = sizeInfo.NewSize.Width * aspectRatio; 
} 

Я считаю, что второе вычисление должно быть:

this.Height = sizeInfo.NewSize.Width * (1/aspectRatio);  

Я сделал вариацию этой работы в обработчике события SizeChanged. Поскольку я хотел, чтобы ширина была контрольным размером, я просто заставил высоту соответствовать ей с вычислением формы:

if (aspectRatio > 0)
// enforce aspect ratio by restricting height to stay in sync with width.  
this.Height = this.ActualWidth * (1 / aspectRatio);

Вы можете отметить проверку на аспект

person Code-an the Barbarian    schedule 21.12.2010

Может быть, уже слишком поздно, но я нашел решение в блоге Майка О'Брайена, и оно действительно хорошо работает. http://www.mikeobrien.net/blog/mainpting-aspect-ratio-when-resizing/ Ниже приведен код из его блога:

<Window ... SourceInitialized="Window_SourceInitialized" ... >
    ...
Window>

public partial class Main : Window
{
    private void Window_SourceInitialized(object sender, EventArgs ea)
    {
        WindowAspectRatio.Register((Window)sender);
    }
    ...
}


internal class WindowAspectRatio
{
    private double _ratio;

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

    public static void Register(Window window)
    {
        new WindowAspectRatio(window);
    }

    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;

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

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

        return IntPtr.Zero;
    }
}
person Nguyen Kien    schedule 04.07.2013