绑定到 Item ItemsControl 的 ActualHeight

发布于 2024-10-18 11:04:01 字数 597 浏览 5 评论 0原文

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

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

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

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

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

I have two separate ItemsControls that appear side by side. The ItemsControls bind to the same ItemsSource, but they display the data differently.

Each item displayed on the left will most likely be smaller than the same item on the right. This causes a problem because the rows will not line up, so I need the item on the left to bind to the item on the right.

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

As you can see, Item 2 on the right is larger, so it throws off the alignment. So if I can bind left's Item 2 to right's Item 2's ActualHeight the problem would be solved. How can I do this in XAML?

Edit: To make things more complicated, the ItemsControl on the right needs to scroll right to left, but both ItemsControls need to scroll up and down together. Basically, the left one provides a header of sorts for the items on the right.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

世界和平 2024-10-25 11:04:01

跟进 Jobi Joy 的回答

您无法在 Xaml 中为 ReadOnly 依赖属性 ActualHeight 进行直接的 OneWayToSource 绑定,但有许多解决方法。 Kent Boogaart这个问题中的回答< /a> 是我的最爱。它的作用是使用一个附加行为来侦听任何 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);
    }
}

Following up on Jobi Joy's answer

You can't do a direct OneWayToSource Binding in Xaml for the ReadOnly Dependency Property ActualHeight but there are many workarounds. The answer by Kent Boogaart in this question is my favorite. What is does is that it uses an Attached Behavior that listens to the SizeChanged event of any FrameworkElement and updates two Attached Properties, Width and Height, accordingly.

With a TextBlock for example, ActualHeight can be used to push into a Height property of the ViewModel like

<TextBlock local:ActualSizeBehavior.ObserveActualSize="True"
           local:ActualSizeBehavior.ActualHeight="{Binding Path=Height,
                                                           Mode=OneWayToSource}"
           .../>

Synkronize two ScrollViewers
You can either use a DependencyPropertyDescriptor to listen for changes in the VerticalOffsetProperty property or subscribe to the ScrollChanged event and call ScrollToVerticalOffset. Example

Xaml

<ScrollViewer Name="scrollViewerLeft"
              ScrollChanged="scrollViewerLeft_ScrollChanged">
<ScrollViewer Name="scrollViewerRight"
              ScrollChanged="scrollViewerRight_ScrollChanged">

Code behind event handlers

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);
    }
}
烟酉 2024-10-25 11:04:01

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

另一种方法是,在 ViewModel 中添加 Height 属性(当然,自从将 View 依赖项添加到 VM 以来,这不是非常正确的设计)。 TwoWay 将高度绑定到 left-itemsControl ItemContainerStyle 的 ActualHeight。在右侧项目控件上,将该 Height 属性绑定到 ItemsContainerStyle {One Way} 的高度。所以两者会同步。

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

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

Since the ItemsSource is same on both, you can use a single ItemsControl and a whole row represented as two sections (Two columns of a Grid) inside that single DataTemplate, then heights will get aligned automatically. You can always style it to look like it is part of two different ItemsControl but technically one.

Another way to go with this is, adding a Height property in to the ViewModel (of course not very right design since adding View dependency to VM). TwoWay bind the height to the ActualHeight of the left-itemsControl ItemContainerStyle. And on the right-itemscontrol bind that Height property to the Height of ItemsContainerStyle {One Way}. So both will be in sync.

Another idea based on your update 'Need of the scroll on the right side' :
Use a single ListView and have two columns in it, and of those two GridViewColumn.CellTemplate have your two DataTemplates. This idea still needs column freezing on the first column. But that may be more tricky.

Anyway I would go with the first approach here.

几度春秋 2024-10-25 11:04:01

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

Take a look at my article: http://www.codeproject.com/KB/WPF/BindingHub.aspx

That's how you can bind to Read-Only Dependency Properties using BindingHub:

<bindings:BindingHub 
       Visibility="Hidden"
       Socket1="{Binding ActualWidth, ElementName=Item, Mode=OneWay}"
       Socket2="{Binding ItemWidth, Mode=OneWayToSource}"
       Connect="(1 in, 2 out)"/>
天煞孤星 2024-10-25 11:04:01

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

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

I had to solve this exact problem. I used the read only binding solution found here:
https://meleak.wordpress.com/2011/08/28/onewaytosource-binding-for-readonly-dependency-property/

Using this I was able to bind the read only ActualHeight of each ListViewItem in one ListView to a property in my item view model called ListViewItemHeight. Then, in the second ListView, I bound the height of each item to ListViewItemHeight.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文