Esencialmente se requiere una reescritura completa del lienzo. Las soluciones propuestas anteriores que anulan MeasureOverride fallan porque las propiedades predeterminadas de Canvas.Left/.Top & c invalidan el Arrangment, pero TAMBIÉN necesitan invalidar la medida. (Obtiene el tamaño correcto la primera vez, pero el tamaño no cambia si mueve elementos después del diseño inicial).
La solución de cuadrícula es más o menos razonable, pero vinculante para los márgenes con el fin de obtener el desplazamiento x-y puede causar estragos en otro código (particalar en MVVM). Luché con la solución Grid view por un tiempo, pero las complicaciones con las interacciones View/ViewModel y el comportamiento de desplazamiento finalmente me llevaron a esto. Lo cual es simple y al grano, y solo funciona.
No es TAN complicado reintroducir ArrangeOverride y MeasureOverride. Y está obligado a escribir al menos la cantidad de código en cualquier otro lugar que tenga que ver con la estupidez de Grid/Margin. Así que ahí estás.
Aquí hay una solución más completa. el comportamiento de Margen no nulo no se ha probado. Si necesita algo más que el izquierdo y el superior, esto proporciona un punto de partida, al menos.
ADVERTENCIA: Debe usar las propiedades adjuntas AutoResizeCanvas.Left y AutoResizeCanvas.Top en lugar de Canvas.Left y Canvas.Top. Las propiedades restantes del lienzo no se han implementado.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Mu.Controls
{
public class AutoResizeCanvas : Panel
{
public static double GetLeft(DependencyObject obj)
{
return (double)obj.GetValue(LeftProperty);
}
public static void SetLeft(DependencyObject obj, double value)
{
obj.SetValue(LeftProperty, value);
}
public static readonly DependencyProperty LeftProperty =
DependencyProperty.RegisterAttached("Left", typeof(double),
typeof(AutoResizeCanvas),
new FrameworkPropertyMetadata(0.0, OnLayoutParameterChanged));
private static void OnLayoutParameterChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// invalidate the measure of the enclosing AutoResizeCanvas.
while (d != null)
{
AutoResizeCanvas canvas = d as AutoResizeCanvas;
if (canvas != null)
{
canvas.InvalidateMeasure();
return;
}
d = VisualTreeHelper.GetParent(d);
}
}
public static double GetTop(DependencyObject obj)
{
return (double)obj.GetValue(TopProperty);
}
public static void SetTop(DependencyObject obj, double value)
{
obj.SetValue(TopProperty, value);
}
public static readonly DependencyProperty TopProperty =
DependencyProperty.RegisterAttached("Top",
typeof(double), typeof(AutoResizeCanvas),
new FrameworkPropertyMetadata(0.0, OnLayoutParameterChanged));
protected override Size MeasureOverride(Size constraint)
{
Size availableSize = new Size(double.MaxValue, double.MaxValue);
double requestedWidth = MinimumWidth;
double requestedHeight = MinimumHeight;
foreach (var child in base.InternalChildren)
{
FrameworkElement el = child as FrameworkElement;
if (el != null)
{
el.Measure(availableSize);
Rect bounds, margin;
GetRequestedBounds(el,out bounds, out margin);
requestedWidth = Math.Max(requestedWidth, margin.Right);
requestedHeight = Math.Max(requestedHeight, margin.Bottom);
}
}
return new Size(requestedWidth, requestedHeight);
}
private void GetRequestedBounds(
FrameworkElement el,
out Rect bounds, out Rect marginBounds
)
{
double left = 0, top = 0;
Thickness margin = new Thickness();
DependencyObject content = el;
if (el is ContentPresenter)
{
content = VisualTreeHelper.GetChild(el, 0);
}
if (content != null)
{
left = AutoResizeCanvas.GetLeft(content);
top = AutoResizeCanvas.GetTop(content);
if (content is FrameworkElement)
{
margin = ((FrameworkElement)content).Margin;
}
}
if (double.IsNaN(left)) left = 0;
if (double.IsNaN(top)) top = 0;
Size size = el.DesiredSize;
bounds = new Rect(left + margin.Left, top + margin.Top, size.Width, size.Height);
marginBounds = new Rect(left, top, size.Width + margin.Left + margin.Right, size.Height + margin.Top + margin.Bottom);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
Size availableSize = new Size(double.MaxValue, double.MaxValue);
double requestedWidth = MinimumWidth;
double requestedHeight = MinimumHeight;
foreach (var child in base.InternalChildren)
{
FrameworkElement el = child as FrameworkElement;
if (el != null)
{
Rect bounds, marginBounds;
GetRequestedBounds(el, out bounds, out marginBounds);
requestedWidth = Math.Max(marginBounds.Right, requestedWidth);
requestedHeight = Math.Max(marginBounds.Bottom, requestedHeight);
el.Arrange(bounds);
}
}
return new Size(requestedWidth, requestedHeight);
}
public double MinimumWidth
{
get { return (double)GetValue(MinimumWidthProperty); }
set { SetValue(MinimumWidthProperty, value); }
}
public static readonly DependencyProperty MinimumWidthProperty =
DependencyProperty.Register("MinimumWidth", typeof(double), typeof(AutoResizeCanvas),
new FrameworkPropertyMetadata(300.0,FrameworkPropertyMetadataOptions.AffectsMeasure));
public double MinimumHeight
{
get { return (double)GetValue(MinimumHeightProperty); }
set { SetValue(MinimumHeightProperty, value); }
}
public static readonly DependencyProperty MinimumHeightProperty =
DependencyProperty.Register("MinimumHeight", typeof(double), typeof(AutoResizeCanvas),
new FrameworkPropertyMetadata(200.0,FrameworkPropertyMetadataOptions.AffectsMeasure));
}
}
me cambiaron de lienzo a la cuadrícula y funcionó, después de algunos ajustes. Tuve que hacer dos cambios: (1) en todos los lugares donde solía establecer las propiedades adjuntas Canvas.Left y Canvas.Top, ahora configuré las propiedades regulares Margin.Left y Margin.Top (Margin.Right y Margin.Bottom se pueden a la izquierda en 0); (2) use la Alineación Horizontal = "Izquierda" y la Alineación Vertical = "Superior" en cada elemento en la Rejilla. El modo predeterminado "Estirar" puede hacer que los elementos terminen en el centro cuando los márgenes son 0. – Qwertie
"Para los casos en los que desea que el contenido secundario se redimensione y alinee automáticamente, generalmente es mejor usar un elemento Grid". Pero la pregunta original es sobre el cambio de tamaño del lienzo, no los elementos secundarios.Creo que la solución proporcionada por illef a continuación responde mejor esta pregunta y evita establecer tantas propiedades en todos los elementos secundarios. Con la respuesta de Illef, simplemente configura las propiedades adjuntas Superior e Izquierda que creo que es una solución más ordenada. Una vez que haya definido el nuevo objeto Canvas, se trata de una solución reutilizable que puede usarse en cualquier otro lugar de su proyecto. – MikeKulls
El problema que tengo con esto es que al renderizarlo no se superponen los controles y se desvían por todos lados, a menos que, por supuesto, estoy haciendo algo mal. Además, la solución illef es un error de cálculo por alguna razón. –