Pourquoi mon ornement de ne pas rendre à nouveau lorsque l'élément il est appliqué à des changements?
Dans une UI je suis en train de construire, je veux orner un panneau à chaque fois que l'un des contrôles dans le panneau a le focus. J'ai donc gérer la IsKeyboardFocusWithinChanged
événement, et ajouter un décorateur de l'élément lorsqu'il obtient le focus et retirer le décorateur quand il perd le focus. Cela semble fonctionner OK.
Le problème, je vais avoir, c'est que l'ornement n'est pas d'obtenir un nouveau rendu si les limites du orné élément change. Par exemple, dans ce cas simple:
<WrapPanel Orientation="Horizontal"
IsKeyboardFocusChanged="Panel_IsKeyboardFocusChanged">
<Label>Caption</Label>
<TextBox>Data</TextBox>
</WrapPanel>
le décorateur correctement décore les limites de la WrapPanel
lorsque le TextBox
reçoit le focus, mais comme je tape dans le texte, les TextBox
étend au dessous du bord de l'ornement. Bien sûr, dès que je fais quelque chose que les forces de l'ornement à rendre, comme ALT-TAB de l'application ou de donner un autre panneau de l'accent, il corrige de lui-même. Mais comment puis-je l'obtenir à nouveau le rendu lorsque les limites de la parée élément de changement?
OriginalL'auteur Robert Rossney | 2010-03-15
Vous devez vous connecter pour publier un commentaire.
WPF est doté d'un mécanisme à cause de tous les
Adorners
être réévaluée, réarrangés, et réaffichées chaque fois que le correspondantAdornedElement
des changements de taille, la position ou la transformer. Ce mécanisme nécessite de respecter certaines règles lors du codage de votre ornement, qui ne sont pas toutes documentées aussi clairement qu'ils devraient l'être.Je vais d'abord répondre à votre question titre de pourquoi votre ornement de ne pas consistenty rendre à nouveau, puis expliquer la meilleure façon de le fixer.
Pourquoi l'ornement de ne pas re-rendre
Chaque fois qu'un AdornerLayer reçoit un LayoutChanged notification de l'analyse de chacun de ses Ornements pour voir si le
AdornedElement
a changé dans la taille, la position ou de transformer. Si oui, il définit les drapeaux à la force de laAdorner
de mesurer, d'organiser et de rendre à nouveau -- à peu près équivalent àInvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
.Ce qui se passe normalement dans cette situation est que le contrôle est d'abord mesuré, puis arrangé, puis rendu. En fait, WPF essaie de faire de ce le cas le plus courant parce que c'est la plus efficace de la séquence. Cependant, il existe de nombreuses situations où un contrôle peut être modifié et/ou réaffichées avant il est réévaluée. C'est une légitime ordre des événements dans WPF (pour permettre la flexibilité des techniques de mise en page), mais il n'est pas commun, il n'est souvent pas testé.
Correctement mis en œuvre
Adorner
ou d'autresUIElement
sera prudent de faire appelInvalidateVisual()
tout moment, le rendu peut être affectée à moins qu'AffectsRender
les propriétés de dépendance ont été modifiés.Dans votre cas, votre ornement de la taille de l'évidence une incidence sur le rendu. Les propriétés de taille ne sont pas
AffectsRender
les propriétés de dépendance, il est donc nécessaire d'appel à la mainInvalidateVisual()
lorsqu'elles changent. Si vous ne le faites pas, WPF ne peut jamais savoir à nouveau le rendu de votre décorateur.Ce qui se passe dans votre situation est probablement ce:
LayoutChanged
événement se déclencheAdornerLayer
découvre le changement de taille sur votreAdornedElement
AdornerLayer
horaires de votre ornement de re-mesurer, re-mise en page, et de re-rendreArrange()
à être appelé, ce qui provoque le re-mise en page et la rendre à nouveau de se produire avant la re-mesure. Cela provoque WPF à penser l'ornement n'est plus besoin d'une re-mise en page ou de re-rendre.Measure
MeasureOverride
recalcule la taille souhaitée mais n'a rien à dire WPF l'ornement des besoins de re-rendreCe que vous pouvez faire pour le réparer
La solution est, bien sûr, pour corriger le bug dans le
Adorner
en appelantInvalidateVisual()
chaque fois que le contrôle est à nouveau mesuré, comme ceci:Faire cela à cause de votre Ornement de toujours obéir à toutes les règles de WPF, donc il fonctionnera comme prévu dans toutes les situations. C'est aussi la solution la plus efficace, car
InvalidateVisual()
ne fait rien du tout, sauf dans les cas où elle est vraiment nécessaire.Vous n'avez probablement jamais couru à travers elle avant parce que, dans le cas le plus fréquent - où RenderSize est mis à jour uniquement dans ArrangeCore et rendre ne dépend pas de toute autre taille - nouveau rendu est systématiquement déclenché sans avoir besoin de l'InvalidateVisual (). Journal d'une séquence de ArrangeOverride et OnRender appels pour obtenir une meilleure idée de comment cela fonctionne. Ma conjecture est que les concepteurs ne veulent pas programmer automatiquement un OnRender chaque fois RenderSize est défini parce qu'ils avaient imaginé les scénarios où RenderSize serait modifié et transformé immédiatement en arrière.
Une autre raison pour ne pas le rendu peut être le
UIElement
que vous devez passer à laAdorner
constructeur de base n'est pas dans l'ornement de la couche. par exemple, Si vous utilisezAdornerDecorator
pour créer un local d'ornement couche et puis vous avez tort de référence d'unUIElement
qui est à l'extérieur que visual/arborescence logique.Le contrôle je suis parure est dans une grille et la Grille.Row="1". Quand j'ai mis la AdornerDecorator autour d'elle, je pensais que je devrais déplacer la Grille.Row="1" jusqu'à la AdornerDecorator. J'ai ensuite trouvé mon ornement n'était pas mise à jour et la suggestion ci-dessus ne fonctionne pas. Une fois que j'ai déplacé la Grille.Row="1" vers le bas à mon contrôle, il a bien fonctionné, et au moins dans mon cas, sans la nécessité pour la suggestion ci-dessus. Espérons que cela aide quelqu'un.
OriginalL'auteur Ray Burns
Vous avez besoin pour appeler l'expéditeur sur le panneau. Ajouter un gestionnaire de la zone de texte SizeChanged événement:
Cela vient essentiellement de ce post: http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx
OriginalL'auteur Scott J