【问题标题】:How can I line up WPF items in a Horizontal WrapPanel so they line up based on an arbitrary vertical point in each item?如何在 Horizo​​ntal WrapPanel 中排列 WPF 项目,以便它们根据每个项目中的任意垂直点排列?
【发布时间】:2010-05-22 15:15:19
【问题描述】:

我正在尝试在 WPF 中创建一个视图,但很难弄清楚如何设置它。这是我正在尝试构建的内容:

  • 我的 ViewModel 公开了一个名为 Items 的 IEnumerable 属性
  • 每一项都是时间线上的一个事件,每一项都实现了 ITimelineItem
  • 每个项目的 ViewModel 都有自己的 DataTemplate 来显示它
  • 我想显示由一条线连接的时间线上的所有项目。我认为 ListView 中的 WrapPanel 可以很好地解决这个问题。
  • 但是,每个项目的高度会根据其显示的信息而有所不同。有些项目的线条上会有图形对象(如圆形或菱形,或其他),有些项目在线条上方或下方有注释。

所以这对我来说似乎很复杂。似乎时间线上的每个项目都必须渲染自己的线段。没关系。但是项目顶部到行(以及项目底部到行)之间的距离可能会有所不同。如果一个项目的线距顶部向下 50 px,而下一个项目的线距顶部向下 100 px,则第一个项目需要 50 px 的填充,以便线段相加。

我想我可以解决这个问题,但是,如果这两个项目在 WrapPanel 的同一行上,我们只需要添加填充!假设有 5 个项目,屏幕上只有 3 个空间... WrapPanel 将把另外两个放在下一行。没关系,但这意味着只有前 3 个需要一起填充,最后 2 个需要一起填充。

这让我很头疼。我可以看看其他方法吗?

编辑:我倾向于用继承自 Canvas 并覆盖 MeasureOverride 和 ArrangeOverride 方法的自定义面板替换 WrapPanel。

【问题讨论】:

    标签: wpf listview mvvm wrappanel


    【解决方案1】:

    对此的理想解决方案可能是使用 Adorners。在 AdornerLayer 上,您将为面板上的每个项目提供一个自定义 Adorner(您在 DataTemplate 中定义它)。您将能够检索每个项目的尺寸和位置(装饰器的边界框)。同样在 AdornerLayer 上,您将在这些边界框之间绘制线条。 使用此解决方案,您可以按照您想要的任何方式(使用您想要的任何面板)布局您的项目,并且这些项目仍将与线连接。

    很久以前,我将这种方法用于图形可视化器。可以以任何方式布局的任意 UIElements 的节点。用线连接的项目。

    要使所有项目完美对齐,您可以在 ItemTemplate 中使用 Canvas 作为根面板。画布可以是 0x0 单位大。然后,您将围绕该 Canvas 排列您的元素。画布成为您的参考点。行顶部的所有内容都将获得负 Canvas.Top 值。 另一种方法是使用负边距,该负边距绑定到围绕该线的顶部和底部控件的高度。使用 IValueConverter (Height*-1) 反转高度。

    【讨论】:

    • 这绝对是绘制线条的有用解决方案。谢谢!但我仍在寻找一种方法来安排这些项目,以便它们都排列在应该的位置。线条都是水平且笔直的,因此必须移动物品来补偿。
    • 装饰器还解决了我的拖放视觉指示器!谢谢你教我一些新东西!
    • 我更新了我的帖子,提出了解决您的 alginment 问题的建议。
    【解决方案2】:

    这不会解决你的问题,但我敢打赌它会简化它:

    尝试定义一个看起来像这样的DataTemplate

    <Grid>
       <Grid.RowDefinitions>
          <RowDefinition Height="Auto" SharedSizeGroup="Top"/>
          <RowDefinition Height="10"/>
          <RowDefinition Height="Auto" SharedSizeGroup="Bottom"/>
       </Grid.RowDefinitions>
       <ContentControl Grid.Row="0" Content="{Binding TopAnnotations}/>
       <Rectangle Fill="Black" Grid.Row="1" Margin="0,4,0,4"/>
       <ContentControl Grid.Row="1" Height="10" Content="{Binding GraphicObject}"/>
       <ContentControl Grid.Row="2" Content="{Binding BottomAnnotations}"/>
    </Grid>  
    

    现在创建一个ItemsControl,其ItemsPanelTemplate 是一个水平WrapPanel,并将Grid.IsSharedSizeScope 附加属性设置为True。当然,您还需要为注释和图形对象属性包含的任何内容定义数据模板。

    您仍然会遇到这样的问题,即当 WrapPanel 中的项目换行时,时间轴上方和下方的对象具有相同的填充,而不管它们实际需要多少适合它们当前的 WrapPanel 行。因此,要获得我认为您正在寻找的效果,您仍然必须使用度量和安排来胡闹 - 基本上,您将创建将这些项目放入 StackPanel 的东西,而不是创建单个 WrapPanel在每个“行”上,每个“行”都是其自己的共享大小范围。但实际上测量和安排时间线项目的高度并不是您需要担心的事情。您只需要它们的宽度(以便您的逻辑可以确定何时换行)。 Grid 将负责为您计算高度。

    【讨论】:

    • 有趣的想法...谢谢!它有点限制,因为所有图形必须具有相同的大小(在中间),所以如果一个图形使用两倍大的图形,同一行中其他图形上的所有注释都不会正确反对图形。例如,如果一个项目只有一个大图形,没有注释怎么办?正如我所说,它比我想象的要严格一些,但这是一种有趣的方法。
    • 实际上,您可以让中间列自动调整大小并让它属于自己的SharedSizeGroup。它仍然可以工作;您只需提供Rectangle 或任何您用来生成VerticalAlignmentCenter 的行。它将在行中垂直居中,并且行的大小将调整为其包含的最大图形的高度。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    相关资源
    最近更新 更多