Silverlight2.0のレイアウト

最近、WPFになじんできたので、ひょっとして今ならSilverlight余裕ちゃうん?と思って過去に挫折したmRadioのSilverlight化を再開しようかと思ってきました。とりあえずSilverlight2.0のレイアウト機能をみてみることにしました。

上の記事で挫折したのは、Silverlight 1.0にはレイアウト用のコントロールとして”Canvas”しか用意されていないことにあります。Canvasではコントロールの配置にはおのおのに座標を指定してあげないといけません。HTMLでいえば全ての表示をcssのabsoluteでやると考えてもらうとわかりやすいでしょうか。表示する項目が少なければこれでもよいのですが、特に動的にデータを取得して表示する場合など、それぞれの座標をいちいち計算して指定するというのは非常に面倒です。

Silverlight 2.0(Beta)ではCanvus, Grid, StackPanelが使えるようになりました。GridはHTMLでいうtableのようなものだと思ってもらうとよいと思います。StackPanelは縦方向、横方向にコントロールを並べることができます。

大まかなレイアウトはGridで行い、リストのようにたくさんのコンテンツを並べるときにはStackPanelを使うというような感じで使えます。

リストボックスのアイテムをStackPanelで横に並べた場合:
mRadioSliverlight2_200805210.jpg

ただし、WPFにあるようなWrapPanelやUniformGridはないので横に並べながら画面右端にきたら折り返す。というような並べ方はちょっと難しそうです。

ただ自前のコントロールも実装でき、CodeProjectでWarpPanelを作っている人がいました。

http://www.codeproject.com/KB/silverlight/WrapPanelSilverlight.aspx

CodeProjectにあったWrapPanelを使った場合:
mRadioSliverlight2_200805211.jpg

がんばればCoverFlowのようなコントロールも作れそうですね。

WrapPanel.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace mRadio
{
  public class WrapPanel : Panel
  {
    public Orientation Orientation
    {
      get { return (Orientation)GetValue(OrientationProperty); }
      set { SetValue(OrientationProperty, value); }
    }
    public static readonly DependencyProperty OrientationProperty =
    DependencyProperty.Register("Orientation", typeof(Orientation), typeof(WrapPanel), null);
    public WrapPanel()
    {
      // default orientation
      Orientation = Orientation.Horizontal;
    }
    protected override Size MeasureOverride(Size availableSize)
    {
      foreach (UIElement child in Children)
      {
        child.Measure(new Size(availableSize.Width, availableSize.Height));
      }
      return base.MeasureOverride(availableSize);
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
      Point point = new Point(0,0);
      int i = 0;
      if (Orientation == Orientation.Horizontal)
      {
        double largestHeight = 0.0;
        foreach (UIElement child in Children)
        {
          child.Arrange(new Rect(point, new Point(point.X + child.DesiredSize.Width, point.Y + child.DesiredSize.Height)));
          if (child.DesiredSize.Height > largestHeight)
          largestHeight = child.DesiredSize.Height;
          point.X = point.X + child.DesiredSize.Width;
          if ((i + 1) < Children.Count)
          {
            if ((point.X + Children[i + 1].DesiredSize.Width) > finalSize.Width)
            {
              point.X = 0;
              point.Y = point.Y + largestHeight;
              largestHeight = 0.0;
            }
          }
          i++;
        }
      }
      else
      {
        double largestWidth = 0.0;
        foreach (UIElement child in Children)
        {
          child.Arrange(new Rect(point, new Point(point.X + child.DesiredSize.Width, point.Y + child.DesiredSize.Height)));
          if (child.DesiredSize.Width > largestWidth)
          largestWidth = child.DesiredSize.Width;
          point.Y = point.Y + child.DesiredSize.Height;
          if ((i + 1) < Children.Count)
          {
            if ((point.Y + Children[i + 1].DesiredSize.Height) > finalSize.Height)
            {
              point.Y = 0;
              point.X = point.X + largestWidth;
              largestWidth = 0.0;
            }
          }
          i++;
        }
      }
      return base.ArrangeOverride(finalSize);
    }
  }
}
<UserControl x:Class="mRadio.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:mRadio;assembly=mRadio"
>
<Grid x:Name="LayoutRoot" Background="White">
  <Grid.RowDefinitions>
  <RowDefinition Height="40"/>
  <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Test" Click="Button_Click"/>
<ListBox Grid.Row="1" x:Name="MediaList" SelectionChanged="MediaList_SelectionChanged" Style="{StaticResource MediaListBoxStyle}">
  <ListBox.ItemsPanel>
  <ItemsPanelTemplate>
    <me:WrapPanel Orientation="Horizontal"/>
  </ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
  <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
    <Image Source="{Binding Jacket}"/>
    <TextBlock Text="{Binding Title}" TextAlignment="Center"/>
  </StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>