Предотвратить RowHeight = Auto от растяжения моей сетки? - PullRequest
1 голос
/ 22 июля 2011

В моем сценарии конечный пользователь настраивает свой пользовательский интерфейс, разбивая его на строки и определяя правило высоты для этих строк (фиксированное, заполнить пространство, подогнать содержимое).Я реализую это с помощью WPF Grid.

Сетка начинает заполнять весь экран и не должна становиться больше - пользователь ДОЛЖЕН иметь возможность видеть всю сетку в любое время (полосы прокрутки WITHIN в порядке,но не полосы прокрутки для всей сетки).

Суть моей проблемы: когда пользователь создает одну или несколько строк с автоматическим размером, содержимое в этих строках может заставить размер всей сетки расширяться., добавляя полосы прокрутки, даже когда я установил максимальную высоту сетки на фиксированное число.

Становится хуже, когда задействован ряд размером со звезду, потому что, как только сетка немного растянется, этоСтрока размером со звездочку заполняет доступное пространство, поэтому сетка ПОСТОЯННО растягивается, даже если впоследствии строка автоматического размера уменьшается.

Мне нужно найти способ ограничить «автоматические» строки, чтобы они расширялись и сжимались какнеобходимо, НО общая фактическая высота всех строк никогда не превышает всю сетку.

Для иллюстрации у меня есть эта сетка с фиксированной максимальной высотой, и строки rПредставление всех режимов размера.

<Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="200"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <TextBlock>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>
        abc<LineBreak/>                    
    </TextBlock>
</ScrollViewer>

В этом примере, когда текстовый блок abc расширяется, общая высота сетки выходит за пределы фиксированной.300 "максимальная высота.Как я могу предотвратить такое поведение, чтобы гарантировать максимальную высоту сетки при сохранении гибкости строк автоматического размера?

1 Ответ

0 голосов
/ 23 июля 2011

Хорошо, я обнаружил, что мне нужно создать подкласс Grid, чтобы переопределить шаги макета Measure () и Arrange ().

Я не утверждаю, что это отличное решение общего назначения, но оноработает по моему сценарию.Обратите внимание, что я не имею дело со столбцами, поскольку в моем случае есть только один столбец.Я также не размещаю элементы внутри ячеек (я оставляю их на якоре в верхнем левом углу).

Если вам нужно более общее решение, я думаю, что это очень хорошее начало.Проблема столбца такая же, как и проблема строки, только в другом направлении.

