Essenzialmente si richiede una riscrittura completa di tela di canapa. Le soluzioni proposte in precedenza che annullano MeasureOverride non riescono perché le proprietà predefinite Canvas.Left/.Top & c invalidano l'Arrangment, ma ANCHE devono invalidare la misura. (Si ottiene la misura giusta la prima volta, ma la dimensione non cambia se si spostano elementi dopo il layout iniziale).
La soluzione Grid è più o meno ragionevole ma vincolante per i margini al fine di ottenere lo spostamento x-y può causare il caos su altro codice (in particolare in MVVM). Ho faticato con la soluzione Grid view per un po ', ma le complicazioni con le interazioni View/ViewModel e il comportamento di scorrimento mi hanno finalmente portato a questo. Che è semplice e al punto, e funziona solo.
Non è così complicato implementare nuovamente ArrangeOverride e MeasureOverride. E tu sei destinato a scrivere almeno altrettanti codici che trattano della stupidità Grid/Margin. Quindi eccoti.
Ecco una soluzione più completa. il comportamento del margine diverso da zero non è stato verificato. Se hai bisogno di qualcosa di diverso da Left e Top, allora questo fornisce un punto di partenza, almeno.
ATTENZIONE: è necessario utilizzare AutoResizeCanvas.Left e AutoResizeCanvas.Top proprietà collegate anziché Canvas.Left e Canvas.Top. Le proprietà restanti della tela non sono state implementate.
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));
}
}
Sono passato da Canvas a Grid e ha funzionato, dopo alcuni ritocchi. Ho dovuto apportare due modifiche: (1) ovunque che ho usato per impostare le proprietà allegate Canvas.Left e Canvas.Top, ora ho impostato le proprietà regolari Margin.Left e Margin.Top (Margin.Right e Margin.Bottom possono essere lasciato a 0); (2) utilizzare HorizontalAlignment = "Left" e VerticalAlignment = "Top" su ogni elemento della griglia. La modalità "Stretch" predefinita può far sì che gli elementi finiscano al centro quando i margini sono 0. – Qwertie
"Per i casi in cui desideri ridimensionare e allineare automaticamente i contenuti secondari, solitamente è preferibile utilizzare un elemento Grid." Ma la domanda iniziale riguarda il ridimensionamento della tela, non gli elementi figlio.Penso che la soluzione fornita da illef qui sotto risponda meglio a questa domanda ed eviti di impostare così tante proprietà su tutti gli elementi figli. Con la risposta di illef è sufficiente impostare le proprietà associate Top e Left che ritengo sia una soluzione più ordinata. Una volta definito il nuovo oggetto Canvas, si tratta di una soluzione riutilizzabile che può essere utilizzata altrove nel progetto. – MikeKulls
Il problema che ho con questo è che quando si esegue il rendering non si sovrappongono i controlli e vengono smistati ovunque, a meno che, naturalmente, non stia facendo qualcosa di sbagliato. Anche la soluzione illef è male calcolata per qualche motivo. –