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

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

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

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

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

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

Ответы на вопрос(5)

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

<Grid>
  <Grid.    <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.
  <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;
  }
}

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

Когда мы конструируем Окно в конструкторе 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>

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

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

Первоначальное решение, как и в ответе Орен, использоватьSizeTocontent свойство. По сути, он говорит WPF определять размер окна из его содержимого (он же «размер клиента»), а не самого окна (он же «размер клиента + все эти не-клиентские / chrome / border абсолютно неуправляемые вещи»).

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

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

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

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

Но, если мы хотим, чтобы поведение в вопросе («Я могу изменить размер окна, но его содержимое не изменяется с ним, потому что я указал фиксированный размер.»), Что также было оригинальным поведением, вместо указания Ширина и высота, мы можем использовать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>

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

Это должен быть макет WPF по умолчанию для дизайнера IMHO.

ToContent = "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.pn

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

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

Решение Вопроса

м из двух способов:

ПРИМЕЧАНИЕ: содержимое сетки 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 и обработчик события Loaded в окне, а также ширину и высоту, указанные в 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) настройка учета ширины и высоты окна для размеров окна клиентской системы для конкретной системы:

using 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 или обработчик события Loaded в Window или Width и Height, указанные в 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.

 Joe White18 окт. 2009 г., 00:00
Я наконец вернулся к проекту, где мне это нужно, и ваше первое решение делает именно то, что я хочу. Благодарность
 Nicke Manarin17 окт. 2017 г., 17:48
Опция B, видимо, не работает с Windows 7 (с базовой аэро темой).
 samuelesque20 авг. 2013 г., 21:46
Опция B здесь отлично подходит для меня. Именно то, что я искал.

CanResizeWithGrip" в xaml, но это зависит от того, сколько места занимает ваш контент при запуске

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

Ваш ответ на вопрос