Привязать к ActualHeight Item ItemsControl
У меня два отдельных ItemsControl
с, которые появляются рядом. ItemsControl
привязать к тому же ItemsSource
, но они отображают данные по-разному.
Каждый элемент, отображаемый слева, скорее всего будет меньше, чем тот же элемент справа. Это вызывает проблему, потому что строки не будут выстроены в линию, поэтому мне нужен элемент слева, чтобы связать его с элементом справа.
ItemsControl ItemsControl
|Item 1 |Item 1
|Item 2 |Item 2
|Item 3 |
|Item 4 |Item 3
Как вы можете видеть, пункт 2 справа больше, поэтому он нарушает выравнивание. Так что, если я смогу связать левый пункт 2 с правым пунктом 2 ActualHeight
проблема будет решена. Как я могу сделать это в XAML?
Изменить: чтобы сделать вещи более сложными, ItemsControl
справа нужно прокрутить справа налево, но оба ItemsControls
нужно прокручивать вверх и вниз вместе. По сути, левый обеспечивает своего рода заголовок для элементов справа.
3 ответа
Продолжение ответа Джоби Джой
Вы не можете сделать прямой OneWayToSource
Привязка в Xaml для свойства зависимостей ReadOnly ActualHeight, но есть много обходных путей. Ответ Кента Бугаарта в этом вопросе мой любимый. Что делает, так это то, что он использует присоединенное поведение, которое слушает SizeChanged
событие любого FrameworkElement
и обновляет два Прикрепленных свойства, Ширина и Высота, соответственно.
С TextBlock
например, ActualHeight
может быть использован для вставки в свойство высоты ViewModel, как
<TextBlock local:ActualSizeBehavior.ObserveActualSize="True"
local:ActualSizeBehavior.ActualHeight="{Binding Path=Height,
Mode=OneWayToSource}"
.../>
Синхронизировать два ScrollViewers
Вы можете использовать DependencyPropertyDescriptor
прислушиваться к изменениям в VerticalOffsetProperty
собственности или подписаться на ScrollChanged
событие и звонок ScrollToVerticalOffset
, пример
Xaml
<ScrollViewer Name="scrollViewerLeft"
ScrollChanged="scrollViewerLeft_ScrollChanged">
<ScrollViewer Name="scrollViewerRight"
ScrollChanged="scrollViewerRight_ScrollChanged">
Код за обработчиками событий
private void scrollViewerLeft_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
scrollViewerRight.ScrollToVerticalOffset(scrollViewerLeft.VerticalOffset);
}
private void scrollViewerRight_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
scrollViewerLeft.ScrollToVerticalOffset(scrollViewerRight.VerticalOffset);
}
ActualSizeBehavior
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);
}
}
Посмотрите на мою статью: http://www.codeproject.com/KB/WPF/BindingHub.aspx
Вот как вы можете связать со свойствами зависимости только для чтения, используя BindingHub:
<bindings:BindingHub
Visibility="Hidden"
Socket1="{Binding ActualWidth, ElementName=Item, Mode=OneWay}"
Socket2="{Binding ItemWidth, Mode=OneWayToSource}"
Connect="(1 in, 2 out)"/>
Я должен был решить именно эту проблему. Я использовал найденное здесь решение привязки только для чтения:https://meleak.wordpress.com/2011/08/28/onewaytosource-binding-for-readonly-dependency-property/
Используя это, я смог привязать только чтение
ActualHeight
каждого
ListViewItem
в одном
ListView
к свойству в моей модели представления элементов, называемой
ListViewItemHeight
. Затем во втором
ListView
, Я привязал высоту каждого элемента к
ListViewItemHeight
.
Так как ItemsSource
то же самое на обоих, вы можете использовать один ItemsControl
и целый ряд, представленный в виде двух разделов (два столбца сетки) внутри этого единого DataTemplate
, тогда высоты будут выровнены автоматически. Вы всегда можете придать ему стиль, чтобы он выглядел как часть двух разных ItemsControl
но технически один.
Другой способ это добавить, добавив свойство Height в ViewModel (конечно, не очень правильный дизайн, так как добавление зависимости View в VM). TwoWay привязывает высоту к ActualHeight элемента left-itemsControl ItemContainerStyle. А в элементе управления right-items свяжите это свойство Height с высотой ItemsContainerStyle {One Way}. Таким образом, оба будут синхронизированы.
Еще одна идея, основанная на вашем обновлении "Необходимость прокрутки с правой стороны": используйте один ListView и имейте в нем два столбца, и из этих двух GridViewColumn.CellTemplate есть ваши два DataTemplates. Эта идея все еще нуждается в замораживании столбцов в первом столбце. Но это может быть более сложным.
Во всяком случае, я бы пошел с первым подходом здесь.