По сути это требует полного переписывания Canvas. Предыдущие предложенные решения, которые переопределяют MeasureOverride, терпят неудачу, потому что свойства Canvas.Left / .Top & c по умолчанию делают недействительным Arrangment, но ТАКЖЕ нужно аннулировать меру. (Вы получаете правильный размер в первый раз, но размер не изменится, если вы переместите элементы после первоначального макета).
Решение Grid более или менее разумно, но привязка к полям для получения смещения по оси x может нанести ущерб другому коду (частично в MVVM). Некоторое время я боролся с решением Grid View, но сложности с взаимодействиями View / ViewModel и поведением прокрутки, наконец, привели меня к этому. Что просто и по существу, и просто работает.
Нет ничего сложного в том, чтобы повторно реализовать ArrangeOverride и MeasureOverride. И вы должны написать хотя бы столько же кода в другом месте, касающегося глупости Grid / Margin. Итак, вы здесь.
Вот более полное решение. ненулевое маржинальное поведение не проверено. Если вам нужно что-то кроме Left и Top, то это, по крайней мере, отправная точка.
ВНИМАНИЕ: Вы должны использовать присоединенные свойства AutoResizeCanvas.Left и AutoResizeCanvas.Top вместо Canvas.Left и Canvas.Top. Остальные свойства Canvas не были реализованы.
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));
}
}