WPF: MeasureOverride с побочными эффектами?

Ситуация:

Я пытаюсь создать класс панели StickyInfoPanel для двух дочерних элементов, расположенных вертикально. Нижний из двух пунктов должен быть 100px в высоту. Верхний элемент должен занимать оставшуюся часть общей доступной высоты.

Верхний элемент представляет собой ScrollView со стековой панелью и двумя дочерними элементами внутри (upper1 и upper2). Поэтому, когда для стековой панели недостаточно места, должна появиться вертикальная полоса прокрутки, как показано на рисунке ниже:

Эта проблема:

Хотя правильная высота передается верхнему элементу во время фазы аранжировки, его итоговая высота выше, и, как следствие, полоса прокрутки не отображается. (См 2-й скриншот)

Только когда окно будет уменьшено еще больше, так что будет отображаться только верхний1, появится полоса прокрутки. Но его кнопка "вниз" все еще отсутствует (см. Третий скриншот)

Как ни странно, при передаче правильной желаемой высоты на элемент из MeasureOverrideвсе работает как положено.

Насколько я понимаю, MeasureOverride не должен иметь побочных эффектов, чего, очевидно, нет. Кто-нибудь может объяснить, чего мне здесь не хватает?

XAML:

<Window x:Class="GridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:GridTest"
        Title="MainWindow" Height="190.57" Width="800">
    <local:StickyInfoPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
        <ScrollViewer Background="LightYellow" VerticalScrollBarVisibility="Auto" CanContentScroll="false">
            <StackPanel Margin="30 0" CanVerticallyScroll="False">
                <TextBlock Background="AliceBlue" Height="100">upper1</TextBlock>
                <TextBlock Background="Azure" Height="100">upper2</TextBlock>
            </StackPanel>
        </ScrollViewer>
        <TextBlock Background="Gainsboro" Height="100">Lower</TextBlock>
    </local:StickyInfoPanel>
</Window>

Класс панели:

class StickyInfoPanel : Panel
{
    public StickyInfoPanel()
        : base()
    {
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        InternalChildren[0].Measure(availableSize);
        InternalChildren[1].Measure(availableSize);
        //this works:
        //InternalChildren[0].Measure(new Size(availableSize.Width, availableSize.Height - 100));
        return availableSize;
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        InternalChildren[0].Arrange(new Rect(new Point(0, 0),                      new Size(finalSize.Width, finalSize.Height - 100)));
        InternalChildren[1].Arrange(new Rect(new Point(0, finalSize.Height - 100), new Size(finalSize.Width, 100)));
        return finalSize; 
    }
}

1 ответ

Итак, после некоторых измерений, вот окончательная формула для результирующих размеров дочерних элементов внутри пользовательских панелей (операторы min и max работают здесь поэлементно):

actualSize = max (min (доступный размер, требуемый размер, теоретический), окончательный размер)

куда

  • actualSize - результирующий размер дочернего элемента
  • availableSize - это размер, который панель передала методу элемента из его собственного MeasureOverride.
  • wantedSizeTheoretical - это размер, который дочерний элемент хотел бы занять, если бы было доступно бесконечное пространство
  • finalSize - это размер, который панель передала методу Arrange дочернего элемента из собственного ArrangeOverride.

Или, говоря словами: результирующий размер всегда будет finalSize, если дочернему элементу не нужно больше места и ему не было обещано больше места в MeasureOverride. В этом случае результирующий размер будет меньшим из этих двух значений.

Это было найдено для ScrollViewer как дочерний элемент. Надеюсь, другие классы будут вести себя так же.

Другие вопросы по тегам