Список сообщений в стиле Message Hub

в 14:23, , рубрики: .net, WP7, метки: ,

Если вы уже разрабатывали приложения под Windows Phone 7, то возможно уже заметили отсутствие некоторых элементов управления, таких как Hub Tile, сгруппированный список или список сообщений. Некоторые из них можно найти в библиотеке Silverlight Toolkit for Windows Phone. Но что делать, когда нам нужно создать приложение, похожее на стандартный Message Hub? Основная проблема заключается в том, что в стандартном LisBox'е элементы размещаются cверху вниз, в то время как в хабе сообщений — наоборот.

Список сообщений в стиле Message Hub

Хочу продемонстрировать одно из решений данной задачи. Нам понадобится лишь стандартный элемент управления ListBox, библиотека System.Windows.Interaction из Blend SDK и некоторые знания в области трансформации визуальных компонентов.

Итак. Как я уже говорил, стандартный ListBox размещает свои элементы в StackPanel в направлении сверху вниз. Написание своего StackPanel, который будет размещать элементы снизу вверх, может занять очень много времени. К тому же стандартный StackPanel уже содержит логику виртуализации, которая решает проблему потребления огромных объёмов памяти. Всё что нам нужно, это перевернуть ListBox вверх ногами, так, чтоб новые элементы размещались снизу. Для этого используем ScaleTransform:

<ListBox>
	<ListBox.RenderTransform>
		<ScaleTransform ScaleX="1" ScaleY="-1">
	</ListBox.RenderTransform>
</ListBox>

Теперь наш ListBox перевёрнут, но уехал вверх. Для того, чтобы вернуть его на место, нужно указать центр трансформации. В нашем случае это середина по вертикали, т.е.:

_scaleTransform.CenterY = ListBox.ActualHeight / 2;

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

Поклонники MVVM могут покривиться лишнему коду в .xaml.cs файлах. Всю дополнительную логику я обычно пишу в Behavior'ах из библиотеки System.Windows.Interaction. Для этого создадим класс MirrorBehavior c кодом:

public class MirrorBehavior : Behavior<Control>
{
	private readonly ScaleTransform _transform = new ScaleTransform();

	public MirrorBehavior()
	{
		_transform.ScaleX = 1;
		_transform.ScaleY = -1;
	}

	protected override void OnAttached()
	{
		UpdateCenter();

		AssociatedObject.SizeChanged += AssociatedObject_SizeChanged;

		AssociatedObject.RenderTransform = _transform;
	}

	protected override void OnDetaching()
	{
		AssociatedObject.RenderTransform = null;

		AssociatedObject.SizeChanged -= AssociatedObject_SizeChanged;

		ResetCenter();
	}

	private void AssociatedObject_SizeChanged(object sender, SizeChangedEventArgs e)
	{
		UpdateCenter();
	}

	private void UpdateCenter()
	{
		_transform.CenterY = AssociatedObject.ActualHeight / 2;
	}

	private void ResetCenter()
	{
		_transform.CenterY = 0;
	}
}

Осталось только применить его к нашим элементам управления:

<ListBox ItemsSource="{Binding Messages}">

	<i:Interaction.Behaviors>
		<Interactivity:MirrorBehavior />
	</i:Interaction.Behaviors>

	<ListBox.ItemTemplate>
		<DataTemplate>
			<ContentControl HorizontalContentAlignment="Stretch"
							VerticalContentAlignment="Stretch"
							ContentTemplate="{StaticResource MessageTemplate}"
							Content="{Binding}">

				<i:Interaction.Behaviors>
					<Interactivity:MirrorBehavior />
				</i:Interaction.Behaviors>

			</ContentControl>
		</DataTemplate>
	</ListBox.ItemTemplate>
</ListBox>

Данное решение хорошо тем, что использует стандартные стили и встроенную виртуализацию.

Автор: igoriok

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js