0

例如,实现一个TwoColumnStackPanel. 顾名思义,一般StackPanel只能将元素堆叠在一列,而我的 TwoColumnStackPanel 可以将元素堆叠在两列。

TwoColumnStackPanel 应在两列中均匀分布元素。如果是4个元素,左边2个右边2个;5 个元素,左 2 和右 3。

我认为 TwoColumnStackPanel 实际上是两个并排的 StackPanel,它可以使用现有的 StackPanel来实现吗?

class TwoColumnStackPanel : Panel
{
    private readonly StackPanel leftPanel;
    private readonly StackPanel rightPanel;

    public TwoColumnStackPanel()
    {
        leftPanel = new StackPanel();
        rightPanel = new StackPanel();
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        int size = InternalChildren.Count;
        int leftCount = size / 2;
        int rightCount = size - leftCount;
        //Load elements to left stackpanel.
        int index = 0;
        leftPanel.Children.Clear();
        for (int s = 0; s < leftCount; s++)
        {
            leftPanel.Children.Add(InternalChildren[index + s]);
        }
        //Load elements to right stackpanel.
        index += leftCount;
        rightPanel.Children.Clear();
        for (int s = 0; s < rightCount; s++)
        {
            rightPanel.Children.Add(InternalChildren[index + s]);//error
        }

        //Measure the two stackpanel and the sum is my desired size.
        double columnWidth = availableSize.Width / 2;

        leftPanel.Measure(new Size(columnWidth, availableSize.Height));
        rightPanel.Measure(new Size(columnWidth, availableSize.Height));

        return new Size(leftPanel.DesiredSize.Width + rightPanel.DesiredSize.Width, Math.Max(leftPanel.DesiredSize.Height, rightPanel.DesiredSize.Height));
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        leftPanel.Arrange(new Rect(0,0,leftPanel.DesiredSize.Width,leftPanel.DesiredSize.Height));
        rightPanel.Arrange(new Rect(leftPanel.DesiredSize.Width,0,rightPanel.DesiredSize.Width,rightPanel.DesiredSize.Height));

        return finalSize;
    }
}

上面的代码在标签行抛出异常。如何解决?我是否以正确的方式实施它?

4

2 回答 2

1

我知道这很旧,但实际上有一种更简单的方法来实现它,您可以使用两个(或更多)现有面板的现有功能来获得您想要的行为。

