【问题标题】:Positioning Items in ItemsControl在 ItemsControl 中定位项目
【发布时间】:2019-04-05 15:58:02
【问题描述】:

我有一个ItemsControl 绑定到这样的数据:

<ItemsControl Name="MainPanel" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
           <TextBlock Text="{Binding Definition}" TextWrapping="Wrap"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

在我的数据模型中,每个项目都有一个y 坐标,用于将项目垂直放置在屏幕上,并且每个项目的高度都是可变的(因为TextWrapping)。

我需要将每个项目定位在y 坐标上,除非它与前一个项目重叠,在这种情况下它被放置在前一个项目的下方。

我想使用Margin 属性来做到这一点,但实际上并不是那么简单......

关于如何进行的任何想法?

【问题讨论】:

  • 我认为您需要为此编写自己的包装面板。
  • 我的 WPF 生锈了,但我想说:用画布 (&lt;ItemsPanelTemplate&gt;&lt;Canvas /&gt;&lt;/ItemsPanelTemplate&gt;) 替换 ItemsControl 面板,然后将您的项目绑定到 Canvas.Top 依赖属性 (&lt;TextBlock Text="..." Canvas.Top="{Binding y}" ...)
  • 哦,我错过了“不要重叠”的要求。那我的解决方案就不行了
  • 我会尝试使用自定义 StackPanel 和附加属性,例如 MyCustomStackPanel.MinY

标签: c# wpf itemscontrol


【解决方案1】:

我认为更改TextBlock.Margin.Top 是一种简单的方法。

    <ItemsControl ItemsSource="your model collection"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <!-- Need ItemsControl for calculation. -->
                <TextBlock Text="{Binding Text}" TextWrapping="Wrap"
                           Tag="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}}">
                    <TextBlock.Margin>
                        <MultiBinding Converter="{StaticResource calcOffsetY}">
                            <Binding RelativeSource="{RelativeSource Self}"/>
                            <Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
                        </MultiBinding>
                    </TextBlock.Margin>
                </TextBlock>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

还有转换器。

public class CalcOffsetY : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var tbk = (TextBlock)values[0];
        var offsetY = tbk.TranslatePoint(tbk.RenderTransformOrigin, (UIElement)tbk.Tag).Y -
            tbk.Margin.Top;
        var y = ((YourModelType)tbk.DataContext).Y;

        tbk.SetCurrentValue(TextBlock.MarginProperty, new Thickness(tbk.Margin.Left, y > offsetY ? y - offsetY : 0,
            tbk.Margin.Right, tbk.Margin.Bottom));
        tbk.UpdateLayout(); // Update layout immediately, so next item will get correct result.

        return Binding.DoNothing; // Already nothing to do.
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

【讨论】:

  • 这看起来很有希望 - 我会让你知道我的进展情况......谢谢你的帮助。我不明白你为什么要添加:&lt;Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType=ItemsControl}"/&gt;...这是为什么呢?
  • 另外,当Y 更改时,这会自动移动我的项目吗?
  • @LeonardoSeccia ActualWidth 绑定将在每次ItemsControl 的宽度发生变化时重新计算,包括在初始化时的第一次。如果您希望此响应更改Y,则需要在MultiBinding 中再添加一个Binding
  • 我已经尝试过了,但它似乎不太正确(项目 重叠 并且定位不太正确,即当 Y = 0 项目不在顶部时) - screencast.com/t/IwIdLxobO
  • @LeonardoSeccia 由于您没有提及项目顺序,我只是假设它们按 Y 排序。实际上,我的回答是利用 WPF 的布局计算来解决重叠问题。所以我建议您使用CollectionViewSource 代替集合作为绑定源,并在CollectionViewSource 中添加SortDescription,使视图自动按Y 排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多