Silverlight: HorizontalContentAlignment не работает в ListBox. Как починить?
Silverlight и WPF предоставляют богатые возможности для создания графического интерфейса приложения. Привычные компоненты, такие как Button, ListBox и многие другие, могут быть очень гибко настроены для отображения почти всего что угодно. К примеру, в кнопку можно засунуть произвольный набор картинок, текста, итд.
Однажды мне понадобилось видоизменить шаблон итемов ListBox в Silverlight, чтобы заставить его отображать элементы в примерно таком виде:

Ничего сложного, подумал я, и очень скоро напоролся на проблему: для того, чтобы листбокс выглядел по-человечески, каждый Item должен быть растянут на всю ширину листбокса. Предполагается, что на выравнивание итемов влияет пропертя HorizontalContentAlignment. Задание HorizontalCotnentAlignment=Stretch, по идее, должно растянуть все итемы по горизонтали на 100%, но ничего подобного не происходит (Silverlight 2.0, 3.0Beta). HorizontalContentAlignment не работает! В результате листбокс выглядит так:

Меня это, разумеется, не устроило, пришлось думать, как лечить.
Решения тут могут быть разные:
- отказаться от ListBox и использовать ItemsControl, в котором этой проблемы не замечено (тогда не будет функциональности листбокса, такой как выделения итемов, например)
- указывать ширину содержимого итема явно, и при ресайзе листбокса соответственно подгонять ширину содержимого под листбокс
- делать тоже самое на WPF
- обработать напильником ListBox и таки научить его растягивать итемы
Как раз о последнем способе и написано ниже.
При более детальном рассмотрении проблемы нашлась причина такого поведения листбокса: итемы не выравниваются, так как сам лист бокс не устанавливает пропертю HorizontalContentAlignment у ContentPresenter-а, который и отображает каждый отдельный итем. Видимо, при изменении HorizontalContentAlignment у листбокса это значение должно задаваться всем итемам в листбоксе, но этого не происходит. Оказалось, что это относительно малой кровью можно поправить – путём создания подкласса ListBox-a, который всё сделает правильно:
using System.Windows; using System.Windows.Controls; namespace SilverlightApplication1 { public class ListBox2 : ListBox { private class ListItem2 : ListBoxItem { private ListBox2 parent; public ListItem2(ListBox2 parent) { this.parent = parent; } public override void OnApplyTemplate() { base.OnApplyTemplate(); ContentPresenter cp = this.GetTemplateChild("contentPresenter") as ContentPresenter; if (cp != null) { cp.HorizontalAlignment = parent.HorizontalContentAlignment; cp.VerticalAlignment = parent.VerticalContentAlignment; } } } protected override DependencyObject GetContainerForItemOverride() { return new ListItem2(this); } } }
Получаем новый, «правильный» листбокс, который плодит объекты для каждого итема, которые при задании им шаблона берут из этого шаблона ContentPresenter и назначают ему выравнивание, как указано в самом ListBox-e.
Соответственно, в XAML тоже вносим изменения — вместо ListBox используем наш ListBox2:
<UserControl x:Class="SilverlightApplication1.MainControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:me="clr-namespace:SilverlightApplication1" > <me:ListBox2 HorizontalContentAlignment="Stretch" /> </UserControl>
После чего наслаждаемся растягивающимся листбоксом!
В заключение добавлю: всё вышенаписанное актуально, если размеры листбокса и его итемов не фиксированные. Если зафиксировать размер листбокса, указав явно его размер, то можно соответственно задать жестко размер итемов и не приделывать костылей. Однако я всё же придерживаюсь мнения, что фиксированный layout в большинстве случаев — зло, и абсолютные размеры контролов должны задаваться только там, где это действительно необходимо.














Comments
Leave a comment Trackback