VB のたまご

作成日: 2017/03/07, 更新日: 2017/03/12



DataTemplate

  •  DataTemplate は、データの表示方法をカスタマイズする仕組みです。 コントロールの見た目を変える ControlTemplate と間違いやすいですが、DataTemplate の場合は、コントロール自体の見た目は変わらず、 あくまでデータの表示部分のみのカスタマイズになります。Data に関する Template を定義しようということですね。

  •  とりあえずどういう風に動くのか見てみましょう。主に使う頻度が多いコレクションデータを対象にしています。 以下サンプルです。「DataTemplateWindow1」という画面で作成していますが、「MainWindow」に書いても構いません。

  • スポンサーリンク


    <Window x:Class="DataTemplateWindow1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow1" Height="300" Width="300"
            Loaded="Window_Loaded">
      
        <Grid>
    
            <ListBox Name="listbox1" Margin="10" />
    
        </Grid>
        
    </Window>
    

    Public Class DataTemplateWindow1
    
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    
            Me.listbox1.ItemsSource = Enumerable.Range(1, 100).Select(Function(i)
    
                                                                          Return New Person With {
                                                                          .Name = "taro" + i.ToString(),
                                                                          .Age = 20 + i,
                                                                          .IsMan = (i Mod 5 = 0)
                                                                          }
    
                                                                      End Function)
    
        End Sub
    
        Private Class Person
    
            Public Property Name As String = String.Empty
            Public Property Age As Integer = -1
            Public Property IsMan As Boolean = False
    
        End Class
    
    End Class
    

  •  これを実行すると、名前空間付きのクラス名が表示されてしまいました。 これは1つ1つのデータを ToString メソッドで取得した結果を表示するためですので、自前で ToString メソッドをオーバーライドして表示処理を記載します。

  •  それが以下のサンプルです。

  • <Window x:Class="DataTemplateWindow2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow2" Height="300" Width="300" Loaded="Window_Loaded">
    
        <Grid>
    
            <ListBox Name="listbox1" Margin="10" />
    
        </Grid>
    
    </Window>
    

    Public Class DataTemplateWindow2
    
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    
            Me.listbox1.ItemsSource = Enumerable.Range(1, 100).Select(Function(i)
    
                                                                          Return New Person With {
                                                                          .Name = "taro" + i.ToString(),
                                                                          .Age = 20 + i,
                                                                          .IsMan = (i Mod 5 = 0)
                                                                          }
    
                                                                      End Function)
    
        End Sub
    
        Private Class Person
    
            Public Property Name As String = String.Empty
            Public Property Age As Integer = -1
            Public Property IsMan As Boolean = False
    
            Public Overrides Function ToString() As String
    
                Dim gender = If(IsMan = True, "男", "女")
                Return "{" & Name & ", " & Age.ToString() & ", " & gender & "}"
    
            End Function
    
        End Class
    
    End Class
    

  •  ListBox 的には、通常は Name だけ表示するのが一般的な表示方法だと思います。

  •  それでは DataTemplate をカスタマイズして、表示方法を変えてみます。

  • <Window x:Class="DataTemplateWindow3"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow3" Height="300" Width="300" Loaded="Window_Loaded">
    
        <Grid>
    
            <ListBox Name="listbox1" Margin="10">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        
                        <Grid>
                            
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
    
                            <TextBlock Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Text="{Binding Name}" Margin="10" />
                            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Age}" Margin="10" />
                            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding IsMan}" Margin="10" />
    
                        </Grid>
                        
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
        </Grid>
    
    </Window>
    

    Public Class DataTemplateWindow3
    
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    
            Me.listbox1.ItemsSource = Enumerable.Range(1, 100).Select(Function(i)
    
                                                                          Return New Person With {
                                                                          .Name = "taro" + i.ToString(),
                                                                          .Age = 20 + i,
                                                                          .IsMan = (i Mod 5 = 0)
                                                                          }
    
                                                                      End Function)
    
        End Sub
    
        Private Class Person
    
            Public Property Name As String = String.Empty
            Public Property Age As Integer = -1
            Public Property IsMan As Boolean = False
    
            Public Overrides Function ToString() As String
    
                Dim gender = If(IsMan = True, "男", "女")
                Return "{" & Name & ", " & Age.ToString() & ", " & gender & "}"
    
            End Function
    
        End Class
    
    End Class
    

  •  実行して確認してみます。これでは項目と項目の間の切れ目が分からないですね。 少し微調整した結果が以下のサンプルです。

  • <Window x:Class="DataTemplateWindow4"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow4" Height="300" Width="300" Loaded="Window_Loaded">
    
        <Grid>
    
            <ListBox Name="listbox1" Margin="10" HorizontalContentAlignment="Stretch">
                <ListBox.ItemTemplate>
                    <DataTemplate>
    
                        <Border BorderBrush="Olive" BorderThickness="1" CornerRadius="10">
                            <Grid>
    
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                    <ColumnDefinition />
                                </Grid.ColumnDefinitions>
    
                                <TextBlock Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Text="{Binding Name}" Margin="10" />
                                <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Age}" Margin="10" />
                                <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding IsMan}" Margin="10" />
    
                            </Grid>
                        </Border>
    
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
        </Grid>
        
    </Window>
    

    Public Class DataTemplateWindow4
    
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    
            Me.listbox1.ItemsSource = Enumerable.Range(1, 100).Select(Function(i)
    
                                                                          Return New Person With {
                                                                          .Name = "taro" + i.ToString(),
                                                                          .Age = 20 + i,
                                                                          .IsMan = (i Mod 5 = 0)
                                                                          }
    
                                                                      End Function)
    
        End Sub
    
        Private Class Person
    
            Public Property Name As String = String.Empty
            Public Property Age As Integer = -1
            Public Property IsMan As Boolean = False
    
            Public Overrides Function ToString() As String
    
                Dim gender = If(IsMan = True, "男", "女")
                Return "{" & Name & ", " & Age.ToString() & ", " & gender & "}"
    
            End Function
    
        End Class
    
    End Class
    

  •  1つ1つの項目に枠を付けました。これだけだと左寄りにまとまってしまうので、ListBox の HorizontalContentAlignment プロパティに Stretch をセットして、 1つ1つの項目(Content)の位置を引き延ばしています。

  • スポンサーリンク


  •  今までコレクションデータで見てきましたが、単一データに対してもデータの表示方法をカスタマイズすることができます。

  • <Window x:Class="DataTemplateWindow5"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow5" Height="300" Width="300">
        
        <StackPanel Margin="10">
    
            <Button Content="button1" />
    
            <Button>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Ellipse Width="16" Height="16" Stroke="Olive"/>
                    <TextBlock Text="→" HorizontalAlignment="Center" />
                    <TextBlock Text="button2" Grid.Column="1" />
                </Grid>
            </Button>
    
        </StackPanel>
        
    </Window>
    
    

  •  1つ目が通常のボタンなのに対して、2つ目のボタンはアイコン的なものも一緒に表示するようにセットしています。 違いは、1つ目のボタンの Content プロパティには「文字列」をセットしているのに対して、 2つ目のボタンの Content プロパティには「コントロール」をセットしているという違いです。

  • (追加)
    実際には、Button.Content タグが間に入っているが、タグを省略して書くことができます。
    
    <Button>
        <Button.Content>←★
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Ellipse Width="16" Height="16" Stroke="Olive"/>
                <TextBlock Text="→" HorizontalAlignment="Center" />
                <TextBlock Text="button2" Grid.Column="1" />
            </Grid>
        </Button.Content>←★
    </Button>
    

  •  WinForms の時は、Button.Text プロパティには文字列しかセットできませんでした。 「Text」もセットできるけどこれだけではなく「Content」として表現できるもの全部を表示するよ。 ということで、Text プロパティではなく Content プロパティが用意されているんですね。

  •  Content プロパティに「コントロール」をセットしたらコントロールが表示されました。 次は、「データクラス」をセットしてみます。

  • <Window x:Class="DataTemplateWindow6"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:w05_WpfSystem"
            mc:Ignorable="d"
            Title="DataTemplateWindow6" Height="300" Width="300">
       
        <StackPanel Margin="10">
    
            <Button Name="button1" />
    
            <Button Name="button2">
                <Button.ContentTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <TextBlock Text="{Binding Name}" />
                            <TextBlock Text="{Binding Age}" Grid.Row="1" />
                        </Grid>
                    </DataTemplate>
                </Button.ContentTemplate>
            </Button>
            
        </StackPanel>
        
    </Window>
    
    

    Public Class DataTemplateWindow6
    
        Public Sub New()
    
            ' この呼び出しはデザイナーで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
            Me.button1.Content = New Person With {.Name = "taro", .Age = 32}
            Me.button2.Content = New Person With {.Name = "taro", .Age = 32}
    
        End Sub
    
        Private Class Person
    
            Public Property Name As String = String.Empty
            Public Property Age As Integer = -1
            Public Property IsMan As Boolean = False
    
            Public Overrides Function ToString() As String
    
                Dim gender = If(IsMan = True, "男", "女")
                Return "{" & Name & ", " & Age.ToString() & ", " & gender & "}"
    
            End Function
    
        End Class
    
    End Class
    
    

  •  実行してみてください。 1つ目のボタンは、データクラスを ToString したものが表示されていることが分かります。 2つ目のボタンは、上と下にデータクラスのメンバーとなる各プロパティが表示されています。 DataTemplate に表示方法をカスタマイズしてあるからですね(前述のコレクションと同じこと)。