【问题标题】:Bind to ActualHeight of Item ItemsControl绑定到 Item ItemsControl 的 ActualHeight
【发布时间】:2011-06-30 23:31:57
【问题描述】:

我有两个单独的ItemsControls 并排出现。 ItemsControls 绑定到同一个 ItemsSource,但它们显示数据的方式不同。

左侧显示的每个项目很可能比右侧的相同项目小。这会导致问题,因为行不会对齐,所以我需要将左侧的项目绑定到右侧的项目。

ItemsControl        ItemsControl
|Item 1         |Item 1
|Item 2         |Item 2
|Item 3         |
|Item 4         |Item 3

如您所见,右侧的第 2 项较大,因此它偏离了对齐方式。因此,如果我可以将左侧的第 2 项绑定到右侧的第 2 项ActualHeight,问题将得到解决。如何在 XAML 中执行此操作?

编辑:为了让事情变得更复杂,右边的ItemsControl 需要从右向左滚动,但ItemsControls 需要一起上下滚动。基本上,左边为右边的项目提供了一个排序的标题。

【问题讨论】:

    标签: wpf xaml binding itemscontrol actualheight


    【解决方案1】:

    由于两者的ItemsSource 相同,您可以使用单个ItemsControl 和在单个DataTemplate 内表示为两个部分(网格的两列)的整行,然后高度将自动对齐。您始终可以将其设置为看起来像是两个不同 ItemsControl 的一部分,但在技术上是一个。

    另一种方法是在 ViewModel 中添加一个 Height 属性(当然不是很正确的设计,因为将 View 依赖添加到 VM)。 TwoWay 将高度绑定到 left-itemsControl ItemContainerStyle 的 ActualHeight。然后在右边的itemscontrol 上将该Height 属性绑定到ItemsContainerStyle {One Way} 的高度。所以两者都会同步。

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

    无论如何,我会在这里采用第一种方法。

    【讨论】:

    • 不幸的是,由于我的滚动场景,两列网格无法工作(请参阅我更新的问题)。 ViewModel 中的实际高度可能有效,但我不确定这是否应该是 ViewModel 中的内容,因为它是特定于 View 的。
    • 很遗憾,我无法绑定到 ActualHeight,因此无法完成选项一或二。我会想办法的。
    【解决方案2】:

    跟进Jobi Joy 的回答

    您不能在 Xaml 中为 ReadOnly 依赖属性 ActualHeight 执行直接的 OneWayToSource 绑定,但有许多解决方法。 Kent Boogaartthis question 中的回答是我最喜欢的。它的作用是使用一个附加行为来侦听任何FrameworkElementSizeChanged 事件并相应地更新两个附加属性,宽度和高度。

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

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

    【讨论】:

    • 这对我有用。但是,我对在视图模型中存储视图特定数据是否支持 MVVM 持保留意见。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我必须解决这个确切的问题。我使用了此处找到的只读绑定解决方案: https://meleak.wordpress.com/2011/08/28/onewaytosource-binding-for-readonly-dependency-property/

      使用它,我能够将一个ListView 中的每个ListViewItem 的只读ActualHeight 绑定到我的项目视图模型中名为ListViewItemHeight 的属性。然后,在第二个ListView中,我将每个项目的高度绑定到ListViewItemHeight

      【讨论】:

        猜你喜欢
        • 2015-08-09
        • 2011-02-20
        • 1970-01-01
        • 2011-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-24
        相关资源
        最近更新 更多