Как установить запуск окна WPF ClientSize?
Я хочу установить начальный размер моего окна в WPF. Я не вижу прямого способа сделать это.
В частности, когда мое окно открывается, я хочу, чтобы размер был достаточно большим, чтобы его содержимое соответствовало без необходимости прокрутки. Но после того, как это показано, я хочу, чтобы окно свободно изменялось (либо больше, либо меньше).
Если я устанавливаю атрибуты Width и Height в моем элементе Window, это устанавливает неклиентский (внешний) размер, что не полезно. Как только заголовок и изменение размера границ съедят в этом пространстве, клиентская область больше не будет достаточно большой для ее содержимого, и у меня будут полосы прокрутки. Я мог бы компенсировать, выбирая больший размер, но высота заголовка и ширина границы настраиваются пользователем (а также значения по умолчанию, меняющиеся по версии ОС) и не обязательно будут одинаковыми на другой машине.
Я могу установить ширину и высоту элемента содержимого окна (тогда <Grid>
), а затем установить атрибут Window SizeToContent в WidthAndHeight. Это получает начальный размер окна именно там, где я его хочу. Но тогда вещи больше не изменяются - я могу изменить размер окна, но его содержимое не изменяется с ним, потому что я указал фиксированный размер.
Можно ли установить размер начального клиента Window, желательно без кода? (Я возьму код, если это единственный способ, но я бы предпочел подход XAML, если у кого-то есть.)
Ответы
Ответ 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;
}
}