Создание элемента компоновки

Последнее обновление: 1.11.2015

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

Во-первых, как и все элементы компоновки, новый элемент должен быть унаследован от класса System.Windows.Controls.Panel.

Во-вторых, новый контейре компоновки, должен реализовать два метода - MeasureOverride и ArrangeOverride. который унаследованы в классовой ирерахии от класса FrameworkElement. Вобщем всю логику работы можно представить в два этапа - измерение размеров элементов, которое происходит в методе MeasureOverride, и сам процесс компановки элементов, который происходит в методе ArrangeOverride. Посмотрим, что представляют эти методы.

MeasureOverride

В методе MeasureOverride мы должны пройти по всем дочерним элементам контейнера и вызвать для каждого элемента метод Measure(). В этот метод в качестве параметра передаются размеры максимально допустимого места для данного элемента. После этого метода мы можем получить текущее значение свойства DesiredSize данного элемента с учетом ограничения по размеру. В любом случае этот метод должен вызываться обязательно, тем более что без этого некоторые дочерние элементы не будут отрисованы. В конце метода мы возвращаем размер панели:

protected override Size MeasureOverride(Size panelSpace)
{
    // Обход всех дочерних элементов
    foreach (UIElement element in this.Children)
    {
        //Указываем аксимально допустимый размер элемента
        Size availableElementSize = new Size(...);
        element.Measure(availableElementSize);
    }
    return new Size(...);
}

ArrangeOverride

Метод ArrangeOverride похож на метод MeasureOverride. В нем мы также возвращаем размер панели и также осуществляем обход элементов. Только теперь вместо метода Measure() вызывается метод Arrange(). В этот метод в качестве параметра передаются координаты прямоугольника, в котором должен располагаться элемент на панели для данного элемента:

protected override Size ArrangeOverride(Size panelSize)
{
    // Обход всех дочерних элементов
    foreach (UIElement element in this.Children)
    {
        // Указываем координаты прямоугольнкиа,
		// в который будет вписан элемент
        Rect elementBounds = new Rect(...);
        element.Arrange(elementBounds);
    }

    // возвращаем размер панели
    return arrangeSize;
}

Теперь посмотрим на примере, как реализовать собственную панель. Допустим, мы хотим создать некую панель Table - таблицу, которая будет содержать некоторое количество строк и столбцов, автоматически настраивающих свои размеры под размер элементов управления.

Для начала в методе CalculateColumns мы будем устанавливать количество строк и столбцов. Кроме того, в методе MeasureOverride мы будем устанавливать значения высоты для каждой строки и значения ширины для каждого столбца. И на основании этих данных в методе ArrangeOverride позиционировать элемент на панели:

