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 в большинстве случаев — зло, и абсолютные размеры контролов должны задаваться только там, где это действительно необходимо.

  • Print this article!
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • E-mail this story to a friend!
  • LinkArena
  • LinkedIn
  • MisterWong
  • StumbleUpon
  • Technorati
  • Twitter