2010-05-22 11 views
0

J'essaie de créer une vue dans WPF et j'ai du mal à comprendre comment le configurer. Voici ce que je suis en train de construire:Comment puis-je aligner les éléments WPF dans un Horizontal WrapPanel afin qu'ils s'alignent en fonction d'un point vertical arbitraire dans chaque élément?

  • Mon ViewModel expose une propriété IEnumerable appelée Articles
  • Chaque élément est un événement sur une ligne de temps, et chacun met en œuvre ITimelineItem
  • Le ViewModel pour chaque élément a sa propre DataTemplate à pour l'afficher
  • Je veux afficher tous les éléments sur la timeline connectés par une ligne. Je pense qu'un WrapPanel à l'intérieur d'un ListView fonctionnerait bien pour cela.
  • Cependant, la hauteur de chaque élément varie en fonction de l'information qu'il affiche. Certains objets auront des objets graphiques directement sur la ligne (comme un cercle ou un diamant, ou autre), et certains ont des annotations au-dessus ou en dessous de la ligne.

Donc, il me semble compliqué. Il semble que chaque élément de la chronologie doit rendre son propre segment de la ligne. C'est très bien. Mais la distance entre le haut de l'article à la ligne (et le bas de l'article à la ligne) pourrait varier. Si un élément a la ligne 50 px en haut et l'élément suivant a la ligne 100 px en haut, alors le premier élément a besoin de 50 px de remplissage pour que les segments de ligne s'additionnent.

Je pense que je pourrais résoudre ce problème, cependant, nous avons seulement besoin d'ajouter un remplissage si ces deux éléments sont sur la même ligne dans le WrapPanel! Disons qu'il y a 5 éléments et seulement de la place sur l'écran pour 3 à travers ... le WrapPanel mettra les deux autres sur la ligne suivante. Ce n'est pas grave, mais cela signifie que seulement les 3 premiers ont besoin de se tamponner ensemble, et les 2 derniers ont besoin de se mettre ensemble.

C'est ce qui me donne mal à la tête. Y a-t-il une autre approche que je pourrais envisager?

EDIT: Je suis penché pour remplacer le WrapPanel avec un panneau personnalisé qui hérite de Canvas et remplace les méthodes MeasureOverride et ArrangeOverride.

Répondre

2

La solution idéale serait probablement d'utiliser des Adorners. Sur AdornerLayer, vous avez un Adorner personnalisé pour chaque élément du Panel (vous le définissez dans le DataTemplate). Vous serez en mesure de récupérer les dimensions et les positions de chaque objet (Boundingbox de l'Adorner). Aussi sur AdornerLayer vous dessinez les lignes entre ces boundingbox. En utilisant cette solution, vous pouvez mettre en page vos articles comme vous le souhaitez (en utilisant n'importe quel panneau que vous voulez) et les articles seront toujours connectés avec des lignes.

Il y a longtemps, j'ai utilisé cette approche pour un visualiseur graphique. Les nœuds sont des UIElements arbitraires qui peuvent être mis en page de quelque façon que ce soit. Les éléments où connecté avec des lignes.

Pour que tous les éléments s'alignent parfaitement, vous pouvez utiliser un canevas comme panneau racine dans ItemTemplate. Le canevas peut être de grandes unités 0x0. Vous organisez ensuite vos éléments autour de cette toile. La toile devient votre point de référence. Tout en haut de la ligne aura des valeurs Canvas.Top négatives. Une autre approche consisterait à utiliser des marges négatives liées à la hauteur des commandes supérieure et inférieure qui entourent la ligne. Utilisez un IValueConverter (Height * -1) pour inverser la hauteur.

+0

C'est certainement une solution utile pour dessiner les lignes. Merci! Mais je suis toujours à la recherche d'un moyen d'organiser les éléments de sorte qu'ils s'alignent tous là où ils le devraient. Les lignes sont toutes horizontales et droites, donc ce sont les objets qui doivent se déplacer pour compenser. –

+0

Les ornements résolvent également mes indicateurs visuels drag & drop! Merci de m'apprendre quelque chose de nouveau! –

+0

J'ai mis à jour mon post avec une suggestion pour résoudre votre problème d'alginment. – bitbonk

1

Cela ne résoudra pas votre problème, mais je parie qu'il va simplifier:

Essayez de définir un DataTemplate qui ressemble à ceci:

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto" SharedSizeGroup="Top"/> 
     <RowDefinition Height="10"/> 
     <RowDefinition Height="Auto" SharedSizeGroup="Bottom"/> 
    </Grid.RowDefinitions> 
    <ContentControl Grid.Row="0" Content="{Binding TopAnnotations}/> 
    <Rectangle Fill="Black" Grid.Row="1" Margin="0,4,0,4"/> 
    <ContentControl Grid.Row="1" Height="10" Content="{Binding GraphicObject}"/> 
    <ContentControl Grid.Row="2" Content="{Binding BottomAnnotations}"/> 
</Grid> 

maintenant créer un ItemsControl dont ItemsPanelTemplate est horizontale WrapPanel avec la propriété jointe Grid.IsSharedSizeScope définie sur True. Bien entendu, vous devrez également définir des modèles de données pour toutes les propriétés d'annotation et d'objet graphique.

Vous avez toujours le problème que lorsque les éléments dans l'emballage WrapPanel, les objets au-dessus et au-dessous de la ligne de temps ont le même remplissage indépendamment de combien ils ont réellement besoin de s'adapter sur leur ligne WrapPanel actuelle. Donc, pour obtenir l'effet que je pense que vous cherchez, vous devez toujours singe autour avec mesure et arranger - fondamentalement, au lieu de créer un seul WrapPanel, vous allez créer quelque chose qui met ces éléments dans un StackPanel sur chaque "rangée ", dont chacun est propre portée de taille partagée. Mais mesurer et organiser les hauteurs des éléments de la chronologie n'est pas quelque chose dont vous avez besoin de s'inquiéter. Vous avez seulement besoin de leurs largeurs (de sorte que votre logique puisse déterminer quand emballer). Le Grid prendra soin de calculer les hauteurs pour vous.

+0

Idée intéressante ... merci! Cela limite un peu les choses car tous les graphiques doivent être de la même taille (au milieu), donc si l'on utilisait un graphisme deux fois plus grand, toutes les annotations sur les autres dans la même ligne ne seraient pas correctes. contre le graphique. Que se passe-t-il si, par exemple, un élément n'a qu'un gros graphique et aucune annotation? Comme je l'ai dit, c'est un peu plus restrictif que je ne le pensais, mais c'est une approche intéressante. –

+0

En fait, vous pouvez faire en sorte que la colonne du milieu soit automatiquement dimensionnée et qu'elle appartienne à son propre 'SharedSizeGroup'. Ça marchera toujours; il suffit de donner le 'Rectangle' ou tout ce que vous utilisez pour produire la ligne' VerticalAlignment' de 'Center'. Il sera centré verticalement dans la rangée, et la ligne sera dimensionnée à la hauteur du plus grand graphique qu'il contient. –