tutorial - wpf vs winforms
¿Cómo configurar el inicio del tamaño de la ventana de WPF? (5)
Puede eliminar los atributos de ancho y alto de la ventana en XAML y agregar SizeToContent = "WidthAndHeight". Esto establece las dimensiones iniciales de la ventana a su contenido, y aún así le permite cambiar su tamaño.
<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>
Cuando se inicia, se ve así:
texto alt http://img7.imageshack.us/img7/1285/onstart.png
Aún así puedes estirarlo con el ratón:
texto alt http://img16.imageshack.us/img16/6687/stretched.png
Quiero configurar el tamaño del cliente inicial de mi ventana WPF. No estoy viendo una manera directa de hacer esto.
Específicamente, cuando se abra mi ventana, quiero que tenga un tamaño lo suficientemente grande para que su contenido se ajuste sin necesidad de barras de desplazamiento. Pero una vez que se muestra, quiero que la ventana se pueda cambiar de tamaño libremente (ya sea más grande o más pequeña).
Si configuro los atributos Ancho y Alto en mi elemento de Ventana, eso establece el tamaño no externo (externo), lo que no es útil. Una vez que la barra de título y los bordes de tamaño ingresen a ese espacio, el área del cliente ya no será lo suficientemente grande para su contenido, y tendré barras de desplazamiento. Podría compensarlo seleccionando un tamaño más grande, pero tanto la altura de la barra de título como el grosor del borde son personalizables por el usuario (así como los valores predeterminados que varían según la versión del SO) y no necesariamente serán iguales en una máquina diferente.
Puedo establecer el Ancho y la Altura en el elemento de contenido de la ventana (un <Grid>
en este caso), y luego configurar el atributo SizeToContent de la Ventana en WidthAndHeight. Eso hace que el tamaño inicial de la ventana sea exactamente donde lo quiero. Pero entonces las cosas ya no cambian de tamaño: puedo cambiar el tamaño de la ventana, pero su contenido no cambia de tamaño, porque especifiqué un tamaño fijo.
¿Hay alguna manera de establecer el tamaño del cliente inicial de una ventana, preferiblemente sin código detrás? (Tomaré el código detrás si esa es la única manera, pero preferiría un enfoque solo de XAML si alguien tiene uno).
Puede hacerlo en el código subyacente en el controlador de eventos de carga de una de estas dos maneras:
NOTA: El contenido de la Cuadrícula de LayoutRoot es el mismo en ambos ejemplos, pero el Ancho y la Altura en el LayoutRoot solo se especifican en el ejemplo A.
A) ClearValue en el SizeToContent de la ventana y en el ancho y alto del contenido:
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);
}
}
}
asumiendo un diseño de página similar (tenga en cuenta la configuración de SizeToContent y el controlador de eventos cargados en la ventana y el ancho y la altura especificados en el 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>
o
B) la configuración del ancho y alto de la ventana para los tamaños de marco de la ventana del cliente específicos del sistema:
utilizando 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;
}
}
}
asumiendo un diseño de página proporcional como (no tenga en cuenta la configuración de SizeToContent o el controlador de eventos cargados en la ventana o el ancho y la altura especificados en 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>
No he podido encontrar una manera de hacerlo declarativamente en XAML todavía.
Realizo lo siguiente en el constructor y agrego ResizeMode = "CanResizeWithGrip" en xaml, pero depende de la cantidad de espacio que ocupe tu contenido en el inicio
public Window1()
{
this.Height = SystemParameters.WorkArea.Height;
this.Width = SystemParameters.WorkArea.Width;
}
También paso bastante tiempo para imaginar esa historia completa. Es sorprendentemente difícil encontrar una respuesta XAML pura (código cero detrás) a esta pregunta en la red, así que aquí está la mía.
Cuando diseñamos una ventana en el diseñador WPF de Visual Studio, la forma estándar (y por defecto) es definir las propiedades de Width
y Height
en la Window
, como en 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>
La vista previa del diseñador se ve así:
Todo se ve bien, pero cuando ejecutamos la aplicación, dependiendo de la versión actual de Windows, el tema y todas las configuraciones de pantalla de jazz, hay un 99% de posibilidades de que la ventana de tiempo de ejecución no se vea como la diseñada. Aquí está lo que parece en mi Windows 8.1:
La solución inicial es, como en la respuesta de Oren, utilizar la propiedad SizeTocontent
. Básicamente le dice a WPF que defina el tamaño de la ventana a partir de su contenido (también conocido como "tamaño del cliente"), en lugar de la ventana en sí (también conocido como "tamaño del cliente + todo lo que no es cliente / chrome / border, cosas totalmente incontrolables").
Entonces, podemos definir que el Contenido sea de tamaño fijo, como este:
<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>
Y ahora, la ventana de tiempo de ejecución se ve exactamente como queremos (tenga en cuenta que la altura y el ancho de la cuadrícula no tienen exactamente los mismos valores que el original Window uno - 40/180 vs 75/190 - y eso está bien, como en el modo de diseño, ahora solo puede olvidar la propiedad Altura y Ancho de la ventana para siempre ):
¿Cómo se comporta eso cuando se cambia el tamaño de la ventana? De esta manera, la cuadrícula está centrada, lo cual está bien si desea ese comportamiento:
Pero, si queremos el comportamiento en la pregunta, ("Puedo cambiar el tamaño de la ventana, pero su contenido no cambia de tamaño con ella, porque especificé un tamaño fijo"), que era también el comportamiento original, en lugar de especificar el Ancho y Altura, podemos usar MinWidth
y MinHeight
, así:
<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>
La ventana de tiempo de ejecución tiene el mismo aspecto, pero la experiencia de cambio de tamaño ahora es comparable al diseño de ventana predeterminado original:
Ese debería ser el diseño predeterminado de WPF.
publica una excelente respuesta, pero necesitaba el tamaño de un control para diferir a los demás. Así que invertir el comportamiento del tamaño de la ventana con el atributo SizeToContent
no era lo que necesitaba. Terminé siguiendo [Tim''s] método de [Tim''s] para calcular el tamaño que no es del cliente] para restar el área de no cliente del Ancho y la Altura dinámicos de MainWindow; en un elemento <MultiBinding>
XAML. Esto se logró mediante la lectura de las propiedades SystemParameters.WindowCaptionHeight
y SystemParameters.ResizeFramVerticalBorderWidth
(consulte el código IMultiValueConverter
continuación).
<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
es el control interno de la última fila de la cuadrícula. A medida que se cambia el tamaño de MainWindow, necesito reducir este control por respeto a los demás.
El convertidor se complica al tener que manejar los tipos de objeto System.Double
y System.Windows.GridLength
. También conservo el diseño entre las sesiones de la aplicación, por lo que necesitaba que el convertidor fuera bidireccional (disculpas por el código denso).
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;
}
}