WrapPanel из Silverlight Toolkit

Silverlight Toolkit включает в себя парочку новых панелей, одна из который вам очень пригодится - это WrapPanel. Своей простотой в управлении WrapPanel позволяет вам добавлять UIElements, которые она же позиционирует последовательно (обычно слева направо) до того, как им не будет хватать места, при этом она создаст следующую строку и перенесет их на нее - то есть, осуществит перенос элементов.

Создание простой тестовой программы демонстрирует некоторые интересные аспекты данной панели (WrapPanel). Для этого, откройте Visual Studio и создайте новое приложение Silverlight (Silverlight Application). Убедитесь в том, что вы добавили ссылку на Microsoft Windows Controls.dll, который прилагается c Toolkit (самое главное помнить то, где вы их расположили!)

Также вам необходимо создать пространство имен в самом начале файла Page.xaml, но Intellisence готов вам помочь в этом,

Сделав это, данный пример программы создаст WrapPanel c одной единственной кнопкой в ней - вот полный код Xaml:

<UserControl x:Class="WrapPanel.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"           
    Width="800" Height="600">
    <Grid x:Name="LayoutRoot" Background="White">
    <controls:WrapPanel x:Name="WPanel"
      VerticalAlignment="Stretch"
      HorizontalAlignment="Stretch"
      Orientation="Horizontal">
      <Button
        x:Name="Add"
        Content="Add!"
        FontFamily="Georgia"
        FontSize="24"
        Width="Auto"
        Height="Auto"
        Background="#FF0000FF"
        Margin="5" />
    </controls:WrapPanel>
  </Grid>
</UserControl>

Назначение кода, поддерживающего данную страницу, заключается в том, что когда пользователь нажимает на кнопку добавления, то  выбирается один из элементов управления, созданный и добавленный к панели.

using System;
using System.Windows;
using System.Windows.Controls;

namespace WrapPanel
{
  public partial class Page : UserControl
  {
    private Random rand = new Random();

    public Page()
    {
      InitializeComponent();
      Add.Click += new RoutedEventHandler( Add_Click );

    }

Для реализации данной цели добавляется переменная экземпляра типа Random, так как существует обработчик события для кнопки добавления ("Add") созданный в Xaml.

Вся интересная работа будет проделана в обработчике события. Если вы начнете с создания элементов управления одинакового размера,

 void Add_Click( object sender, RoutedEventArgs e )
 {
   Button b = new Button();
   b.Content = DateTime.Now.ToLocalTime().ToString();
   b.Width = 120;
   b.Height = 35;
   b.Margin = new Thickness( 5 );
   this.WPanel.Children.Add( b );
}

то вы получите хорошо структурированный внешний вид, так как каждый элемент управления добавлятся до тех пор, пока не будет хватать места на строке и, следовательно, будет осуществлен перенос. (в продемонстрированном ниже примере я создал небольшие элементы управления и кнопку Add такого же размера):

Если же вы создаете другие объекты, то результат будет иным. В следующем примере я создал один из четырех типов объектов

void Add_Click( object sender, RoutedEventArgs e )
{
  int choice = rand.Next( 0, 4 );
  Control c;
  switch ( choice )
  {
    case 0:
      // создание кнопки
    case 1:
      // создание текстового поля
    case 2:
      // создание короткого списка
    case 3:
    default:
     // создание элемента управления паролем (password control)
  }
 // установка размера шрифта
 // установка ширины, высоты и отступа
 // добавление элемента управления в панель  
}

Тут могут появиться несколько интересных вопросов. А что, если мы установим не фиксированный размер шрифта, и что если высота будет автоматически установлена (это можно сделать программным путем, установив значение в double.Nan ).

 void Add_Click( object sender, RoutedEventArgs e )
{
  /*
  Button b = new Button();
  b.Content = DateTime.Now.ToLocalTime().ToString();
  b.Width = 120;
  b.Height = 35;
  b.Margin = new Thickness( 5 );
  this.WPanel.Children.Add( b );
  */

 
  int choice = rand.Next( 0, 4 );
  Control c;
  switch ( choice )
  {
    case 0:
      Button b = new Button();
      b.Content = DateTime.Now.ToLocalTime().ToString();
      c = b;
      break;
    case 1:
      TextBox t = new TextBox();
      t.Text = rand.Next(0,int.MaxValue-1).ToString();
      c = t;
      break;
    case 2:
      ItemsControl i = new ItemsControl();
      i.Items.Add( "Cormac McCarthy" );
      i.Items.Add( "Neal Stephenson" );
      i.Items.Add( "Marcel Proust" );
      i.Items.Add( "Virginia Woolfe" );
      c = i;
      break;
    case 3:
    default:
      PasswordBox p = new PasswordBox();
      p.Height = 35;
      p.Password = "Secret";
      c = p;
      break;
  }

  c.FontSize = rand.Next( 6, 18 );
  if ( c is ItemsControl )
  {
    c.FontSize = Math.Max( c.FontSize, 10 );
  }
  c.Width = Double.NaN;
  c.Height = Double.NaN;
  c.Margin = new System.Windows.Thickness( 5 );
 
  this.WPanel.Children.Add( c );
}

Полученный результат  интересно будет рассмотреть, хотя это и не совсем то, что мы хотели бы получить. Одним из решений будет исправление высоты кнопок, что впоследствии создаст другой интересный эффект. Сделав небольшую модификацию кода, мы можем это проверить:

if ( c is ItemsControl )
{
  c.FontSize = rand.Next( 8, 18 );
  c.Height = Double.NaN;
  //c.FontSize = Math.Max( c.FontSize, 10 );
}
else
{
  c.FontSize = 12;
  c.Height = 30;
}

Это вызовет центрирование элементов управления в списке, вместо того, чтобы изменять их размеры.

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