0

たとえば、 を実装しTwoColumnStackPanelます。名前が示すように、ジェネラルStackPanelは要素を 1 列にしかスタックできませんが、TwoColumnStackPanel は要素を 2 列にスタックできます。

TwoColumnStackPanel は、要素を 2 つの列に均等に分散する必要があります。4 つの要素の場合、左 2 と右 2。左 2 と右 3 の 5 つの要素。

TwoColumnStackPanel は実際には 2 つの横に並んだ 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

これが古いことは知っていますが、実際には、2 つ (またはそれ以上) の既存のパネルの既存の機能を使用して、必要な動作を取得できる、これを実装するはるかに簡単な方法があります。

まず、プレーンな Panel ではなく、UniformGrid をサブクラス化します。配置を行いたい場合にのみ、パネルをサブクラス化する必要があります。あなたはそうしない。内部スタック パネルでそれを行う必要があります。それらを内部パネルに配布しているだけです。(ドッキング パネルについても言及しましたが、それはドッキングされた添付プロパティも指定する必要があることを意味しますが、どちらにしても、このコードはまったく同じです。

*注意:任意のパネルをルートとして自由に使用してください。StackPanels が横並びになるように、UniformGrid を選択しました。ただし、任意のパネルを他の任意のパネル内にネストできます。完全にあなた次第です。

次に、新しいサブクラスのコンストラクターで、2 つの内部 StackPanels を UniformGrid に追加します。1 つは左側、もう 1 つは右側です。これは、追加するコントロールではなく、'Children' コレクションが実際に 2 つの StackPanels を返すことを意味することに注意してください。Children プロパティはもう使用しないので、これで問題ありません。提供された子に使用する独自のプロパティを作成します。

今それをしてください。タイプ ObservableCollection の StackPanelChildren という新しいプロパティを作成し、クラスに属性 [ContentProperty("StackPanelChildren")] を追加します。これは、コントロールの開始タグと終了タグの間にあるものは、通常のプロパティではなく、そのプロパティに挿入されることを XAML プロセッサに通知します。子供の財産。次に、クラスのコンストラクターに Collection Changed ハンドラーを追加して、子がいつ追加または削除されたかがわかるようにします。次に、そのハンドラーで、必要に応じて 2 つの内部 StackPanels からアイテムを追加または削除します。

本当に完全にするには、(実際の Children プロパティをサポートする) CreateUIElementCollection をオーバーライドして、その読み取り専用バージョンを返して、人々が内部の 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 プロパティと同様のプロパティを追加できます。ただし、デフォルトのコンテンツとして使用できるのは 1 つだけです。それでも、明示的に呼び出す場合は、XAML でそれらのいずれかを指定できます。たとえば、何らかの理由でアイテムの 2 つ目のコレクションが必要な場合、おそらくそれらは左側のパネルにのみ表示されます。そのプロパティは、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 に答える