如何根据尺寸安排控件? (不从Panel继承)

时间:2009-11-11 10:52:13

标签: c# .net wpf

范围

我正在尝试让我的应用程序排列一些复杂控件的行并将这些行拆分成页面。像这样的东西: alt text http://apreleva.com/vm/controls-in-pages.png

就像文字处理器一样,只有复杂的控件而不是单词。请注意,分割成页面的要求是必不可少的。

另请注意,从Panel继承并实现MeasureOverride / ArrangeOverride的明显解决方案不会这样做,因为我非常希望将页面作为单独的框架元素 - 这样,我就可以做一些很好的事情了它们就像拖动它们一样,旋转等等。我正在考虑这个解决方案(继承自Panel),但我确信应该有更好的方法。

采用策略

这似乎相当简单,前提是我知道这些控件的大小。我对页面使用垂直方向的堆栈面板,对于线条使用水平方向的堆叠面板。我将控件一个接一个地打包到行中,直到它们超出行的宽度,然后为下一行创建stackpanel并继续。当下一行不适合当前页面时,我创建下一页,然后该过程继续。

我希望一次有几百个控件的顺序,所以我认为这种方法应该这样做。

问题

问题是我对WinForms的期望:我不知道这些控件的大小!事实证明,如果控件没有明确设置宽度和高度,则在创建后它将DesiredSize为零。

可以通过调用.Measure(Size.Empty)来解决此问题。之后,DesiredSize(有些)正确计算。

我说“有点” - 因为不幸的是,它仍然不完全正确。我发现的最新问题是绑定到相对源。请考虑以下XAML:

<ItemsControl x:Class="MyClass" xmlns=...>
    <ItemsControl.ItemTemplate>
        <Ellipse Width={DataBinding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyClass}}, Path=EllipseWidth} />
    </ItemsContro.ItemTemplate>
</ItemsControl> 

“EllipseWidth”是MyClass上的一个属性,它是我的自定义ItemsControl后代。

事实证明,对Measure()的调用会导致ItemsControl创建其项目,但绑定不会得到解决。查看跟踪:

52 : Created BindingExpression (hash=15030582) for Binding (hash=15006601)
54 :   Path: 'EllipseWidth'
56 : BindingExpression (hash=15030582): Default mode resolved to OneWay
57 : BindingExpression (hash=15030582): Default update trigger resolved to PropertyChanged
58 : BindingExpression (hash=15030582): Attach to Ellipse.Width (hash=12274123)
62 : BindingExpression (hash=15030582): RelativeSource (FindAncestor) requires tree context
61 : BindingExpression (hash=15030582): Resolve source deferred

具体来说,请注意最后两行。什么是“树上下文”,我该如何提供它?我试着在调用Measure()之前将控件添加到“实时”面板,但它没有多大帮助。

我必须说,根据跟踪,源最终会得到解决,但为时已晚:我现在需要知道控件的大小。

概括问题

即使我解决了这个绑定问题,我也无法确定是否会有其他问题。所以最终的问题是:我需要一个“干净”(如“支持”)的方式来知道控件的初始化何时完成,并找到该控件的大小

或者(另一种选择)也许我采取了错误的方法,还有另一种更清洁的方法来做我想做的事情?

1 个答案:

答案 0 :(得分:0)

我不认为你只能使用控制组合来完成你想要的东西。我想您肯定需要参与测量/排列并提供您自己的算法。您不必从Panel继承来执行此操作,但它通常最有意义。对于页面,您的自定义Panel可以为每个逻辑分组发出支持。最后,您的自定义面板可以配置构成逻辑分组的尺寸,以便您可以支持不同的尺寸(想象支持Letter与A4或类似的东西)。

所以最后我认为你应该最终得到的是:

  1. 自定义Panel实施,支持铺设并根据通过各种属性配置的维度确定逻辑分组。
  2. 公开名为ItemGroupTemplate之类的属性,当您生成逻辑分组时,您可以使用它作为逻辑分组的“背景”进行渲染,从而有效地创建它们所在的“页面”。
  3. 将您的实际商品数据绑定到ItemsControl
  4. 将自定义Panel设置为ItemControl::ItemsPanel
  5. 最后,请记住,如果要布置那么多项目,您可能会想要在面板中使用虚拟化算法。这意味着您应该从VirtualizingPanel继承。有关创建自定义VirtualizingPanel的更多信息,请check out Dan Crevier's awesome series on the subject