Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Панель — это объект, который предоставляет поведение макета для дочерних элементов, содержащихся в нем, когда выполняется система макета расширяемого языка разметки приложений (XAML) и отрисовывается пользовательский интерфейс приложения.
Важные API: Панель, ArrangeOverride, MeasureOverride
Вы можете создать пользовательские панели для макета XAML, создав класс, производный от класса панели . Вы задаёте поведение для вашей панели, переопределяя методы MeasureOverride и ArrangeOverride, предоставляя логику, которая измеряет и размещает дочерние элементы.
Базовый класс панели Panel
Чтобы определить пользовательский класс панели, можно либо наследовать от класса Panel напрямую, либо наследовать от одного из удобных классов панели, которые не запечатаны, например, Grid или StackPanel. Проще сделать производный от панели панели, поскольку бывает сложно работать с существующей логикой макета панели, у которой уже есть поведение макета. Кроме того, панель с поведением может иметь существующие свойства, которые не относятся к функциям макета панели.
Из панели ваша пользовательская панель наследует следующие API:
- Свойство Детей.
- фоновые, ChildrenTransitions и свойства IsItemsHost, а также идентификаторы свойств зависимостей. Ни один из этих свойств не является виртуальным, поэтому обычно не переопределяете или не заменяете их. Обычно эти свойства не требуются для пользовательских сценариев панели, даже не для чтения значений.
- Методы переопределения макета MeasureOverride и ArrangeOverride. Изначально они были определены FrameworkElement. Базовый класс Panel не переопределяет их, но практические панели, такие как Grid, имеют реализации переопределения, которые написаны на родном коде и выполняются системой. Предоставление новых (или аддитивных) реализаций для ArrangeOverride и MeasureOverride является основной частью усилий, необходимых для определения пользовательской панели.
- Все остальные API FrameworkElement, UIElement и DependencyObject, такие как Высота, Видимость и т. д. Иногда вы используете значения этих свойств в ваших переопределениях макета, но они не являются виртуальными, поэтому обычно не переопределяются или не заменяются.
Здесь основное внимание уделяется описанию концепций макета XAML, поэтому вы можете рассмотреть все возможности того, как пользовательская панель может и должна вести себя в макете. Если вы хотите сразу приступить и увидеть пример реализации пользовательской панели, см. статью BoxPanel — это пример такой панели.
Свойство "Дочерние"
Свойство Children относится к настраиваемой панели, так как все классы, производные от Панели, используют свойство Children в качестве места для хранения содержащихся дочерних элементов в коллекции. Children назначено как свойство содержимого XAML для класса Panel, а все классы, производные от класса Panel, могут наследовать поведение свойства содержимого XAML. Если свойство присваивается свойству содержимого XAML, это означает, что разметка XAML может опустить элемент свойства при указании этого свойства в разметке, а значения задаются как непосредственные дочерние элементы разметки (содержимое). Например, если вы наследуете класс CustomPanel из Панели , который не определяет новое поведение, вы по-прежнему можете использовать эту разметку:
<local:CustomPanel>
<Button Name="button1"/>
<Button Name="button2"/>
</local:CustomPanel>
Когда XAML-парсер считывает эту разметку, Children известно как свойство содержимого XAML для всех производных типов Panel, поэтому парсер добавит два элемента Button в значение UIElementCollection свойства Children. Свойство содержимого XAML упрощает взаимоотношения между родительскими и дочерними элементами в разметке XAML для определения пользовательского интерфейса. Дополнительные сведения о свойствах содержимого XAML и заполнении свойств коллекции при анализе XAML см. в руководстве по синтаксису XAML.
Тип коллекции, который поддерживает значение свойства Children, является классом UIElementCollection. UIElementCollection — это строго типизированная коллекция, использующая UIElement в качестве типа элемента. UIElement — это базовый тип, наследуемый сотнями практических типов элементов пользовательского интерфейса, поэтому применение типов здесь намеренно свободно. Но это требует, чтобы у вас не могло быть Кисть как прямого дочернего элемента Панели, и обычно это означает, что только те элементы, которые должны отображаться в пользовательском интерфейсе и участвовать в макете, будут найдены как дочерние элементы в Панели.
Как правило, пользовательская панель принимает любой UIElement в качестве дочернего элемента в определении XAML, просто используя характеристики свойства Children as-is. В качестве расширенного сценария можно поддерживать дополнительную проверку типов дочерних элементов при переопределении коллекции в макете.
Помимо цикла дочерних коллекции в переопределениях, логика панели также может влиять на Children.Count. У вас может быть логика, которая выделяет пространство по крайней мере частично на основе количества элементов, а не требуемых размеров и других характеристик отдельных элементов.
Переопределение методов макета
Базовая модель для методов переопределения макета (MeasureOverride и ArrangeOverride) заключается в том, что они должны выполнять итерацию всех дочерних элементов и вызывать конкретный метод макета каждого дочернего элемента. Первый цикл макета начинается, когда система макета XAML задает визуальный элемент для корневого окна. Поскольку каждый родительский элемент вызывает отображение макета у дочерних элементов, это передаёт вызов методов макета всем элементам пользовательского интерфейса, которые должны являться частью макета. В макете XAML существует два этапа: сначала измерение, затем расположение.
Вы не получаете встроенное поведение метода макета для MeasureOverride и ArrangeOverride из базового класса Panel. Элементы в дочерние элементы не будут автоматически отображаться как часть визуального дерева XAML. Ваша задача сделать элементы известными для процесса макета, вызывая методы макета для каждого из элементов, которые находятся в Дочерних с помощью прохождения макета в MeasureOverride и в реализации ArrangeOverride.
Нет никаких причин вызывать базовые реализации в переопределениях макета, если у вас нет собственного наследования. Встроенные методы для поведения макета (если они существуют) выполняются в любом случае, и отсутствие вызова базовой реализации из переопределения не препятствует встроенному поведению.
Во время прохода измерения логика макета опрашивает каждый дочерний элемент о его требуемом размере, вызывая метод Measure для этого дочернего элемента. Вызов метода Measure устанавливает значение свойства DesiredSize . Возвращаемое значение MeasureOverride представляет собой требуемый размер самой панели.
Во время процесса размещения позиции и размеры дочерних элементов определяются в пространстве x-y, а структура компоновки подготавливается для отрисовки. Код должен вызывать Упорядочить для каждого дочернего элемента в Children, чтобы система макета детектировала, что элемент принадлежит макету. Вызов упорядочить является предшественником композиции и отрисовки; он сообщает системе макета, в которой идет этот элемент, когда композиция отправляется для отрисовки.
Многие свойства и значения способствуют работе логики макета во время выполнения. Один из способов подумать о процессе компоновки заключается в том, что элементы без дочерних элементов (как правило, самый глубоко вложенный элемент в пользовательском интерфейсе) — это те, которые могут первыми завершить измерения. У них нет зависимостей от дочерних элементов, влияющих на их требуемый размер. Они могут иметь свои предпочтительные размеры, и это лишь предложения по размеру до тех пор, пока макет фактически не будет сформирован. Затем процесс измерения продолжается вверх по визуальному дереву, пока корневой элемент не получит свои размеры и все измерения можно будет завершить.
Макет кандидата должен помещаться в текущее окно приложения, иначе части пользовательского интерфейса будут обрезаны. Панели часто являются местом, где определяется логика вырезки. Логика панели может определить, какой размер доступен в рамках реализации MeasureOverride, и может потребоваться применить ограничения размера на дочерние элементы и распределить пространство между ними так, чтобы всё как можно лучше подходило. В идеале результат макета — это то, что использует различные свойства всех частей макета, но по-прежнему помещается в окно приложения. Для этого требуется хорошая реализация логики макета панелей, а также разумный дизайн пользовательского интерфейса в части любого кода приложения, создающего пользовательский интерфейс с помощью этой панели. Дизайн панели не будет выглядеть хорошо, если общая конструкция пользовательского интерфейса включает в себя больше дочерних элементов, чем может быть включено в приложение.
Большая часть работы системы макета заключается в том, что любой элемент, основанный на FrameworkElement уже имеет свое собственное поведение при работе в качестве дочернего элемента в контейнере. Например, существует несколько API FrameworkElement, которые либо информируют о поведении макета, либо необходимы для работы макета в целом. К ним относятся:
- DesiredSize (фактически свойство UIElement)
- ФактическаяВысота и ФактическаяШирина
- Высота и Ширина
- Отступ
- событие LayoutUpdated
- Горизонтальное выравнивание и Вертикальное выравнивание
- методы ArrangeOverride и MeasureOverride
- Методы "Упорядочить" и "Измерить" : они имеют собственные реализации, определённые на уровне FrameworkElement, обеспечивающем обработку действий макета на уровне элемента.
MeasureOverride
Метод
Все реализации MeasureOverride должны выполнять цикл Дочерниеи вызывать метод Measure для каждого дочернего элемента. Вызов метода Measure устанавливает значение свойства DesiredSize . Это может определить, сколько пространства требуется самой панели, а также как это пространство делится между элементами или выделяется для конкретного дочернего элемента.
Ниже приведен очень базовый скелет метода MeasureOverride:
protected override Size MeasureOverride(Size availableSize)
{
Size returnSize; //TODO might return availableSize, might do something else
//loop through each Child, call Measure on each
foreach (UIElement child in Children)
{
child.Measure(new Size()); // TODO determine how much space the panel allots for this child, that's what you pass to Measure
Size childDesiredSize = child.DesiredSize; //TODO determine how the returned Size is influenced by each child's DesiredSize
//TODO, logic if passed-in Size and net DesiredSize are different, does that matter?
}
return returnSize;
}
Элементы часто имеют естественный размер к тому времени, когда они готовы к макету. После прохождения меры DesiredSize может указывать на натуральный размер, если доступный размер, который вы передали для Measure, был меньше. Если естественный размер превышает availableSize, который вы передали для измерения, то размер DesiredSize ограничивается значением availableSize. Вот как ведет себя внутренняя реализация Мера, и ваши переопределения макета должны учитывать это поведение.
Некоторые элементы не имеют естественного размера, так как они имеют автоматические значения для высоты и ширины. Эти элементы используют полную availableSize, потому что значение Авто означает следующее: размерьте элемент до максимального доступного размера, который ближайший родитель макета сообщает, вызывая Measure с availableSize. На практике всегда существует определенное измерение, по которому определяется размер пользовательского интерфейса (даже если это окно верхнего уровня). В конечном итоге передача размеров разрешает все значения Auto на основе родительских ограничений, а все Auto элементы получают реальные измерения (которые можно получить, проверив ActualWidth и ActualHeightпосле завершения макета).
Допустимо передать размер для мере, которая имеет по крайней мере одно бесконечное измерение, чтобы указать, что панель может попытаться изменить свой размер, чтобы соответствовать измерениям её содержимого. Каждый дочерний элемент, измеряемый, задает значение DesiredSize с использованием естественного размера. Затем панель во время прохождения упорядочивания обычно использует этот размер для упорядочивания.
Текстовые элементы, такие как TextBlock, имеют вычисляемые ActualWidth и ActualHeight, которые определяются на основе их строк и текстовых свойств, даже если значения Height или Width не заданы, и эти размеры должны учитываться логикой вашей панели. Обрезка текста является особенно плохим пользовательским интерфейсом.
Даже если ваша реализация не использует требуемые измерения размера, лучше вызвать метод Measure для каждого дочернего элемента, поскольку это активирует внутренние и собственные поведения, которые срабатывают при вызове Measure. Для участия элемента в макете на каждом дочернем элементе должен быть вызван метод Measure во время прохождения меры и метод Arrange во время прохождения упорядочивания. Вызов этих методов задает внутренние флаги объекта и заполняет значения (например, свойство DesiredSize), которые требуются логике макета системы при создании визуального дерева и отображении пользовательского интерфейса.
Значение MeasureOverride основывается на логике, которую панель применяет при интерпретации DesiredSize или других размерных аспектов для каждого дочернего элемента в Children при вызове Measure. Что делать со значениями DesiredSize от дочерних элементов и как значение, возвращаемое MeasureOverride, должно использоваться — это зависит от интерпретации вашей собственной логики. Обычно значения не добавляются без изменений, так как входные данные MeasureOverride часто являются фиксированным доступным размером, предлагаемым родительским элементом панели. При превышении этого размера сама панель может быть обрезана. Обычно вы сравниваете общий размер дочерних элементов с доступным размером панели и при необходимости вносите корректировки.
Советы и рекомендации
- В идеале пользовательская панель должна подходить для того, чтобы стать первым настоящим визуальным элементом в композиции пользовательского интерфейса, возможно, на уровне непосредственно под Page, UserControl или другим элементом, который является корнем страницы XAML. В реализации MeasureOverride не возвращайте входные размеры без тщательной проверки значений. Если возвращаемый размер содержит значение Infinity, это может вызвать ошибки в логике компоновки во время выполнения. Значение Бесконечности может поступать из главного окна приложения, которое можно прокручивать и поэтому не имеет максимальной высоты. Другое прокручиваемое содержимое может иметь то же поведение.
- Еще одна распространенная ошибка в реализации MeasureOverride — вернуть новый размер по умолчанию (значения высоты и ширины равны 0). Вы можете начать с этого значения, и это может быть даже правильное значение, если панель решит, что ни один из дочерних элементов не должен отображаться. Но размер по умолчанию приводит к тому, что хост неправильно задает размер вашей панели. Это не запрашивает пространства в пользовательском интерфейсе, и, следовательно, не получает его и не отображается. Весь код вашей панели может работать нормально, но вы все равно не увидите панель или её содержимое, если она отрисовывается с нулевой высотой и нулевой шириной.
- В переопределениях избегайте соблазна привести дочерние элементы к FrameworkElement и использовать свойства, вычисленные из-за особенностей компоновки, особенно ActualWidth и ActualHeight. В большинстве распространенных сценариев логику можно строить на основе значения DesiredSize дочернего, и вам не понадобятся никакие из Height или Width, связанных свойств дочернего элемента. В специализированных случаях, когда вы знаете тип элемента и имеете дополнительную информацию, например естественный размер файла изображения, можно использовать специализированные сведения элемента, так как это не значение, активно изменяемое системами макетов. Включение вычисляемых свойств макета в рамках логики макета значительно увеличивает риск определения непреднамеренного цикла макета. Эти циклы вызывают ситуацию, в результате которой не может быть создан допустимый макет, и система может выдать LayoutCycleException, если цикл невозможно возобновить.
- Панели обычно распределяют доступное пространство между несколькими дочерними элементами, хотя способ распределения пространства может варьироваться. Например, сетка реализует логику макета, которая использует значения в RowDefinition и ColumnDefinition, чтобы разделить пространство на ячейки сетки , поддерживая как размер по звездам, так и по пикселям. Если это значения пикселей, доступный размер для каждого дочернего элемента уже известен, поэтому он используется как входящий размер для сетки меры.
- Сами панели могут создавать зарезервированное пространство для заполнения между элементами. Если это сделать, обязательно предоставьте измерения как свойство, отличное от свойства Margin или любого свойства padding.
- Элементы могут иметь значения для свойств ActualWidth и ActualHeight на основе предыдущего прохода макета. Если значения изменяются, код UI приложения может установить обработчики для LayoutUpdated на элементы, если необходимо выполнить специальную логику, но логика панели обычно не требует проверки изменений с помощью обработки событий. Система разметки уже определяет, когда необходимо повторно запустить макет, если изменяется значение связанного с макетом свойства. Методы MeasureOverride панели или ArrangeOverride вызываются автоматически при соответствующих условиях.
ПереопределениеУпорядочивания
Метод ArrangeOverride имеет возвращаемое значение Size, используемое системой макета при отрисовке самой панели, когда метод Упорядочить вызывается на панели родительским элементом в макете. Обычно входные finalSize и ArrangeOverride возвращенные Size одинаковы. Если это не так, значит, панель пытается изменить свои размеры на другие, отличные от тех, что утверждают другие участники в макете доступны. Окончательный размер был основан на том, что ранее был выполнен проход меры через код панели, поэтому возврат другого размера не является типичным: это означает, что вы намеренно игнорируете логику мер.
Не возвращайте размер с компонентом Infinity. Попытка использовать такой размер приводит к возникновению исключения из внутреннего макета.
Все реализации ArrangeOverride должны выполнять цикл Дочерниеи вызывать метод Arrange для каждого дочернего элемента. Как и Measure, Arrange не имеет возвращаемого значения. В отличие от Measure, в результате не устанавливается никакое вычисленное свойство (однако элемент, о котором идет речь, обычно вызывает событие LayoutUpdated).
Вот очень базовый скелет метода ArrangeOverride :
protected override Size ArrangeOverride(Size finalSize)
{
//loop through each Child, call Arrange on each
foreach (UIElement child in Children)
{
Point anchorPoint = new Point(); //TODO more logic for topleft corner placement in your panel
// for this child, and based on finalSize or other internal state of your panel
child.Arrange(new Rect(anchorPoint, child.DesiredSize)); //OR, set a different Size
}
return finalSize; //OR, return a different Size, but that's rare
}
Процесс компоновки может произойти без предварительного этапа измерения. Однако это происходит только в том случае, если система макета не изменила свойства, которые повлияли бы на предыдущие измерения. Например, если выравнивание изменяется, не нужно повторно измерять этот элемент, так как его DesiredSize не изменится при изменении выбора выравнивания. С другой стороны, если ActualHeight изменяется у любого элемента в макете, требуется новый проход проверки размеров. Система макета автоматически обнаруживает изменения точного измерения и снова вызывает этап измерения, а затем запускает новый этап упорядочивания.
Входные данные для Упорядочить принимают значение типа Rect. Самый распространенный способ создания этого Rect — использовать конструктор с параметрами: точка и размер . Точка , соответствующая точке, это место, где должен находиться верхний левый угол рамки элемента. Размер — это измерения, используемые для отрисовки этого конкретного элемента. Вы часто используете DesiredSize для этого элемента как значение Size, поскольку установление DesiredSize для всех элементов, участвующих в макете, было целью прохождения меры макета. Проход измерения определяет все размеры элементов в итеративном режиме, чтобы система макета могла оптимизировать размещение элементов после того, как она перейдет к проходу упорядочения.
Как правило, что обычно различается между реализациями ArrangeOverride, так это логика, с помощью которой панель определяет компонент Point того, как она упорядочивает каждый дочерний элемент. Панель абсолютного размещения, такая как Canvas, использует явные сведения о размещении, которые она получает от каждого элемента через значения Canvas.Left и Canvas.Top. Панель разделения пространства, например Сетка , будет иметь математические операции, разделяющие доступное пространство на ячейки, и каждая ячейка будет иметь значение x-y, для которого его содержимое должно быть помещено и упорядочено. Адаптивная панель, такая как StackPanel, может расширяться для соответствия содержимому по направлению ориентации.
В компоновке по-прежнему есть дополнительные факторы, влияющие на элементы компоновки, помимо того, что вы напрямую управляете и передаете параметры в Упорядочить. Эти элементы следуют из внутренней реализации Упорядочить, которая общая для всех FrameworkElement производных типов и расширена другими типами, такими как текстовые элементы. Например, элементы могут иметь поле и выравнивание, а некоторые могут иметь заполнение. Эти свойства часто взаимодействуют. Дополнительные сведения см. в разделе Выравнивание, отступы и заполнение.
Панели и элементы управления
Избегайте размещения функциональных возможностей в настраиваемую панель, которая вместо этого должна быть создана как пользовательский элемент управления. Роль панели заключается в том, чтобы представить содержимое дочерних элементов в виде функции размещения, выполняющейся автоматически. Панель может добавлять украшения в содержимое (аналогично тому, как Border добавляет границу вокруг элемента, который он представляет), или выполнять другие корректировки, связанные с макетом, например заполнение. Но это примерно так далеко, как вы должны идти при расширении выходных данных визуального дерева за пределами отчетов и использования информации от детей.
Если есть какое-либо взаимодействие, доступное пользователю, необходимо написать пользовательский элемент управления, а не панель. Например, панель не должна добавлять окна просмотра прокрутки в содержимое, даже если цель заключается в предотвращении вырезки, так как полосы прокрутки, пальцы и т. д. являются интерактивными частями управления. (Содержимому может потребоваться добавление полос прокрутки, но положитесь на логику дочернего элемента. Не заставляйте его, добавляя прокрутку в качестве операции макета.) Вы можете создать элемент управления и написать пользовательскую панель, которая имеет существенное значение в визуальном дереве этого элемента управления для представления содержимого в этом элементе управления. Но элемент управления и панель должны быть отдельными объектами кода.
Одна из причин, по которой важно различие между элементом управления и панелью, обусловлена автоматизацией пользовательского интерфейса Майкрософт и специальными возможностями. Панели обеспечивают поведение визуального макета, а не логическое поведение. Визуальное представление элемента пользовательского интерфейса не является аспектом пользовательского интерфейса, который обычно важен для сценариев специальных возможностей. Доступность заключается в предоставлении доступа к частям приложения, которые логически важны для понимания пользовательского интерфейса. При необходимости элементы управления должны предоставлять возможности взаимодействия инфраструктуре автоматизации пользовательского интерфейса. Чтобы получить больше информации, см. узлы автоматизации пользовательских интерфейсов.
API другого макета
Существуют некоторые другие API, которые являются частью системы макета, но не объявлены в Панели. Их можно использовать в реализации панели или в пользовательском элементе управления, использующего панели.
- UpdateLayout, InvalidateMeasure и InvalidateArrange — это методы, инициирующие передачу макета. InvalidateArrange может не вызывать прохождение измерения, но два других метода вызывают. Никогда не вызывать эти методы из переопределения метода макета, так как они почти наверняка вызывают цикл макета. Код управления обычно не должен вызывать их. Большинство аспектов макета активируются автоматически путем обнаружения изменений в свойствах макета, определенных платформой, таких как Ширина и т. д.
- LayoutUpdated — это событие, которое возникает при изменении некоторых аспектов макета элемента. Это не является специфическим для панелей; событие определяется FrameworkElement.
- SizeChanged — это событие, которое запускается только после завершения прохождения макета и указывает, что ActualHeight или ActualWidth изменились в результате. Это очередное событие FrameworkElement. Существуют случаи, когда срабатывает LayoutUpdated, но SizeChanged не происходит. Например, внутреннее содержимое может быть изменено, но размер элемента не изменился.
Связанные темы
Справочные материалы
Основные понятия
Windows developer