2008-10-13 17 views
11

Estoy creando un control para WPF, y tengo una pregunta para ustedes, los gurús de WPF.Áreas de desplazamiento anidadas

Quiero que mi control se pueda expandir para adaptarse a una ventana de tamaño variable.

En mi control, tengo un cuadro de lista que quiero expandir con la ventana. También tengo otros controles en el cuadro de lista (botones, texto, etc.).

Quiero poder establecer un tamaño mínimo en mi control, pero quiero que la ventana se pueda dimensionar más pequeña creando barras de desplazamiento para ver el control.

Esto crea áreas de desplazamiento anidado: una para el cuadro de lista y un ScrollViewer que envuelve todo el control.

Ahora, si el cuadro de lista está configurado en tamaño automático, nunca tendrá una barra de desplazamiento porque siempre se dibuja a tamaño completo dentro del ScrollViewer.

Solo quiero que el control se desplace si el contenido no puede ser más pequeño, de lo contrario no quiero desplazar el control; en su lugar, quiero desplazarme por el cuadro de lista dentro del control.

¿Cómo puedo alterar el comportamiento predeterminado de la clase ScrollViewer? Intenté heredar de la clase ScrollViewer y anular las clases MeasureOverride y ArrangeOverride, pero no pude encontrar la forma de medir y organizar al niño correctamente. Parece que el arreglo tiene que afectar al ScrollContentPresenter de alguna manera, no al contenido real hijo.

Cualquier ayuda/sugerencia sería muy apreciada.

+0

Muy buena pregunta. Nos encontramos con un problema muy similar nosotros mismos. – cplotts

Respuesta

0

Mientras que no recomendaría la creación de una interfaz de usuario que requiere barras de desplazamiento exteriores se puede lograr esto con bastante facilidad:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    >  
    <ScrollViewer HorizontalScrollBarVisibility="Auto" 
        VerticalScrollBarVisibility="Auto"> 
     <Grid> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto"/> 
       <RowDefinition Height="Auto"/> 
       <RowDefinition Height="Auto"/> 
      </Grid.RowDefinitions> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="*"/> 
       <ColumnDefinition Width="Auto"/> 
      </Grid.ColumnDefinitions> 
      <ListBox Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" MinWidth="200"/> 
      <Button Grid.Row="0" Grid.Column="1" Content="Button1"/> 
      <Button Grid.Row="1" Grid.Column="1" Content="Button2"/> 
      <Button Grid.Row="2" Grid.Column="1" Content="Button3"/> 
     </Grid> 
    </ScrollViewer> 
</Window> 

Realmente no recomiendo este. WPF proporciona sistemas de disposición excepcionales, como Grid, y debe intentar permitir que la aplicación cambie de tamaño según sea necesario. ¿Quizás pueda establecer MinWidth/MinHeight en la ventana para evitar este cambio de tamaño?

+0

La dificultad es que quiero poder reducir la ventana por debajo del tamaño mínimo sin recortar los controles. –

+0

¿Tienes * para permitir que la ventana se reduzca por debajo de ese tamaño mínimo? ¿Qué estás tratando de lograr al permitir eso? –

+0

No, no es necesario, pero todo el problema desaparece si no lo hago. Quiero reducir el tamaño por debajo del mínimo para permitir diseños flexibles. De hecho, estoy construyendo una aplicación que contiene controles "tipo ventana" que se pueden dimensionar y posicionar. –

2

Tu problema surge, porque Control s dentro de un ScrollViewer tienen espacio prácticamente ilimitado disponible. Por lo tanto, su interior ListBox cree que puede evitar el desplazamiento tomando la altura completa necesaria para mostrar todos sus elementos. Por supuesto, en su caso, ese comportamiento tiene el efecto secundario no deseado de ejercitar demasiado el exterior ScrollViewer. Por lo tanto,

El objetivo es conseguir que el ListBox para utilizar el visibles altura dentro de la ScrollViewer si y sólo si hay suficiente de la misma y una cierta altura mínima de otro modo. Para lograr esto, la forma más directa es a heredar de ScrollViewer y anular MeasureOverride() que pasar un tamaño apropiado availableSize (que es la availableSize dado soplado hasta el tamaño mínimo en lugar del infinito "habitual") a los Visual s encontrados mediante el uso de VisualChildrenCount y GetVisualChild(int).

