将ItemsControl的绑定属性设置为Item的ActualHeight

8

我有两个独立的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需要一起上下滚动。 基本上,左侧提供了右侧项目的标题。
4个回答

6

跟进Jobi Joy的回答,你不能直接在Xaml中使用OneWayToSource绑定只读依赖属性ActualHeight,但有许多解决方法。我最喜欢的是Kent Boogaart这个问题中提供的答案。它使用一个附加行为来监听任何FrameworkElementSizeChanged事件,并相应地更新两个附加属性,即Width和Height。

例如,对于TextBlock,可以使用ActualHeight将其推入到ViewModel的Height属性中。

<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);
}

实际大小行为

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);
    }
}

那对我来说起作用了。然而,我对于在视图模型中存储特定于视图的数据是否符合MVVM架构持有保留意见。 - kevindaub

0

请看我的文章: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)"/>

0

由于ItemsSource在两个控件中是相同的,因此您可以使用单个ItemsControl,并将整个行表示为该单个DataTemplate内的两个部分(网格的两列),然后高度将自动对齐。您始终可以将其样式设置为看起来像是两个不同的ItemsControl的一部分,但从技术上讲只有一个。

另一种方法是,在ViewModel中添加一个Height属性(当然不是很正确的设计,因为将View依赖项添加到VM中)。将高度双向绑定到左侧itemsControl ItemContainerStyle的ActualHeight上。在右侧itemscontrol上将该Height属性绑定到ItemsContainerStyle {One Way}的高度上。这样两者就会同步。

基于您的更新“需要在右侧滚动”,另一个想法是:使用单个ListView,并在其中有两列,其中两个GridViewColumn.CellTemplate具有您的两个DataTemplates。这个想法仍然需要在第一列上冻结列。但这可能更棘手。

无论如何,我会选择第一种方法。


很遗憾,由于我有滚动场景(请参见我的更新问题),所以双列网格不起作用。ViewModel中的实际高度可能有效,但我不确定是否应该将其放入ViewModel中,因为它是视图特定的。 - kevindaub
不幸的是,我无法将绑定执行到ActualHeight,因此我无法完成选项一或选项二。我会想出办法的。 - kevindaub

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接