WPF: Comment faire un redimensionnement automatique du canevas?

Je voudrais que mon Canvas automatiquement redimensionné à la taille de ses éléments, de sorte que les ScrollViewer défilement ScrollViewer aient la plage correcte. Est-ce que cela peut être fait en XAML?

       

Dans le code ci-dessus, le canevas a toujours la taille 0, bien qu’il ne coupe pas ses enfants.

Non, ce n’est pas possible (voir l’extrait de MSDN ci-dessous). Cependant, si vous voulez avoir des barres de défilement et un redimensionnement automatique, envisagez plutôt d’utiliser une grid et utilisez la propriété Margin pour positionner vos éléments sur cette grid. La grid indiquera à ScrollView quelle taille il veut être et vous obtiendrez la scrollbars .. Canvas indiquera toujours au ScrollViewer qu’il n’a besoin d’aucune taille .. 🙂

Grid vous permet de profiter des deux mondes – Tant que vous mettez tous les éléments dans une seule cellule, vous obtenez les deux: positionnement arbitraire et redimensionnement automatique. En général, il est bon de se rappeler que la plupart des contrôles de panneau (DockPanel, StackPanel, etc.) peuvent être implémentés via un contrôle Grid.

De MSDN :

Canvas est le seul élément de panneau qui ne possède pas de caractéristiques de mise en page inhérentes. Un canevas a des propriétés de hauteur et de largeur par défaut de zéro, sauf s’il s’agit d’un enfant d’un élément qui redimensionne automatiquement ses éléments enfants. Les éléments enfants d’un canevas ne sont jamais redimensionnés, ils sont juste positionnés à leurs coordonnées désignées. Cela offre une flexibilité pour les situations dans lesquelles les contraintes de dimensionnement inhérentes ou l’alignement ne sont pas nécessaires ou souhaités. Pour les cas dans lesquels vous souhaitez que le contenu enfant soit automatiquement redimensionné et aligné, il est généralement préférable d’utiliser un élément Grid.

J’espère que cela t’aides

Je ne fais que copier la réponse d’illef ici mais en réponse à PilotBob, vous définissez simplement un object de type canvas comme celui-ci

 public class CanvasAutoSize : Canvas { protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) { base.MeasureOverride(constraint); double width = base .InternalChildren .OfType() .Max(i => i.DesiredSize.Width + (double)i.GetValue(Canvas.LeftProperty)); double height = base .InternalChildren .OfType() .Max(i => i.DesiredSize.Height + (double)i.GetValue(Canvas.TopProperty)); return new Size(width, height); } } 

puis utilisez CanvasAutoSize dans votre XAML.

   

Je préfère cette solution à celle présentée ci-dessus qui utilise la grid comme elle fonctionne à travers les propriétés attachées et nécessite simplement de définir moins de propriétés sur les éléments.

Je pense que vous pouvez redimensionner Canvas en ArrangeOverride méthodes MeasureOverride ou ArrangeOverride .

Ce travail n’est pas difficile.

Vous pouvez voir ce post. http://illef.tistory.com/entry/Canvas-supports-ScrollViewer

J’espère que ceci vous aide.

Je vous remercie.

Je vois que vous avez une solution viable, mais je pensais partager.

   ...Content...   

La technique ci-dessus vous permettra d’imbriquer une grid dans un canevas et d’avoir un redimensionnement dynamic. L’utilisation ultérieure de la reliure de dimension permet de mélanger des matériaux dynamics avec des matériaux statiques, d’effectuer des superpositions, etc. Il y a trop de possibilités à mentionner, certaines plus difficiles que d’autres. Par exemple, j’utilise cette approche pour simuler un contenu animé se déplaçant d’un emplacement de grid à un autre – effectuant le placement réel lors de l’événement d’achèvement de l’animation. Bonne chance.

Essentiellement, cela nécessite une réécriture complète de Canvas. Les solutions proposées précédentes qui remplacent MeasureOverride échouent car les propriétés par défaut de Canvas.Left / .Top & c invalident Arrangement, mais doivent également invalider la mesure. (Vous obtenez la bonne taille la première fois, mais la taille ne change pas si vous déplacez des éléments après la mise en page initiale).

