DataGrid以上の使いやすさを実現するFlexGrid for Silverlight
業務用アプリケーションでは、一覧表示が必要になる事がよくあります。業務用アプリケーションでは、画面の使いやすさと同時に、作業効率という面も重要な評価ポイントであり、“一覧性”はその重要なファクターになるからです。
Silverlightにも一覧表示用にDataGridコントロールがあります。各種データをデータソースとして連結して手軽に一覧表示はできるのですが、データ連結しなければ一覧表示する事ができないなど、凝ったことをやろうとすると、XAMLに対するスキルなどを身に付けていないと実現が難しいときもあります。有識者にとっては「XAMLでできるよ」「一度IEnum・・・で○○してから使えばできるよ」と簡単に説明できる事でも、実は多くの基礎ノウハウがあってこその簡単さだったりします。
今回紹介するFlexGrid for Silverlightは、様々な設定をプロパティで行えるため、シンプルな操作で目的のデザインを構築できる、使いやすいコンポーネントです。今回は、まずDataGridでTwitter上からハッシュタグのついたTweetを一覧表示する画面を作り、それをFlexGridに移行します。そして、最後に両者の使い勝手を比較してみたいと思います。なお、説明箇所ごとの実装をサンプルソース内に入れてあるので、適宜参照してください。
DataGridで一覧表示画面を実装
まずは、DataGridで一覧表示画面を実装していきます。
DataGridでの一覧表示画面のデザイン
ButtonとDataGridを画面上に配置して、Buttonをクリックした時にデータを取得して一覧表示するUIは、次のようなXAML定義になります。
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Button x:Name="Get_Button" Content="取得" Width="100" 
                    Click="Get_Button_Click" />
        </StackPanel>
        <sdk:DataGrid Grid.Row="1"
                      AutoGenerateColumns="True" 
                      HorizontalAlignment="Left" 
                      x:Name="Result_DataGrid" 
                      VerticalAlignment="Top" 
                      Height="227" 
                      Width="500">
        </sdk:DataGrid>
    </Grid>
DataGridの定義の重要な点は、次の2点になります。
- AutoGenerateColumnsを「True」にして、連結したデータソースに合わせて自動的にカラム生成
- 「x:Name」として「Result_DataGird」を設定し、コードからDataGridを操作できるように設定
 XAMLで定義した画面を操作したときのイベントとプロシージャを関連づける記述は、2種類の書き方ができます。今回は、XAML側でButtonに対して「Click="Get_Button_Click"」と記述していますが、コード側で「Handles Get_Button.Click」をプロシージャ宣言に追記しても同じ事が可能です。
DataGridにおける一覧表示させるためのコード
Silverlightでは、HTTP通信はすべて非同期で行うため、DownloadStringAsyncを使ってリクエストを送信し、DownloadStringCompletedイベントによりレスポンスを取得します。
Imports System.Runtime.Serialization.Json
Imports System.Text
Partial Public Class MainPage
    Inherits UserControl
    Private WithEvents Webs As New System.Net.WebClient
    Private Const URL As String = "http://hatsune.wankuma.com/service/CZ1103_search.aspx"
    Public Sub New()
        InitializeComponent()
    End Sub
    Private Sub Get_Button_Click(sender As System.Object,
                                 e As System.Windows.RoutedEventArgs)
        Call GetRecords()
    End Sub
    Private Sub GetRecords()
        Call Webs.DownloadStringAsync(New Uri(URL))
        Me.Get_Button.IsEnabled = False
        Me.Cursor = Cursors.Wait
    End Sub
    Private Sub Webs_DownloadStringCompleted(sender As Object,
                                             e As DownloadStringCompletedEventArgs) _
                                         Handles Webs.DownloadStringCompleted
        Me.Get_Button.IsEnabled = True
        Me.Cursor = Cursors.Arrow
        If e.Error IsNot Nothing Then
            MessageBox.Show(e.Error.Message)
        Else
            Dim serializer As New DataContractJsonSerializer(GetType(TSearchResult))
            Using stream As New System.IO.MemoryStream(Encoding.UTF8.GetBytes(e.Result))
                Dim result As TSearchResult = CType(serializer.ReadObject(stream), TSearchResult)
                Dim view As New Data.PagedCollectionView(result.results)
                view.SortDescriptions.Add(New ComponentModel.SortDescription("create_at", ComponentModel.ListSortDirection.Descending))
                Me.Result_DataGrid.ItemsSource = view
            End Using
        End If
    End Sub
    Public Class TSearch
        Public Property created_at As String
        Public Property from_user As String
        Public Property text As String
    End Class
    Public Class TSearchResult
        Public Property results As List(Of TSearch)
    End Class