首先,子类 UniformGrid,而不是普通的 Panel。当您想要进行排列时,您只需要对面板进行子类化。你没有。您希望您的内部堆栈面板能够做到这一点。您只是将它们分发到内部面板。(您还提到了停靠面板,但这意味着您还必须指定停靠的附加属性,但无论哪种方式,此代码都是完全相同的。

*注意:随意使用任何面板作为根。我刚刚选择了 UniformGrid,这样 StackPanels 就可以并排放置了。但是您可以将任何面板嵌套在任何其他面板中。完全取决于你。

接下来,在新子类的构造函数中,将两个内部 StackPanel 添加到 UniformGrid,一个在左侧,一个在右侧。请注意,这意味着您的 'Children' 集合实际上将返回两个 StackPanel,而不是您添加的控件。这没关系,因为我们不再使用 Children 属性。您将创建自己的财产以供您提供的孩子使用。

现在就这样做。创建一个名为 StackPanelChildren 的 ObservableCollection 类型的新属性,然后在您的类上添加属性 [ContentProperty("StackPanelChildren")],它告诉 XAML 处理器您的控件的开始和结束标记之间的任何内容都插入到属性中,而不是正常的儿童财产。接下来,在类的构造函数中添加一个 Collection Changed 处理程序,以便您知道何时添加或删除了子项。然后简单地说,在该处理程序中,您可以根据需要从两个内部 StackPanel 中添加或删除项目。

为了真正完整,您可能想要覆盖 CreateUIElementCollection(它支持实际的 Children 属性),以便您可以返回它的只读版本,这样人们就不会弄乱您的内部 StackPanels。我会留给你来弄清楚那部分,因为它实际上并不需要。

这是一些伪代码(从我的脑海中输入,所以它可能无法编译,但你明白了......

[ContentProperty("StackPanelChildren")]
public class TwoColumnStackPanel : UniformGrid
{
    private readonly StackPanel leftStackPanel = new StackPanel();
    private readonly StackPanel rightStackPanel = new StackPanel();

    public TwoColumnStackPanel()
    {
        this.Rows = 1;
        this.Columns = 2;
        this.Children.Add(leftStackPanel);
        this.Children.Add(rightStackPanel);

        StackPanelChildren.CollectionChanged += StackPanelChildren_CollectionChanged;
    }

    // Note: You should make this a read-only Dependency property
    // I'm just doing it this way for brevity in typing
    private readonly ObservableCollection<UIElement> _stackPanelChildren = new ObservableCollection<UIElement>(); 
    public ObservableCollection<UIElement> StackPanelChildren
    {
        get{ return _stackPanelChildren; }
    }

    private void StackPanelChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // Add your child item distribution logic here. Make sure to account for
        // all new items, all removed items, and the Reset action which
        // clears all items but doesn't actually provide them on the argument
        // since you just assume 'everything must go!'
    } 

}

附加信息:

您可以添加类似于新 StackPanelChildren 属性的其他属性。但是,只有一个可以用作默认内容。不过,如果显式调用它们,您可以在 XAML 中指定它们中的任何一个。例如,假设由于某种原因您想要第二个项目集合,也许它们只进入左侧面板。该属性称为 AdditionalLeftPanelChildren。您只需在代码(ObservableCollection)中以完全相同的方式定义它并像这样访问......

<MyCustomPanel>

    <TextBlock Text="I'm in the normal content for the StackPanels." />
    <TextBlock Text="So am I!" />

    <MyCustomPanel.AdditionalLeftPanelChildren>

        <TextBlock Text="I go to the other property! Woot!" />

    </MyCustomPanel.AdditionalLeftPanelChildren>

</MyCustomPanel>
于 2015-04-26T18:28:48.750 回答
1

您不应该使用面板以
更好的方式实现面板(不完美,但它会给您带来想法):

class TwoColumnStackPanel : Panel
{

    protected override Size MeasureOverride(Size availableSize)
    {   //split the size
        Size halfPanelSize = new Size(availableSize.Width / 2, availableSize.Height / 2);
        Size secondHalfPanelSize = new Size(availableSize.Width - halfPanelSize.Width, availableSize.Height - halfPanelSize.Height);
        int firstHalf = InternalChildren.Count / 2;

        for (int i = 0; i < firstHalf; i++) //measure the first column
        {
            InternalChildren[i].Measure(halfPanelSize);
            Debug.WriteLine(InternalChildren[i].DesiredSize);
        }

        for (int i = firstHalf; i < InternalChildren.Count; i++)//measure the second column
        {
            InternalChildren[i].Measure(secondHalfPanelSize);
            Debug.WriteLine(InternalChildren[i].DesiredSize);
        }

        return availableSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        Size halfPanelSize = new Size(finalSize.Width / 2, finalSize.Height / 2);
        Size secondHalfPanelSize = new Size(finalSize.Width - halfPanelSize.Width, finalSize.Height - halfPanelSize.Height);
        int firstHalf = InternalChildren.Count / 2;
        Point location = new Point();


        for (int i = 0; i < firstHalf; i++) 
        {// arrange from (0,0) and add the height
            InternalChildren[i].Arrange(new Rect(location.X, location.Y, halfPanelSize.Width, InternalChildren[i].DesiredSize.Height));
            location.Y += InternalChildren[i].DesiredSize.Height;

        }

        location.X = halfPanelSize.Width; // move to the next column
        location.Y = 0;

        for (int i = firstHalf; i < InternalChildren.Count ; i++)
        {// arrange from (firts column width,0) and add the height
            InternalChildren[i].Arrange(new Rect(location.X, location.Y, secondHalfPanelSize.Width, InternalChildren[i].DesiredSize.Height));
            location.Y += InternalChildren[i].DesiredSize.Height;
        }

        return finalSize;
    }
}
于 2012-12-18T08:11:06.377 回答