4

この XAML スニペットを検討してください...

<DockPanel x:Name="TestDockPanel">

    <Button x:Name="RightButton" Content="Right" DockPanel.Dock="Right" />
    <Button x:Name="FillButton" Content="Fill" />

</DockPanel>

書かれているように、DockPanel は 'RightButton' を右にレイアウトし、残りの領域を 'FillButton' で次のように埋めます...

ここに画像の説明を入力

「FillButton」の可視性が「Collapsed」に変更されたときに、「RightButton」が次のように領域を塗りつぶすように、スタイルを設定する方法を見つけようとしています...

ここに画像の説明を入力

これを行う唯一の方法は、'TestDockPanel' の子から 'FillButton' を物理的に削除することですが、これにはコード ビハインドが必要であり、回避しようとしています。

アップデート

以下の回答では、サブクラスに基づくソリューションを提案しました。ただし、任意のDockPanel (または他のサブクラス) で使用でき、できればスタイルまたは添付された動作を介して適用できるものが欲しいので、これを開いたままにします。また、明確にするために、ソリューションの要件は、グリッドやその他のパネルではなく、DockPanel に基づいている必要があることです。

4

1 に答える 1

2

DockPanel サブクラスの形式でソリューションを作成しましたが、これを受け入れられた回答としてマークしていません。これは、スタイルまたは添付された動作を介してこれを行う方法を引き続き見つけて、任意のDockPanel で使用できるようにすることを望んでいるためです (または他のサブクラス)、これだけではありません。

それでも、他の人にとっては、これが役立つかもしれないので、ここに投稿します。

クラス全体のコードは次のとおりです。作業の核心は、Reflector から抽出された DockPanel の元のロジックに基づいた ArrangeOverride メソッドにあります。

LastChildFill が設定されている場合、既存のロジックが機能する方法は ArrangeOverride 内で、最後の子のインデックス (つまり、入力される項目のインデックス) を変数に格納していました。LastChildFill が設定されていない場合、代わりにその変数に「count」が格納されます。

次に、実際の配置を実行する子をループするときに、配置される要素のインデックスが以前に格納されたインデックスよりも小さい場合は、「ドッキング」ロジックを実行し、それ以外の場合は「フィル」ロジックを実行しました。

これは、LastChildFill が false の場合、すべての要素が「ドッキング」ロジックを実行したことを意味します。これは、すべての要素が格納されたインデックスよりも下にインデックスを持っているためです (これも「カウント」または最高インデックス + 1 に等しくなります)。ただし、LastChildFill が true の場合、最後の要素は格納されたインデックスよりも小さいインデックスを持っていなかったため (実際にはそれと同じでした)、1 つの要素が「フィル」ロジックを実行し、他のすべてが「ドッキング」ロジックを実行していました。

私が行った変更は、上記のように LastChildFill が設定されている場合、保存されたインデックスは最後の子を指し始めますが、その要素の可視性をチェックし、それが見えない場合は、インデックスを 1 つ下げて再度チェックし、表示可能な要素を見つけるか、チェックする子を使い果たします (つまり、それらがすべて非表示かどうか)。これが、技術的には変数に「firstFilledIndex」という名前を付けた理由でもあり、その後のすべての要素は「Fill」ロジックを使用します。それ以降のすべての要素は見えませんが。

最後に、新しい LastVisibleChildFill プロパティを追加して、新しい動作を有効または無効にしました。これを true に設定すると、暗黙的に LastChildFill も true に設定されます。

これが完全なコードです。

public class DockPanelEx : DockPanel
{
    public static readonly DependencyProperty LastVisibleChildFillProperty = DependencyProperty.Register(
        "LastVisibleChildFill",
        typeof(bool),
        typeof(DockPanelEx),
        new UIPropertyMetadata(true, (s,e) => {

            var dockPanelEx = (DockPanelEx)s;
            var newValue = (bool)e.NewValue;

            if(newValue)
                dockPanelEx.LastChildFill = true; // Implicitly enable LastChildFill
            // Note: For completeness, we may consider putting in code to set
            // LastVisibileChildFill to false if LastChildFill is set to false

        }));

    /// <summary>
    /// Indicates that LastChildFill should fill the last visible child
    /// Note: When set to true, automatically also sets LastChildFill to true as well.
    /// </summary>
    public bool LastVisibleChildFill
    {
        get { return (bool)GetValue(LastVisibleChildFillProperty); }
        set { SetValue(LastVisibleChildFillProperty, value); }
    }

    protected override Size ArrangeOverride(Size totalAvailableSize)
    {
        UIElementCollection internalChildren = base.InternalChildren;
        int count = internalChildren.Count;

        int firstFilledIndex = count;

        if(LastChildFill)
        {
            for(firstFilledIndex = count - 1; firstFilledIndex >= 0; firstFilledIndex--)
            {
                if(!LastVisibleChildFill || internalChildren[firstFilledIndex].IsVisible)   
                    break;
            }
        }

        double usedLeftEdge   = 0.0;
        double usedTopEdge    = 0.0;
        double usedRightEdge  = 0.0;
        double usedBottomEdge = 0.0;

        for (int i = 0; i < count; i++)
        {
            UIElement element = internalChildren[i];
            if (element != null)
            {
                Size desiredSize = element.DesiredSize;

                var finalRect = new Rect(
                    usedLeftEdge,
                    usedTopEdge,
                    Math.Max(0.0, (totalAvailableSize.Width  - (usedLeftEdge + usedRightEdge))),
                    Math.Max(0.0, (totalAvailableSize.Height - (usedTopEdge  + usedBottomEdge))));

                if (i < firstFilledIndex)
                {
                    switch (GetDock(element))
                    {
                        case Dock.Left:
                            usedLeftEdge += desiredSize.Width;
                            finalRect.Width = desiredSize.Width;
                            break;

                        case Dock.Top:
                            usedTopEdge += desiredSize.Height;
                            finalRect.Height = desiredSize.Height;
                            break;

                        case Dock.Right:
                            usedRightEdge += desiredSize.Width;
                            finalRect.X = Math.Max((double) 0.0, (double) (totalAvailableSize.Width - usedRightEdge));
                            finalRect.Width = desiredSize.Width;
                            break;

                        case Dock.Bottom:
                            usedBottomEdge += desiredSize.Height;
                            finalRect.Y = Math.Max((double) 0.0, (double) (totalAvailableSize.Height - usedBottomEdge));
                            finalRect.Height = desiredSize.Height;
                            break;
                    }
                }
                element.Arrange(finalRect);
            }
        }
        return totalAvailableSize;
    }
}
于 2013-09-12T22:28:46.287 に答える