Подтвердить что ты не робот

Как установить запуск окна WPF ClientSize?

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

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

Если я устанавливаю атрибуты Width и Height в моем элементе Window, это устанавливает неклиентский (внешний) размер, что не полезно. Как только заголовок и изменение размера границ съедят в этом пространстве, клиентская область больше не будет достаточно большой для ее содержимого, и у меня будут полосы прокрутки. Я мог бы компенсировать, выбирая больший размер, но высота заголовка и ширина границы настраиваются пользователем (а также значения по умолчанию, меняющиеся по версии ОС) и не обязательно будут одинаковыми на другой машине.

Я могу установить ширину и высоту элемента содержимого окна (тогда <Grid>), а затем установить атрибут Window SizeToContent в WidthAndHeight. Это получает начальный размер окна именно там, где я его хочу. Но тогда вещи больше не изменяются - я могу изменить размер окна, но его содержимое не изменяется с ним, потому что я указал фиксированный размер.

Можно ли установить размер начального клиента Window, желательно без кода? (Я возьму код, если это единственный способ, но я бы предпочел подход XAML, если у кого-то есть.)

4b9b3361

Ответ 1

Вы можете сделать это с помощью кода в обработчике нагрузки Load одним из двух способов:

ПРИМЕЧАНИЕ. Содержимое таблицы LayoutRoot одинаково в обоих примерах, но ширина и высота в LayoutRoot указаны только в примере A.

A) ClearValue в окне SizeToContent и в содержании Ширина и высота:

using System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ClearValue(SizeToContentProperty);
            LayoutRoot.ClearValue(WidthProperty);
            LayoutRoot.ClearValue(HeightProperty);
        }
    }
}

в предположении макета страницы (обратите внимание на параметр SizeToContent и обработчик обработанного события в окне, а также ширину и высоту, указанные в LayoutRoot):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" SizeToContent="WidthAndHeight" Loaded="Window_Loaded">
    <Grid x:Name="LayoutRoot" Width="300" Height="300" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

или

B), устанавливая размер ширины и высоты окна для размеров кадра окна клиента для системы:

с использованием System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            const int snugContentWidth = 300;
            const int snugContentHeight = 300;

            var horizontalBorderHeight = SystemParameters.ResizeFrameHorizontalBorderHeight;
            var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;
            var captionHeight = SystemParameters.CaptionHeight;

            Width = snugContentWidth + 2 * verticalBorderWidth;
            Height = snugContentHeight + captionHeight + 2 * horizontalBorderHeight;
        }
    }
}

предполагая пропорциональный макет страницы (обратите внимание на параметр SizeToContent или загруженный обработчик событий в окне, ширине и высоте, указанном в LayoutRoot):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">
    <Grid x:Name="LayoutRoot" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

Я еще не смог найти способ сделать это декларативно в XAML.

Ответ 2

Вы можете удалить атрибуты Ширина и Высота окна в XAML и добавить SizeToContent = "WidthAndHeight". Это устанавливает начальные размеры окна в его содержимое, но все же позволяет изменять его размер.

<Window x:Class="WpfApplication2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" SizeToContent="WidthAndHeight">
    <Grid>
        <TextBlock Text="How to set WPF window’s startup ClientSize?"/>
    </Grid>
</Window>

При запуске он выглядит следующим образом:

alt text http://img7.imageshack.us/img7/1285/onstart.png

Но вы все равно можете растянуть его мышью:

alt text http://img16.imageshack.us/img16/6687/stretched.png

Ответ 3

Я провожу достаточно времени, чтобы понять эту историю. На удивление трудно найти чистый XAML (нулевой код позади) ответ на этот вопрос в сети, так что вот мой.

Когда мы создаем Window в дизайнере WPF Visual Studio, стандартный (и по умолчанию) способ заключается в определении свойств Width и Height на Window, как это происходит в XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="75" Width="190">
    <Grid>
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

Предварительный просмотр конструктора выглядит следующим образом:

enter image description here

Все выглядит здорово, но когда мы запускаем приложение, в зависимости от текущей версии Windows, темы и всех настроек джаза настроек, есть 99% шансов, что окно выполнения не будет выглядеть как запроектированное. Вот что он смотрит на мою Windows 8.1:

enter image description here

Исходное решение, как и в ответе Орена, использует свойство SizeTocontent. В основном это означает, что WPF определяет размер окна из него (ака "размер клиента" ) вместо самого окна (ака "размер клиента + все, что неконтролируемый клиент/хром/граница" ).

Итак, мы можем определить, что Content имеет фиксированный размер, например:

<Window x:Class="WpfApplication1.MainWindowSizeToContentCentered"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid Height="40" Width="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

И теперь окно выполнения выглядит точно так, как мы хотим (обратите внимание, что высота и ширина сетки не имеют точно таких же значений, что и исходное окно one - 40/180 против 75/190 - и это прекрасно, как в теперь вы можете просто забыть свойство Height и Width окна навсегда):

enter image description here

Как это происходит при изменении размера окна? например, сетка центрирована, что отлично, если вы хотите этого поведения:

enter image description here

