Lier à la hauteur réelle de l'élément ItemsControl
-
15-11-2019 - |
Question
J'en ai deux séparés ItemsControl
s qui apparaissent côte à côte.Le ItemsControl
s se lier au même ItemsSource
, mais ils affichent les données différemment.
Chaque élément affiché à gauche sera probablement plus petit que le même élément à droite.Cela pose un problème car les lignes ne s'alignent pas, j'ai donc besoin que l'élément de gauche soit lié à l'élément de droite.
ItemsControl ItemsControl
|Item 1 |Item 1
|Item 2 |Item 2
|Item 3 |
|Item 4 |Item 3
Comme vous pouvez le voir, l'élément 2 à droite est plus grand, ce qui perturbe l'alignement.Donc, si je peux lier l'élément 2 de gauche à l'élément 2 de droite ActualHeight
le problème serait résolu.Comment puis-je faire cela en XAML ?
Modifier: Pour rendre les choses plus compliquées, le ItemsControl
à droite doit faire défiler de droite à gauche, mais les deux ItemsControls
besoin de faire défiler vers le haut et vers le bas ensemble.Fondamentalement, celui de gauche fournit une sorte d’en-tête pour les éléments de droite.
La solution
Suivi de Jobi Joy's répondre
Vous ne pouvez pas faire de direct OneWayToSource
Liaison dans Xaml pour la propriété de dépendance ReadOnly ActualHeight, mais il existe de nombreuses solutions de contournement.La réponse par Kent Boogaart dans cette question est mon préféré.Ce qu'il fait, c'est qu'il utilise un comportement attaché qui écoute le SizeChanged
événement de tout FrameworkElement
et met à jour deux propriétés attachées, Largeur et Hauteur, en conséquence.
Avec un TextBlock
Par exemple, ActualHeight
peut être utilisé pour insérer une propriété Height du ViewModel comme
<TextBlock local:ActualSizeBehavior.ObserveActualSize="True"
local:ActualSizeBehavior.ActualHeight="{Binding Path=Height,
Mode=OneWayToSource}"
.../>
Synchronisez deux ScrollViewers
Vous pouvez soit utiliser un DependencyPropertyDescriptor
être à l'écoute des changements dans VerticalOffsetProperty
propriété ou souscrire au ScrollChanged
événement et appel ScrollToVerticalOffset
.Exemple
XML
<ScrollViewer Name="scrollViewerLeft"
ScrollChanged="scrollViewerLeft_ScrollChanged">
<ScrollViewer Name="scrollViewerRight"
ScrollChanged="scrollViewerRight_ScrollChanged">
Code derrière les gestionnaires d'événements
private void scrollViewerLeft_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
scrollViewerRight.ScrollToVerticalOffset(scrollViewerLeft.VerticalOffset);
}
private void scrollViewerRight_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
scrollViewerLeft.ScrollToVerticalOffset(scrollViewerRight.VerticalOffset);
}
ComportementTailleActuelle
public static class ActualSizeBehavior
{
public static readonly DependencyProperty ActualSizeProperty =
DependencyProperty.RegisterAttached("ActualSize",
typeof(bool),
typeof(ActualSizeBehavior),
new UIPropertyMetadata(false, OnActualSizeChanged));
public static bool GetActualSize(DependencyObject obj)
{
return (bool)obj.GetValue(ActualSizeProperty);
}
public static void SetActualSize(DependencyObject obj, bool value)
{
obj.SetValue(ActualSizeProperty, value);
}
private static void OnActualSizeChanged(DependencyObject dpo,
DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = dpo as FrameworkElement;
if ((bool)e.NewValue == true)
{
element.SizeChanged += element_SizeChanged;
}
else
{
element.SizeChanged -= element_SizeChanged;
}
}
static void element_SizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
SetActualWidth(element, element.ActualWidth);
SetActualHeight(element, element.ActualHeight);
}
private static readonly DependencyProperty ActualWidthProperty =
DependencyProperty.RegisterAttached("ActualWidth", typeof(double), typeof(ActualSizeBehavior));
public static void SetActualWidth(DependencyObject element, double value)
{
element.SetValue(ActualWidthProperty, value);
}
public static double GetActualWidth(DependencyObject element)
{
return (double)element.GetValue(ActualWidthProperty);
}
private static readonly DependencyProperty ActualHeightProperty =
DependencyProperty.RegisterAttached("ActualHeight", typeof(double), typeof(ActualSizeBehavior));
public static void SetActualHeight(DependencyObject element, double value)
{
element.SetValue(ActualHeightProperty, value);
}
public static double GetActualHeight(DependencyObject element)
{
return (double)element.GetValue(ActualHeightProperty);
}
}
Autres conseils
Depuis le ItemsSource
est le même sur les deux, vous pouvez en utiliser un seul ItemsControl
et une ligne entière représentée comme deux sections (deux colonnes d'une grille) à l'intérieur de cette seule DataTemplate
, les hauteurs seront alors alignées automatiquement.Vous pouvez toujours lui donner l'impression qu'il fait partie de deux ItemsControl
mais techniquement un.
Une autre façon de procéder consiste à ajouter une propriété Height au ViewModel (bien sûr, la conception n'est pas très correcte depuis l'ajout de la dépendance View à la VM).TwoWay lie la hauteur à la ActualHeight du left-itemsControl ItemContainerStyle.Et sur le contrôle d'éléments de droite, liez cette propriété Height à la hauteur de ItemsContainerStyle {One Way}.Les deux seront donc synchronisés.
Une autre idée basée sur votre mise à jour 'Besoin du scroll sur le côté droit' :Utilisez un seul ListView et contient deux colonnes, et parmi ces deux GridViewColumn.CellTemplate avez vos deux DataTemplates.Cette idée nécessite encore un gel de colonne sur la première colonne.Mais cela risque d'être plus délicat.
Quoi qu'il en soit, j'opterais pour la première approche ici.
Jetez un oeil à mon article: http://www.codeproject.com/KB/WPF/BindingHub.aspx
Voici comment vous pouvez vous lier aux propriétés de dépendance en lecture seule à l'aide de BindingHub :
<bindings:BindingHub
Visibility="Hidden"
Socket1="{Binding ActualWidth, ElementName=Item, Mode=OneWay}"
Socket2="{Binding ItemWidth, Mode=OneWayToSource}"
Connect="(1 in, 2 out)"/>