class NoStretchGrid:Grid
{
    //this override determines what size we ask to be
    //gotta make sure we never ask for more than the max height
    protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint)
    {
        //what would a basic Grid do?
        System.Windows.Size desiredSize = base.MeasureOverride(constraint);

        if (desiredSize.Height > constraint.Height)
            desiredSize.Height = constraint.Height;

        //if max height is defined and desired height is too big, reduce it
        if (this.MaxHeight != double.NaN && desiredSize.Height > this.MaxHeight)
        {
            desiredSize.Height = this.MaxHeight;
        }

        return desiredSize;
    }

    //this override tells child controls how big they can be and where they're positioned
    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
    {
        //must decide how tall each row will be
        double[] desiredHeights = new double[this.RowDefinitions.Count];
        double[] minimumHeights = new double[this.RowDefinitions.Count];
        double[] finalHeights = new double[this.RowDefinitions.Count];

        //first, find out how tall each row wants to be

        //check for fixed-size rows
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsAbsolute)
            {
                desiredHeights[i] = this.RowDefinitions[i].Height.Value;
            }
            else
            {
                desiredHeights[i] = 0;
            }

            minimumHeights[i] = this.RowDefinitions[i].MinHeight;
        }

        //then ask children how big they want to be
        foreach (UIElement child in this.InternalChildren)
        {
            int row = Grid.GetRow(child);
            if (!this.RowDefinitions[row].Height.IsAbsolute && child.DesiredSize.Height > desiredHeights[row])
            {
                desiredHeights[row] = child.DesiredSize.Height;
            }

            if ((child as FrameworkElement).MinHeight > minimumHeights[row])
            {
                minimumHeights[row] = (child as FrameworkElement).MinHeight;
            }
        }

        double availableHeight = arrangeSize.Height;

        //reserve minimum heights
        for (int i = 0; i < minimumHeights.Length; i++)
        {
            finalHeights[i] = minimumHeights[i];
            availableHeight -= finalHeights[i];
        }            

        //allow fixed-height rows their height - if some ignoramus made fixed-heights too big, we can't help him
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsAbsolute)
            {
                finalHeights[i] = this.RowDefinitions[i].Height.Value;
                availableHeight = availableHeight + minimumHeights[i] - finalHeights[i];
            }
        }

        //allow auto-size rows their desired heights, so long as there's height left to be had
        for (int i = 0; i < desiredHeights.Length; i++)
        {                
            if (this.RowDefinitions[i].Height.IsAuto)
            {
                double desiredHeightIncrease = desiredHeights[i] - minimumHeights[i];

                if (desiredHeightIncrease <= availableHeight)
                {
                    finalHeights[i] += desiredHeightIncrease;
                    availableHeight -= desiredHeightIncrease;
                }
                else
                {
                    finalHeights[i] = minimumHeights[i] + availableHeight;
                    availableHeight = 0;
                }
            }
        }

        //now that auto-size rows have been prevented from getting out of control, make the min heights of any star-size rows available again
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                availableHeight += minimumHeights[i];
            }
        }

        //divide any leftover available height proportionally amongst the star-sized rows, while there's height left to be had
        double totalStarValues = 0;
        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                totalStarValues += this.RowDefinitions[i].Height.Value;
            }
        }

        for (int i = 0; i < desiredHeights.Length; i++)
        {
            if (this.RowDefinitions[i].Height.IsStar)
            {
                finalHeights[i] = availableHeight * (this.RowDefinitions[i].Height.Value / totalStarValues);
            }
        }

        //decide the vertical position of each row
        double[] rowPositions = new double[desiredHeights.Length];
        rowPositions[0] = 0;
        for (int i = 1; i < rowPositions.Length; i++)
        {
            rowPositions[i] = rowPositions[i - 1] + finalHeights[i - 1];
        }

        //tell children to lay themselves out based on these results
        foreach (UIElement child in this.InternalChildren)
        {
            int row = Grid.GetRow(child);

            //special case for scrollviewer, which doesn't size itself appropriately
            if (child is ScrollViewer)
            {
                ScrollViewer scrollViewer = child as ScrollViewer;

                //temporarily update its height value, JUST for the Arrange() call
                double oldHeight = scrollViewer.Height;
                scrollViewer.Height = finalHeights[row];
                child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row]));

                //restore the original value
                scrollViewer.Height = oldHeight;
            }

            //typical case for non-scroll-viewers
            else
            {
                child.Arrange(new Rect(0, rowPositions[row], arrangeSize.Width, finalHeights[row]));
            }
        }

        return arrangeSize;
    }
}

Вот тестовый пример.Поместите это в окно и измените размер окна, чтобы увидеть, как оно работает.

<local:NoStretchGrid VerticalAlignment="Stretch">

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="50"/>
    </Grid.RowDefinitions>

    <ScrollViewer VerticalScrollBarVisibility="Visible" MinHeight="50">
        <Rectangle Fill="Orange" Height="250"/>
    </ScrollViewer>

    <ScrollViewer VerticalScrollBarVisibility="Visible" Grid.Row="1" MinHeight="50">
        <Rectangle Fill="Blue" Height="200"/>
    </ScrollViewer>

    <Grid Background="Pink" Grid.Row="2" MinHeight="30"/>
    <Grid Background="Green" Grid.Row="3" MinHeight="30"/>
    <Grid Background="Red" Grid.Row="4"/>

</local:NoStretchGrid>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...