Mesurer les contrôles créés lors de l'exécution dans WPF
Je reconnais que c'est une question populaire, mais je ne pouvais pas trouver quelque chose qui répond exactement, mais je prie de m'excuser si j'ai raté quelque chose dans mes recherches.
Je suis en train de créer, puis de mesurer un contrôle au moment de l'exécution en utilisant le code suivant (les mesures seront ensuite utilisés pour chapiteau de style défiler les contrôles de chaque contrôle est d'une taille différente):
Label lb = new Label();
lb.DataContext= task;
Style style = (Style)FindResource("taskStyle");
lb.Style = style;
cnvMain.Children.Insert(0,lb);
width = lb.RenderSize.Width;
width = lb.ActualWidth;
width = lb.Width;
Le code crée une Étiquette de contrôle et applique un style à elle. Le style contient mon modèle de contrôle qui se lie à l'objet de la "tâche". Lorsque je crée l'objet, il s'affiche parfaitement, cependant lorsque j'essaie de mesurer le contrôle à l'aide de l'un des biens ci-dessus j'obtiens les résultats suivants (je fais un pas de travers et inspecter chaque propriété):
lb.Width = NaN
lb.RenderSize.Width = 0
lb.ActualWidth = 0
Est-il possible d'obtenir le rendu de la hauteur et la largeur de contrôle que vous avez créés lors de l'exécution?
Mise à JOUR:
Désolé pour désélectionner les solutions que de réponses. Ils travaillent sur la base d'un système d'exemple j'ai mis en place, mais apparemment pas avec ma solution complète.
Je pense que c'est peut-être quelque chose à voir avec le style, donc désolé pour le désordre, mais je suis coller en entier ici.
D'abord, les ressources:
<Storyboard x:Key="mouseOverGlowLeave">
<DoubleAnimation From="8" To="0" Duration="0:0:1" BeginTime="0:0:2" Storyboard.TargetProperty="GlowSize" Storyboard.TargetName="Glow"/>
</Storyboard>
<Storyboard x:Key="mouseOverTextLeave">
<ColorAnimation From="{StaticResource buttonLitColour}" To="Gray" Duration="0:0:3" Storyboard.TargetProperty="Color" Storyboard.TargetName="ForeColour"/>
</Storyboard>
<Color x:Key="buttonLitColour" R="30" G="144" B="255" A="255" />
<Storyboard x:Key="mouseOverText">
<ColorAnimation From="Gray" To="{StaticResource buttonLitColour}" Duration="0:0:1" Storyboard.TargetProperty="Color" Storyboard.TargetName="ForeColour"/>
</Storyboard>
Et le style lui-même:
<Style x:Key="taskStyle" TargetType="Label">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Canvas x:Name="cnvCanvas">
<Border Margin="4" BorderBrush="Black" BorderThickness="3" CornerRadius="16">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="1" Color="SteelBlue"/>
<GradientStop Offset="0" Color="dodgerBlue"/>
</LinearGradientBrush>
</Border.Background>
<DockPanel Margin="8">
<DockPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="corbel"/>
<Setter Property="FontSize" Value="40"/>
</Style>
</DockPanel.Resources>
<TextBlock VerticalAlignment="Center" Margin="4" Text="{Binding Path=Priority}"/>
<DockPanel DockPanel.Dock="Bottom">
<Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<TextBlock Margin="4" DockPanel.Dock="Right" Text="{Binding Path=Estimate}"/>
</Border>
<Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<TextBlock Margin="4" DockPanel.Dock="Left" Text="{Binding Path=Due}"/>
</Border>
</DockPanel>
<DockPanel DockPanel.Dock="Top">
<Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<TextBlock Margin="4" Foreground="LightGray" DockPanel.Dock="Left" Text="{Binding Path=List}"/>
</Border>
<Border Margin="4" BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="4">
<TextBlock Margin="4" Foreground="White" DockPanel.Dock="Left" Text="{Binding Path=Name}"/>
</Border>
</DockPanel>
</DockPanel>
</Border>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Aussi, le code que j'utilise:
Label lb = new Label(); //the element I'm styling and adding
lb.DataContext= task; //set the data context to a custom class
Style style = (Style)FindResource("taskStyle"); //find the above style
lb.Style = style;
cnvMain.Children.Insert(0,lb); //add the style to my canvas at the top, so other overlays work
lb.UpdateLayout(); //attempt a full layout update - didn't work
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
//Synchronize on control rendering
double width = lb.RenderSize.Width;
width = lb.ActualWidth;
width = lb.Width;
})); //this didn't work either
lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); //nor did this
double testHeight = lb.DesiredSize.Height;
double testWidth = lb.RenderSize.Width; //nor did this
width2 = lb.ActualWidth; //or this
width2 = lb.Width; //or this
//positioning for my marquee code - position off-screen to start with
Canvas.SetTop(lb, 20);
Canvas.SetTop(lb, -999);
//tried it here too, still didn't work
lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
testHeight = lb.DesiredSize.Height;
//add my newly-created label to a list which I access later to operate the marquee
taskElements.AddLast(lb);
//still didn't work
lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
testHeight = lb.DesiredSize.Height;
//tried a mass-measure to see if that would work - it didn't
foreach (UIElement element in taskElements)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
Chaque fois que l'un de ceux qui est appelé la valeur est renvoyée sous forme de 0 ou n'est pas un nombre, mais lorsque l'étiquette est affichée clairement a une taille telle qu'elle est visible. J'ai essayé de lancer ce code à partir d'appuyer sur un bouton lorsque l'étiquette est visible à l'écran, mais qui produit les mêmes résultats.
Est-ce parce que le style que j'utilise? Databindings peut-être? Est ce que j'ai fait de mal évidentes ou faire du WPF Gremlins juste vraiment me détester?
Deuxième Mise À Jour:
Après recherche j'ai découvert que la Mesure (la) s'applique uniquement à l'original de la taille de l'élément. Si un modèle de contrôle modifie ce que le fait de l'ajout de contrôles alors la meilleure méthode serait probablement à la mesure de chacun, mais j'avoue que c'est plus qu'un peu désordonné.
Le compilateur doit disposer d'une méthode de mesure de la totalité du contenu d'un contrôle, comme il se doit l'utiliser pour placer des éléments, par exemple, la pile de panneaux. Il doit y avoir un moyen d'y accéder, mais pour l'instant je suis entièrement d'idées.
source d'informationauteur Nick Udell
Vous devez vous connecter pour publier un commentaire.
Le contrôle n'ont pas une taille jusqu'à ce que WPF fait une passe de mise en page. Je pense que cela arrive de façon asynchrone. Si vous ne le faites pas dans votre constructeur, vous pouvez brancher l'événement Chargé -- la mise en page sera passé d'ici là, et tous les contrôles ajoutés dans le constructeur aura été de taille.
Cependant, une autre façon est de demander le contrôle de calculer la taille qu'il veut être. Pour ce faire, vous appelez Mesureet de passer d'une taille suggérée. Dans ce cas, vous souhaitez passer d'une taille infinie (ce qui signifie le contrôle peut être aussi grand qu'il l'aime), puisque c'est ce que la Toile va faire dans la mise en page passer:
L'appel à la Mesure ne fait pas de changement du contrôle de la Largeur, RenderSize, ou ActualWidth. Il indique simplement l'Étiquette de calculer la taille qu'il veut être, et de mettre cette valeur dans ses DesiredSize (une propriété dont le seul but est de tenir le résultat de la dernière Mesure).
Je suis assez nouveau à WPF, mais ici, c'est ma compréhension.
Lorsque vous mettez à jour ou créer des contrôles par programmation, il y a un non-évident mécanisme qui est également de tir (pour un débutant comme moi en tout cas - même si je l'ai fait windows de traitement de messages d'avant, cela m'a pris...). Les mises à jour et création de contrôles de la file d'attente des messages associés sur l'INTERFACE utilisateur de répartition de la file d'attente, où le point important est que ces vont être traités à un certain moment dans l'avenir. Certaines propriétés dépendent de ces messages en cours de traitement par exemple ActualWidth. Les messages dans ce cas, provoquer le contrôle à rendre et ensuite les propriétés associées à un rendu de contrôle pour être mis à jour.
Il n'est pas évident lors de la création de contrôles par programmation qu'il y a de message asynchrone de traitement de passe et que vous avez à attendre pour que ces messages soient traitées avant que certaines propriétés ont été mises à jour par exemple ActualWidth.
Si vous attendez pour les messages à traiter avant d'accéder à ActualWidth, alors il aura été mis à jour à:
Mise à jour
En réponse à votre commentaire. Si vous souhaitez ajouter d'autres codes, vous pouvez organiser votre code comme suit. L'important est que le répartiteur d'appel assure que vous attendez jusqu'à ce que les contrôles ont été rendus avant votre code qui dépend d'eux étant rendu exécute:
Résolu!
La question était dans mon XAML. Au plus haut niveau de mon étiquette de modèle il y avait un parent de la toile qui n'avait pas de hauteur ou la largeur de champ. Depuis cela n'a pas eu à modifier sa taille à ses enfants, il a été constamment mis à 0,0. Par de les enlever et de remplacer le nœud racine d'une frontière, qui doit redimensionner à la taille de ses enfants, la hauteur et la largeur des champs est mis à jour et retournée à mon code sur Mesure() appelle.