Но, если мы хотим поведения в вопросе, ( "я могу изменить размер окна, но его содержимое не изменяется с ним, потому что я указал фиксированный размер".), что также было изначальным поведением с указанием ширины и высоты, мы можем использовать MinWidth и MinHeight, например:

<Window x:Class="WpfApplication1.MainWindowSizeToContentResizable"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid MinHeight="40" MinWidth="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

Окно выполнения выглядит одинаково, но опыт изменения размера теперь сопоставим с исходным макетом окна по умолчанию:

enter image description here

Это должен быть стандартный дизайн WPF-дизайнера IMHO.

Ответ 4

Я делаю следующее в конструкторе и добавляю ResizeMode = "CanResizeWithGrip" в xaml, но это зависит от того, сколько места занимает ваш контент при запуске

public Window1()
{
    this.Height = SystemParameters.WorkArea.Height;
    this.Width = SystemParameters.WorkArea.Width;
}

Ответ 5

Саймон Моуриер дает отличный ответ, но мне нужен размер одного элемента управления, чтобы отложить его до остальных. Поэтому инвертирование поведения размера окна с атрибутом SizeToContent было не тем, что мне было нужно. Я закончил с [Tim's] [метод вычисления размера неклиента], чтобы вычесть -клиент-область из динамической ширины и высоты MainWindow; в элементе XAML <MultiBinding>. Это было достигнуто путем чтения свойств SystemParameters.WindowCaptionHeight и SystemParameters.ResizeFramVerticalBorderWidth (см. Ниже код IMultiValueConverter).

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition x:Name="_mwR0" Height="Auto"/>
    <RowDefinition x:Name="_mwR1" Height="4*"/>
    <RowDefinition x:Name="_mwR2" Height="Auto"/>
    <RowDefinition x:Name="_mwR3">
      <RowDefinition.Height>
        <MultiBinding Converter="{StaticResource SizeToRemainderConverter}" Mode="TwoWay">
          <Binding ElementName="_LogWindow" Path="Height" Mode="TwoWay"/>
          <Binding ElementName="_MainWindow" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR0" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR1" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR2" Path="Height" Mode="OneWay"/>
        </MultiBinding>
      </RowDefinition.Height>
    </RowDefinition>
  </Grid.RowDefinitions>
  <Menu IsMainMenu="True" Grid.Row="0">...</Menu>
  <ListView Grid.Row="1">...</ListView>
  <GridSplitter Grid.Row="2" ShowsPreview="True" HorizontalAlignment="Stretch" Height="6" VerticalAlignment="Center" Margin="0"/>
  <RichTextBox x:Name="_LogWindow" Grid.Row="3"/>
</Grid>

_LogWindow является внутренним элементом управления последней строки сетки. По мере изменения размера MainWindow мне нужно уменьшить этот контроль в знак уважения к другим.

Конвертор усложняется необходимостью обрабатывать типы объектов System.Double и System.Windows.GridLength. Я также сохраняю компоновку между сеансами приложений, поэтому мне нужно, чтобы конвертер был двунаправленным (извинения за плотный код).

public class SizeToRemainderConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parm, System.Globalization.CultureInfo culture)
  {
    double ret = 0.0;
    if (values != null && values.Length > 0)
    {
      if (values[0].GetType().Name.Equals("String")) double.TryParse((values[0] as string), out ret);
      else if (values[0].GetType().Name.Equals("GridLength")) ret = ((GridLength)values[0]).Value;
      else ret = (double)System.Convert.ChangeType(values[0], typeof(double));
    }

    double available = 0.0;
    if (values != null && values.Length > 1)
    {
      if (values[1].GetType().Name.Equals("String")) double.TryParse((values[1] as string), out available);
      else if (values[1].GetType().Name.Equals("GridLength")) available = ((GridLength)values[1]).Value;
      else available = (double)System.Convert.ChangeType(values[1], typeof(double));

      available -= SystemParameters.WindowCaptionHeight;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
    }

    for (int i = 2; i < (values?.Length ?? 0); ++i)
    {
      double delta = 0.0;

      if (values[i].GetType().Name.Equals("String")) double.TryParse((values[i] as string), out delta);
      else if (values[i].GetType().Name.Equals("GridLength")) delta = ((GridLength)values[i]).Value;
      else delta = (double)System.Convert.ChangeType(values[i], typeof(double));
      available -= delta;
    }

    if (available < ret) ret = 0.0;

    if (targetType.Name.Equals("GridLength")) return new GridLength(ret);
    return System.Convert.ChangeType(ret, targetType);
  }

  public object[] ConvertBack(object v, Type[] t, object p, System.Globalization.CultureInfo c)
  {
    object[] ret = new object[t.Length];
    switch (v.GetType().Name)
    {
      case "Double":
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength((v as double?) ?? 0.0);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;

      case "GridLength":
        GridLength gl = (v as GridLength?) ?? new GridLength();
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = gl.Value.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = gl;
          else ret[i] = System.Convert.ChangeType(gl.Value, t[i]);
        }
        break;

      case "String":
      default:
        double d = 0.0;
        double.TryParse(v as string, out d);
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength(d);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;
    }

    return ret;
  }
}