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;
}
}