Как я могу только разрешить равномерное изменение размера в окне WPF? - PullRequest
17 голосов
/ 22 декабря 2008

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

Ответы [ 8 ]

13 голосов
/ 22 декабря 2008

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

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

Вот мой скрин-шот: enter image description here

<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>
11 голосов
/ 30 марта 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;
}
8 голосов
/ 30 марта 2009

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

Вам нужно будет добавить это в тег элемента управления / окна:

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, и он мне не понравился. Я хотел зафиксировать границу окна до определенного размера. Это было проверено на оконном элементе управления, но я предполагаю, что оно будет работать и на границе.

1 голос
/ 16 сентября 2010

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

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
1 голос
/ 23 декабря 2008

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

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

<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) не соответствует его размеру на экране!

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

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

1 голос
/ 22 декабря 2008

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

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

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

0 голосов
/ 04 июля 2013

Возможно, слишком поздно, но я нашел решение в блоге Майка О'Брайена, и оно действительно хорошо работает. http://www.mikeobrien.net/blog/maintaining-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;
    }
}
0 голосов
/ 21 декабря 2010

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

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);

Вы можете заметить проверку для aspectRatio> 0, ... Я сделал это, потому что обнаружил, что он склонен вызывать мои обработчики, которые делали изменение размера до того, как метод "Load" даже назначил aspectRatio.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...