+0

+1 para la explicación sobre el espacio disponible ilimitado. – Ridcully

12

He creado una clase para evitar este problema:

public class RestrictDesiredSize : Decorator 
{ 
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 

    protected override Size MeasureOverride(Size constraint) 
    { 
     Debug.WriteLine("Measure: " + constraint); 
     base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width), 
             Math.Min(lastArrangeSize.Height, constraint.Height))); 
     return new Size(0, 0); 
    } 

    protected override Size ArrangeOverride(Size arrangeSize) 
    { 
     Debug.WriteLine("Arrange: " + arrangeSize); 
     if (lastArrangeSize != arrangeSize) { 
      lastArrangeSize = arrangeSize; 
      base.MeasureOverride(arrangeSize); 
     } 
     return base.ArrangeOverride(arrangeSize); 
    } 
} 

siempre devolverá un tamaño deseado de (0,0), incluso si el elemento que contiene quiere ser más grande. Uso:

<local:RestrictDesiredSize MinWidth="200" MinHeight="200"> 
    <ListBox /> 
</local> 
+0

Esta es la solución que funcionó para nosotros. – cplotts

+0

Solución limpia, simple y elegante –

1

que usa solución Daniel s. Eso funciona genial Gracias.

Luego agregué dos propiedades de dependencia booleanas a la clase de decorador: KeepWidth y . Entonces, la nueva característica se puede suprimir para una dimensión.

Esto requiere un cambio en MeasureOverride:

protected override Size MeasureOverride(Size constraint) 
{ 
    var innerWidth = Math.Min(this._lastArrangeSize.Width, constraint.Width); 
    var innerHeight = Math.Min(this._lastArrangeSize.Height, constraint.Height); 
    base.MeasureOverride(new Size(innerWidth, innerHeight)); 

    var outerWidth = KeepWidth ? Child.DesiredSize.Width : 0; 
    var outerHeight = KeepHeight ? Child.DesiredSize.Height : 0; 
    return new Size(outerWidth, outerHeight); 
} 
0

crear un método en el código subyacente que establece MaxHeight del cuadro de lista a la altura de lo que el control es que lo contiene y otros controles. Si el Listbox tiene controles/márgenes/relleno arriba o abajo, reste sus alturas de la altura del contenedor asignada a MaxHeight. Llame a este método en los controladores de eventos "cargados" y "cambio de tamaño" de las ventanas principales.

Esto debería darte lo mejor de ambos mundos. Le está dando al ListBox un tamaño "fijo" que hará que se desplace, a pesar del hecho de que la ventana principal tiene su propia barra de desplazamiento.

0

de 2 ScrollViewer

public class ScrollExt: ScrollViewer 
{ 
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 

    public ScrollExt() 
    { 

    } 
    protected override Size MeasureOverride(Size constraint) 
    { 
     base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width), 
             Math.Min(lastArrangeSize.Height, constraint.Height))); 
     return new Size(0, 0); 
    } 

    protected override Size ArrangeOverride(Size arrangeSize) 
    { 
     if (lastArrangeSize != arrangeSize) 
     { 
      lastArrangeSize = arrangeSize; 
      base.MeasureOverride(arrangeSize); 
     } 
     return base.ArrangeOverride(arrangeSize); 
    } 
} 

código:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
     <Grid > 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="Auto" /> 
       <ColumnDefinition Width="*" /> 
      </Grid.ColumnDefinitions> 
      <TextBlock Background="Beige" Width="600" Text="Example"/> 
      <Grid Grid.Column="1" x:Name="grid"> 
        <Grid Grid.Column="1" Margin="25" Background="Green"> 
        <local:ScrollExt HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
         <Grid Width="10000" Margin="25" Background="Red" /> 
        </local:ScrollExt> 
        </Grid> 
       </Grid> 
     </Grid> 
    </ScrollViewer>