public class Table : System.Windows.Controls.Panel
    {
        public int ColumnsNumber { get; set; }
        public int RowsNumber { get; set; }

        public double[] Rows;
        public double[] Columns;

        private int realColumns;
        private int realRows;

        public Table() : base()
        {}

        private void CalculateColumns()
        {
            // Подсчет элементов
            double elementCount = this.Children.Count;
            // Если панель пуста, выходим из метода
            if (elementCount == 0) return;

            realRows = RowsNumber;
            realColumns = ColumnsNumber;

            // Если свойства Rows и Columns установлены, используем их
            if ((realRows != 0) && (realColumns != 0))
                return;

            // Если ни одно из свойств не установлено, вычисляем кол-во столбцов
            if ((realColumns == 0) && realRows == 0)
                realColumns = (int)Math.Ceiling(Math.Sqrt(elementCount));

            // Если установлено только свойство Rows, вычисляем свойство Columns
            if (realColumns == 0)
                realColumns = (int)Math.Ceiling(elementCount / realRows);

            // Если установлено только свойство Columns, вычисляем свойство Rows
            if (realRows == 0)
                realRows = (int)Math.Ceiling(elementCount / realColumns);

            //Массив для значений высоты строк
            Rows = new double[realRows];

            //Массив для значений ширины столбцов
            Columns = new double[realColumns];
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            CalculateColumns();
            // некий ограничитель
            int constraint = 300;
            // распределяем пространство панели равномерно
            Size childConstraint = new Size(constraint / realColumns, constraint / realRows);

            int rowcounter = 0;
            int colcounter = 0;

            // Обход всех элементов
            foreach (UIElement child in this.Children)
            {
                // Получаем желаемый размер элемента
                child.Measure(childConstraint);

                // Обновляем максимальное значение стркои
                Rows[rowcounter] = child.DesiredSize.Height < Rows[rowcounter] ? Rows[rowcounter] : child.DesiredSize.Height;
                // Обновляем максимальное значение столбца
                Columns[colcounter] = child.DesiredSize.Width < Columns[colcounter] ? Columns[colcounter] : child.DesiredSize.Width;
                //Добавляем 10 для задания отступа
                Columns[colcounter] = Columns[colcounter] + 10;
               
                colcounter++;
                if (colcounter == realColumns)
                {
                    rowcounter++;
                    colcounter = 0;
                }
            }
            // Получаем совокупную высоты всех строки и ширину всех столбцов
            double panelHeight = Rows.Sum();
            double panelWidth = Columns.Sum();
            // На основании полученных значений устанавливаем размер панели
            return new Size(panelHeight, panelWidth);
        }

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double cellWidth ;
            double cellHeight;

            // Счетчики
            int rowcounter = 0;
            int colcounter = 0;

            // Текущие позиции
            double currentX = 0;
            double currentY = 0;

            // Обход всех элементов панели
            foreach (UIElement child in this.Children)
            {
                cellHeight = Rows[rowcounter];
                cellWidth = Columns[colcounter];
                // Определяем пространство для каждого дочернего элемента
                child.Arrange(new Rect(currentX, currentY, cellWidth, cellHeight));

                colcounter++;
                currentX += cellWidth;
                if (colcounter == realColumns)
                {
                    rowcounter++;
                    colcounter = 0;
                    currentY += cellHeight;
                    currentX = 0;
                } 
            }
            // Возвращаем размер панели                       
            return arrangeSize;
        }
    }

Чтобы использовать в XAML нашу таблицу, сначала надо добавить в декларации пространство имен приложения:

<UserControl x:Class="TestApplication.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:TestApplication"
    mc:Ignorable="d"
    d:DesignHeight="250" d:DesignWidth="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Text="TIOBE Index" HorizontalAlignment="Center"/>
        <local:Table Background="AntiqueWhite" ColumnsNumber="3" Margin="0 20 0 0">
            <TextBlock Text="Первая" Height="40" />
            <TextBlock Text="пятерка" Height="30" />
            <TextBlock Text="языков" Height="20" />
            <TextBlock Text="Place" />
            <TextBlock Text="Language" />
            <TextBlock Text="Rate" />
            <TextBlock Text="1" />
            <TextBlock Text="C" />
            <TextBlock Text="17.346%" />
            <TextBlock Text="2" />
            <TextBlock Text="Java" />
            <TextBlock Text="16.599%" />
            <TextBlock Text="3" />
            <TextBlock Text="C++" />
            <TextBlock Text="17.346%" />
            <TextBlock Text="4" />
            <TextBlock Text="Objective-C" />
            <TextBlock Text="8.309%" />
            <TextBlock Text="5" />
            <TextBlock Text="C#" />
            <TextBlock Text="6.823%" />
        </local:Table>
    </Grid>
</UserControl>

Если мы запустим приложение, то увидим браузере нашу таблицу:

Обратите внимание. что мы задали число столбцов, и панель автоматически расчитала все строки. Также обратите внимание, что для первой строки высота была установлена по самому большому элементу - в данном случае по кнопке, которая имеет высоту 40 пикселей.


Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850