2009-08-13 9 views
21

Estoy intentando escribir un proyecto de aprendizaje de WPF simple que crea un conjunto de botones dentro de una ventana principal redimensionable. Debe haber un Button por entrada en una colección, y el contenido de esa colección puede variar durante el tiempo de ejecución. Quiero que los botones llenen toda la ventana (por ejemplo, 1 botón @ 100% de ancho, 2 botones @ 50% de ancho, 3 botones a 33% de ancho, etc., todo al 100% de altura). Una versión simplificada de lo que he escrito hasta ahora es:Controles de estiramiento para llenar ItemsControl

<ItemsControl x:Name="itemscontrolButtons" ItemsSource="{Binding}"> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Tag="{Binding}"> 
     <TextBlock Text="{Binding}" /> 
     </Button> 
    </DataTemplate> 
    </ItemsControl.ItemTemplate> 
    <ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel Orientation="Horizontal" /> 
    </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 
...  
List<string> listButtonTexts = new List<string>() { "Button1", "Button2" }; 
... 
itemscontrolButtons.DataContext = listButtonTexts; 

Esto se traduce en lo siguiente:

alt text

he sido incapaz de hacer que los botones se extienden a la anchura y mis intentos utilizar un Grid en lugar de StackPanel fue infructuoso.

Luego, como una mejora opcional, agradecería sugerencias sobre cómo ajustarlo de manera que si hay tantos botones que no pueden caber correctamente en una línea o son más estrechos que un umbral, se ajustará a una nueva línea (reduciendo a la mitad las alturas de los botones si va de 1 a 2 filas).

Me gustaría resaltar que me gustaría saber cómo hacer esto WPF way. Me doy cuenta de que puedo consumir mensajes de cambio de tamaño de ventana y cambiar el tamaño de los controles de forma explícita, y así es como lo hubiera hecho con MFC o WinForms, pero por lo que he leído, no es así como deberían hacerse con WPF.

Respuesta

48

Reemplazar el panel del control de elemento con un UniformGrid, este tamaño voluntad todos los niños así que todo encaja exactamente:

<ItemsControl> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <UniformGrid Rows="1"/> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 
+0

Eso era justo lo que necesitaba para mi objetivo principal, gracias. – Gregyski

+0

Gracias a la respuesta de Nir, pude cumplir el criterio opcional. Los detalles están en la respuesta que he enviado a esta pregunta. Se recomendó en Meta que maneje esta situación de esta manera. http://meta.stackexchange.com/questions/14306/my-improved-answer-based-on-anothers-accepted-answer-for-my-ow-question – Gregyski

+0

¡Gracias! Esto es exactamente lo que me faltaba para espaciar los elementos y su panel de manera uniforme en un cuadro de lista. –

0
<!-- width must be explicitly set for this example --> 
<StackPanel 
    Name="MyStack" 
    Orientation="Horizontal" 
    Width="250" 
    Load="Window_Loaded"> 
    <Button/> 
    <Button/> 
    <Button/> 
</StackPanel> 

public void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    UIElementCollection elements = MyStack.Children; 
    int count = elements.Count; 

    foreach (UIElement element in elements) 
    { 
     if (element is Button) 
      ((Button)element).Width = MyStack.Width/count; 
    } 
} 
+0

He editado mi publicación solo 20 minutos antes de su respuesta para explicar que quería evitar establecer explícitamente el ancho. Lamento haber pensado incluir ese requisito inicialmente. Gestionar explícitamente las dimensiones de esta manera (y al manejar los mensajes de cambio de tamaño también) puede ser necesario para mi requisito secundario, si se utilizara WrapPanel. Gracias. – Gregyski

2

yo era capaz de construir sobre la respuesta de Nir para cumplir con mi criterio opcional que permite el estiramiento administrado por WPF con múltiples filas.

Primero debe poder alterar las propiedades de la plantilla UniformGrid. Para hacer eso, necesitas almacenar una referencia a él. Hay multiple methods for accomplishing this. Elegí manejar el evento Loaded para UniformGrid y almacenar una referencia en ese momento.

<ItemsControl ItemsSource="{Binding}"> 
    <ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <UniformGrid Rows="1" Loaded="UniformGrid_Loaded" /> 
    </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 

Y en el código subyacente:

UniformGrid uniformgridButtons; 
private void UniformGrid_Loaded(object sender, RoutedEventArgs e) 
{ 
    uniformgridButtons = sender as UniformGrid; 
} 

Entonces, manejar la SizeChanged evento y ajustar el parámetro filas de acuerdo a cualquier criterio que usted desee.

private void Window_SizeChanged(object sender, SizeChangedEventArgs e) 
{ 
    if ((uniformgridButtons != null) && (this.Width > 0)) 
    { 
    // Set row count according to your requirements 
    uniformgridButtons.Rows = (int)(1 + ((this.MinWidth * iButtonCount)/this.Width)); 
    } 
} 

Así que esto permite WPF para gestionar el estiramiento como en la respuesta de Nir, pero le permite ajustar de forma explícita el número de filas.El código anterior da este resultado:

alt text

(muestra de animación here)


Actualizar 2009/08/28:

me estaba ajustando mi solicitud para que el aprendizaje ItemsControl estaba en un DataTemplate en un ResourceDictionary por separado. Sin embargo, eventos como Loaded no se pueden manejar en un archivo externo ResourceDictionary XAML porque no tiene código subyacente (generalmente). Por lo tanto, necesitaba guardar en la memoria caché la referencia al UniformGrid usando una técnica diferente.

En su lugar utilicé Alan Haigh's FindItemsPanel method (10th reply). En primer lugar, he sustituido el ItemsControl con:

<ContentPresenter x:Name="contentpresenterButtons" Content="{Binding obscolButtons}" /> 

Luego, en mi ResourceDictionary, puse el ItemsControl en el DataTemplate:

<DataTemplate DataType="{x:Type local:Buttons}"> 
    <ItemsControl ... 
</DataTemplate> 

Por último, guardé la referencia a la plantilla UniformGrid como tan:

private void Window_Loaded(object sender, RoutedEventArgs e) { 
    uniformgridButtons = FindItemsPanel<UniformGrid>(contentpresenterButtons); 
    // Also call the code in Window_SizeChanged which I put in another method 
} 

Esto funciona igual que mi solución anterior pero sin requerir un controlador de eventos en el ResourceDictionary.

Cuestiones relacionadas