End Class
一覧表示の実行
XAML側とコード側が完成したら実行してみましょう。
IDEから実行すれば自動的にWebブラウザが立ち上がり、その中でSilverlightが読み込まれて画面が表示されます。[取得]ボタンをクリックすると、「#wp7jp」のハッシュタグがついたTwitterの発言を一覧表示します。
次に、FlexGridを使った場合の実装方法を見ていきたいと思います。
DataGridからFlexGridへの移行
FlexGridを使う局面としては、新規に作成する場合以外にもDataGridからの置き換え(移行)という局面もあると思います。そこで、DataGridを使ったプログラムをもとにして、FlexGridに移行する手順を確認していきます。
コンポーネントの追加
FlexGridは標準コンポーネントではないので、IDEにコンポーネントを取り込まなければなりません。ツールボックスでアイテムの追加を行い「C1FlexGrid」を指定して、FlexGridコントロールをツールボックスに追加します。
FlexGridでの一覧表示画面のデザイン
FlexGridを使用するための前準備としては、次の2つの手順が必要です。
- 「C1.Silverlight.FlexGrid.4」に対する参照設定の追加
- XAML定義に「xmlns:c1="http://schemas.componentone.com/winfx/2006/xaml"」を追記
この手順は、FlexGridコントロールをツールボックスからドラッグ&ドロップすれば自動的に行われます。よって、DataGridからFlexGridに移行するときに最初に行う事は、DataGrid部分の書き換えではなく、ツールボックスからFlexGridコントロールをXAML上にドラッグ&ドロップする事です。それから、DataGridに書かれている属性をFlexGrid側のXAML定義の中にコピーして、DataGrid部分を削除します。
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <Button x:Name="Get_Button" Content="取得" Width="100" 
                Click="Get_Button_Click" />
    </StackPanel>
    <c1:C1FlexGrid Grid.Row="1"
                  AutoGenerateColumns="True" 
                  HorizontalAlignment="Left" 
                  x:Name="Result_DataGrid" 
                  VerticalAlignment="Top" 
                  Height="227" 
                  Width="500"
                  SelectionMode="RowRange">
    </c1:C1FlexGrid>
</Grid>
 XAML定義としては「sdk:DataGrid」を「c1:C1FlexGrid」に置き換え、「SelectionMode="RowRange"」の定義を追記しただけです。
FlexGridにおける一覧表示させるコード
今回のサンプルコードでは、コード側はDataGridの場合のコードがそのまま使えるので、変更はありません。
一覧表示の実行
XAML側とコード側が完成したら実行してみましょう。
IDEから実行すれば自動的にWebブラウザが立ち上がり、その中でSilverlightが読み込まれて画面が表示されます。[取得]ボタンをクリックすると、「#wp7jp」のハッシュタグがついたTwitterの発言を一覧表示します。
DataGridと同じような結果となりましたが、「from_user」欄の列幅が異なります。これはDataGridが該当列に表示されている情報の最大幅に、自動的に調整をしているからです。
AutoSizeColumnsを使用
DataGridと同じ列幅を実現するためには、FlexGrid側にデータ連結した後に「AutoSizeColumns」を設定する必要があります。
Private Sub Webs_DownloadStringCompleted(sender As Object,
                                         e As DownloadStringCompletedEventArgs) _
                                     Handles Webs.DownloadStringCompleted
    Me.Get_Button.IsEnabled = True
    Me.Cursor = Cursors.Arrow
    If e.Error IsNot Nothing Then
        MessageBox.Show(e.Error.Message)
    Else
        Dim serializer As New DataContractJsonSerializer(GetType(TSearchResult))
        Using stream As New System.IO.MemoryStream(Encoding.UTF8.GetBytes(e.Result))
            Dim result As TSearchResult = CType(serializer.ReadObject(stream), 
                TSearchResult)
            Dim view As New Data.PagedCollectionView(result.results)
            Me.Result_DataGrid.ItemsSource = view
            Me.Result_DataGrid.AutoSizeColumns(0, Me.Result_DataGrid.Columns.Count - 1, 0)
        End Using
    End If
End Sub
この設定を行えば、FlexGridでも自動的に列幅が設定されます。
FlexGridの性能を大量データの取り扱いで評価してみる
Silverlight標準のDataGridにもFlexGridにも行方向の仮想化機能が実装されています。この機能は実際に表示されている行だけを表示処理することで、行数が増えた時でも快適なスクロールを実現する機能です。FlexGridの場合は、これに加えて列方向の仮想化も実現します。列方向の仮想化を同時に実装することで、列数が多い場合でもスクロール性能が低下しません。
500列や1000列のデータを扱うときの性能差を、グレープシティのデモサイト(「パフォーマンス」タブ)にある、サンプルで確認してみましょう。データ設定の時間も含めて、DataGridとFlexGridでは明確な差を感じられると思います。

それでは、列数が少ない場合はどうでしょうか。データが多いときのケースとしては、列方向にデータが多いのではなく、行方向にデータが多いという場合も想定できます。そこで、DataGridとFlexGridに、それぞれ100,000レコードのデータを表示した場合、スクロール表示時に差が出るかを確認してみました。
それぞれスクロールボックスを上下にドラックさせてみると、FlexGridの方がスムーズにスクロールできることが分かると思います。行方向の仮想化だけであればDataGridにも実装されているのですが、同じ機能でもFlexGridの方がよりスマートな表示を実現しているのは驚きです。Silverlightのように、よりよいUIを期待して採用されるであろう技術の場合、このようなちょっとした違いが利用者の印象を左右します。
さいごに
SilverlightのDataGridでは、何か物足りない感がありました。これは、一覧表示だけであればDataGridを使うよりも、ListBoxの中に別のコントロールを配置してデザインした方が自由度が高いデザインが作れる、というのも影響しているように思います。
しかし、FlexGridならば、一覧表示コントロールの利便性と共にデザインの自由度もあります。業務用アプリケーションを作成するという場面では、FlexGridを使用する事で、DataGridやListBoxを使用するよりも様々な恩恵を受けられるでしょう。また、データ連結が必須ではないという手軽さもあります。そして、なによりFlexGrid for Windows Formsと近い感覚で設定ができるというのが、Windows Formsからの移行組にとっては福音となるでしょう。

 
              
               
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                              
                               
                              
                               
                              
                               
                              
                               
                              
                               
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
															
														 
															
														 
    .png) 
     
     
     
     
													 
													 
													 
													 
													 
										
									