La solution de grid est plus ou moins raisonnable, mais la liaison aux marges pour obtenir un déplacement xy peut causer des ravages sur d’autres codes (élémentaire dans MVVM). J’ai eu du mal avec la solution d’affichage Grid pendant un moment, mais les complications avec les interactions View / ViewModel et le comportement de défilement m’ont finalement conduit à cela. Ce qui est simple et précis, et fonctionne juste.

Ce n’est pas si compliqué de ré-implémenter ArrangeOverride et MeasureOverride. Et vous êtes obligé d’écrire au moins autant de code ailleurs que sur la stupidité Grille / Marge. Alors voilà.

Voici une solution plus complète. Le comportement de marge non nul n’est pas testé. Si vous avez besoin de quelque chose d’autre que Left et Top, cela fournit au moins un sharepoint départ.

AVERTISSEMENT: vous devez utiliser les propriétés AutoResizeCanvas.Left et AutoResizeCanvas.Top au lieu de Canvas.Left et Canvas.Top. Les propriétés de Canvas restantes n’ont pas été implémentées.

 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)); } } 

Lier la hauteur / largeur à la taille réelle du contrôle dans la canvas a fonctionné pour moi:

       
 void MainWindow_Loaded(object sender, RoutedEventArgs e) { autoSizeCanvas(canvas1); } void autoSizeCanvas(Canvas canv) { int height = canv.Height; int width = canv.Width; foreach (UIElement ctrl in canv.Children) { bool nullTop = ctrl.GetValue(Canvas.TopProperty) == null || Double.IsNaN(Convert.ToDouble(ctrl.GetValue(Canvas.TopProperty))), nullLeft = ctrl.GetValue(Canvas.LeftProperty) == null || Double.IsNaN(Convert.ToDouble(ctrl.GetValue(Canvas.LeftProperty))); int curControlMaxY = (nullTop ? 0 : Convert.ToInt32(ctrl.GetValue(Canvas.TopProperty))) + Convert.ToInt32(ctrl.GetValue(Canvas.ActualHeightProperty) ), curControlMaxX = (nullLeft ? 0 : Convert.ToInt32(ctrl.GetValue(Canvas.LeftProperty))) + Convert.ToInt32(ctrl.GetValue(Canvas.ActualWidthProperty) ); height = height < curControlMaxY ? curControlMaxY : height; width = width < curControlMaxX ? curControlMaxX : width; } canv.Height = height; canv.Width = width; } 

Dans la fonction, j'essaie de trouver la position X maximale et la position Y, où les contrôles dans le canevas peuvent résider.

Utilisez la fonction uniquement dans l'événement Loaded ou ultérieurement et non dans le constructeur. La fenêtre doit être mesurée avant le chargement.

En guise d’amélioration de la réponse de @ MikeKulls, voici une version qui ne lance pas d’exception quand il n’ya pas d’éléments d’interface utilisateur dans le canevas ou d’éléments d’interface utilisateur sans les propriétés Canvas.Top ou Canvas.Left:

 public class AutoResizedCanvas : Canvas { protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) { base.MeasureOverride(constraint); double width = base .InternalChildren .OfType() .Where(i => i.GetValue(Canvas.LeftProperty) != null) .Max(i => i.DesiredSize.Width + (double)i.GetValue(Canvas.LeftProperty)); if (Double.IsNaN(width)) { width = 0; } double height = base .InternalChildren .OfType() .Where(i => i.GetValue(Canvas.TopProperty) != null) .Max(i => i.DesiredSize.Height + (double)i.GetValue(Canvas.TopProperty)); if (Double.IsNaN(height)) { height = 0; } return new Size(width, height); } } 

J’ai également rencontré ce problème, mon problème était que la grid ne se redimensionnait pas automatiquement lorsque le canevas était redimensionné grâce à la fonction MeasureOverride surchargée.

mon problème: boucle WPF MeasureOverride

J’ai pu obtenir le résultat que vous recherchez en ajoutant simplement un nouvel événement de taille au contrôle qui contenait les données à l’origine de la croissance du canevas. Une fois que le canevas atteint l’étendue du visualiseur de défilement, les barres de défilement apparaissent. Je viens d’affecter l’expression lambda suivante à l’événement de taille modifiée du contrôle:

 text2.SizeChanged += (s, e) => { DrawingCanvas.Height = e.NewSize.Height; DrawingCanvas.Width = e.NewSize.Width; };