wpf - ejemplos - Hacer una Viewbox escalar verticalmente pero estirar horizontalmente
tutorial basico wpf (4)
Estoy bastante seguro de que la respuesta es "no fácil".
Aquí hay una idea que parece funcionar pero es un poco torpe:
Lanza tu StackPanel en un UserControl (
UserControl1
en el ejemplo a continuación).Coloque dos copias de este UserControl en un contenedor (una Cuadrícula en el ejemplo a continuación).
a. Alinee la primera copia arriba / izquierda para que permanezca en su tamaño predeterminado. Esta copia es estrictamente para fines de medición y debe tener su Visibilidad establecida en Oculto.
segundo. Coloque la segunda copia dentro de una Viewbox. Esta copia es la que el usuario realmente verá.
Utilice un MultiBinding con un MultiValueConverter para ajustar el ancho del segundo UserControl para que tenga la relación de aspecto correcta antes de que se expanda por Viewbox.
Aquí está el marcado:
<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>
Aquí está el 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();
}
}
Resultado para contenedor de 525 x 350
Quiero hacer una Viewbox (o algo similar) que solo escala su altura, y luego estira su contenido horizontalmente.
Si hago esto:
<Viewbox>
<StackPanel>
<Button>Foo</Button>
<Button>Bar</Button>
</StackPanel>
</Viewbox>
entonces entiendo esto:
http://www.excastle.com/misc/viewbox-center.png
Actúa como si ambos botones tuvieran HorizontalAlignment = "Center", y luego escala el resultado. Pero no quiero HorizontalAlignment = "Center"; Quiero HorizontalAlignment = "Stretch", así:
http://www.excastle.com/misc/viewbox-stretch.png
Por lo tanto, quiero que lea la altura deseada de su contenido, calcule un factor de escala basado solo en la altura y luego permita que el contenido escalado se estire horizontalmente.
¿Hay alguna manera de lograr esto usando Viewbox y / o algún panel de terceros?
No hay tal control incluido con WPF, pero puede escribir uno usted mismo sin demasiados problemas. Aquí hay un ViewboxPanel personalizado que tiene sus especificaciones:
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;
}
}
y lo usas así:
<local:ViewboxPanel>
<Button>Foo</Button>
<Button>Bar</Button>
</local:ViewboxPanel>
Definitivamente necesita un poco de trabajo, pero esto podría ayudarte a comenzar.
Para mantener el ancho para que funcione correctamente:
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;
}
Tuve un problema casi similar. Mi panel tenía que adaptarse a todos sus niños, colocando tham en una fila y estirándolos, llenando el panel de manera uniforme. El algoritmo anterior usa transformación de transformación para escalar elementos. El problema es que la transformación de render estira a los niños ellos mismos, pero ignora el margen. Si el margen es alto y el coeficiente de escala está por debajo de 1, los elementos se caen del panel. Debe corregir la escala para RenderTransform de acuerdo con el margen en ese eje y usar el mismo coeficiente de escala para organizar. MeasureOverride que utilicé fue
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;
}
Una vez más: en mi proyecto, todos los niños son similares en tamaño y margen, el algoritmo ROMPE de lo contrario.