2010-12-28 9 views
15

Voglio creare una Viewbox (o qualcosa di simile) che riduca solo la sua altezza e quindi stenda il suo contenuto orizzontalmente.Rendere verticale una scala Viewbox ma allungarla orizzontalmente

Se faccio questo:

<Viewbox> 
    <StackPanel> 
    <Button>Foo</Button> 
    <Button>Bar</Button> 
    </StackPanel> 
</Viewbox> 

tanto sono questo:

http://www.excastle.com/misc/viewbox-center.png

Agisce come se entrambi i pulsanti avevano HorizontalAlignment = "center", e poi scale il risultato . Ma non voglio HorizontalAlignment = "Center"; Voglio HorizontalAlignment = "Stretch", in questo modo:

http://www.excastle.com/misc/viewbox-stretch.png

quindi voglio per leggere all'altezza desiderata il suo contenuto, calcolare un fattore di scala basata solo su l'altezza, e quindi consentire il contenuto scalata a allungare orizzontalmente.

Esiste un modo per farlo utilizzando la Viewbox e/o alcuni pannelli di terze parti?

risposta

16

Non c'è alcun controllo incluso con WPF ma è possibile scriverne uno senza troppi problemi.Ecco un ViewboxPanel personalizzato che ha le vostre specifiche:

public class ViewboxPanel : Panel 
{ 
    private double scale; 

    protected override Size MeasureOverride(Size availableSize) 
    { 
     double height = 0; 
     Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
     foreach (UIElement child in Children) 
     { 
      child.Measure(unlimitedSize); 
      height += child.DesiredSize.Height; 
     } 
     scale = availableSize.Height/height; 

     return availableSize; 
    } 

    protected override Size ArrangeOverride(Size finalSize) 
    { 
     Transform scaleTransform = new ScaleTransform(scale, scale); 
     double height = 0; 
     foreach (UIElement child in Children) 
     { 
      child.RenderTransform = scaleTransform; 
      child.Arrange(new Rect(new Point(0, scale * height), new Size(finalSize.Width/scale, child.DesiredSize.Height))); 
      height += child.DesiredSize.Height; 
     } 

     return finalSize; 
    } 
} 

e si utilizza in questo modo:

<local:ViewboxPanel> 
    <Button>Foo</Button> 
    <Button>Bar</Button> 
</local:ViewboxPanel> 

decisamente ha bisogno po 'di lavoro, ma questo potrebbe iniziare.

+0

Grazie per la grande soluzione. Ho aggiunto 'if (System.ComponentModel.DesignerProperties.GetIsInDesignMode (this)) { availableSize = new Size (200, 200); } ' nel metodo' override protetto Size MeasureOverride (Size availableSize) 'quindi il progettista funziona senza generare un'eccezione. – Mike

0

Sono abbastanza sicuro che la risposta sia "non facilmente".

Ecco un'idea che sembra funzionare, ma è una specie di goffo:

  1. Getta il tuo StackPanel in un UserControl (UserControl1 nell'esempio qui sotto).

  2. Inserire due copie di questo UserControl in un contenitore (una griglia nell'esempio seguente).

    a. Allinea la prima copia in alto/a sinistra in modo che rimanga alla sua dimensione predefinita. Questa copia è strettamente per scopi di misurazione e dovrebbe avere la Visibilità impostata su Nascosto.

    b. Posiziona la seconda copia all'interno di una Viewbox. Questa copia è quella che l'utente vedrà effettivamente.

  3. Utilizzare un MultiBinding con un MultiValueConverter per regolare la larghezza della seconda UserControl in modo che abbia le proporzioni corrette prima esso viene espanso dal Viewbox.

Ecco il markup:

<Grid> 
    <local:UserControl1 x:Name="RawControl" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden" /> 
    <Viewbox> 
     <local:UserControl1> 
      <local:UserControl1.Width> 
       <MultiBinding Converter="{StaticResource WidthAdjuster}"> 
        <Binding ElementName="RawControl" Path="ActualHeight" /> 
        <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth" /> 
        <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualHeight" /> 
       </MultiBinding> 
      </local:UserControl1.Width> 
     </local:UserControl1> 
    </Viewbox> 
</Grid> 

Ecco il MultiValueConverter

public class WidthAdjuster : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     var rawHeight = (double)values[0]; 
     var containerWidth = (double)values[1]; 
     var containerHeight = (double)values[2]; 
     var ratio = containerWidth/containerHeight; 
     return rawHeight * ratio; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Risultato per 525 x 350 contenitore

alt text

4

Per mantenere la larghezza di funzionare correttamente:

protected override Size MeasureOverride(Size availableSize) 
    { 
     double height = 0; 
     Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
     foreach (UIElement child in Children) 
     { 
      child.Measure(unlimitedSize); 
      height += child.DesiredSize.Height; 
     } 
     scale = availableSize.Height/height; 

     foreach (UIElement child in Children) 
     { 
      unlimitedSize.Width = availableSize.Width/scale; 
      child.Measure(unlimitedSize); 
     }   

     return availableSize; 
    } 
0

ho avuto un problema quasi simile. Il mio pannello doveva adattarsi a tutti i suoi bambini, mettendo il tham in fila e allungandoli, riempiendo il pannello uniformemente. Algoritmo sopra utilizza renderizza la trasformazione per ridimensionare gli elementi. Il problema è che la trasformazione render distende i bambini stessi, ma ignora il margine. Se il margine è alto e il coefficiente di scala è inferiore a 1, gli elementi cadono dal pannello. È necessario correggere la scala per RenderTransform in base al margine su quell'asse e utilizzare lo stesso coefficiente di scala per l'organizzazione. MeasureOverride che ho usato era

protected override Size MeasureOverride(Size availableSize) 
{ 
    double width = 0; double maxHeight = 0; double mY=0; double mX=0; 
    Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
    foreach (UIElement child in Children) 
    { 
     child.Measure(unlimitedSize); 
     width += child.DesiredSize.Width; 
     maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); 

     FrameworkElement cld = child as FrameworkElement; 
     mY = cld.Margin.Top + cld.Margin.Bottom; 
     mX = cld.Margin.Left + cld.Margin.Right; 
    } 

    double scaleX = availableSize.Width/width; 
    double scaleY = availableSize.Height/maxHeight; 
    //That is scale for arranging 
    positionScaling = Math.Min(scaleX, scaleY); 

    try 
    { 
     // Let FrameworkElement hight be Xn. mY = Element.Margin.Top + Element.Margin.bottom. 
     // DesiredSize includes margin therefore: 
     // (Yn + mY) * scaleY = availableSize.Height 
     // But render transform doesn't scales margin. Actual render height with margin will be 
     // Yn * RenderScaleY + mY = availableSize.Height; 
     // We must find render transform scale coeff like this: 

     double yn = availableSize.Height/scaleY - mY; 
     scaleY = (availableSize.Height - mY)/yn; 

     double xn = availableSize.Width/scaleX - mX; 
     scaleX = (availableSize.Width - mX)/xn; 


     scale = Math.Min(scaleX, scaleY); //scale to use in RenderTransform 
     // In my project all children are similar in size and margin, algorithm BREAKS otherwise!!! 
    } 
    catch { scale = 1; } 

    return availableSize; 
} 

Ancora una volta: nel mio progetto tutti i bambini sono simili per dimensioni e del margine, l'algoritmo ROMPE altrimenti.

Problemi correlati