2017年8月13日日曜日

VB WPF お勉強 06* ListBox と Binding ( #06 - 追補 )

Binding と ItemsSource を少し かじり ます。
項目に String ではなく、
Object (Class) の集合を引き当てて、
ListBox の見た目を少し弄って見ましょう。


Form から来た WPF 初心者(私の様に)は、この バインド に手を焼きます
ここでは、 ListBox に バインドする例 で説明します。

ListBox に項目を表示する上では、
先の投稿 VB WPF お勉強 06 ListBox と項目 で書いた様に、
配列 でも、List(Of String) でも、Dictionary(Of Integer, String) でも、
Me.ListBox1.ItemsSource に値を代入する事で、
また、 Me.ListBox1.Items.Refresh() する事で、
項目の動的な変更が可能です。

単純な場合は、これでも OK でしょう。

でも、 やはり、WPF そしてバインドの醍醐味は、複雑なケースをもカバーするその機能にあります。


例えば、選択した項目に 関連した別の要素を絡める としたら ... 。
上の 配列等の項目をそのままで行くなら、
別の要素を 別の配列等で用意し、その添え字(Index)を頼りに、
Code-Behind で操作する事になるかと思います。
つまり選択された項目の Index を元に、別要素を探し出し、何らかの処理を行う です。

でも、別の方法もあります。
オブジェクトとして、これらの内容を一括りに纏めれば、良い事になります。
そう、LIstBox で選ぶのは、 表示されている項目(字面)ではなく、一連の内容を保持したオブジェクトになります。

ここで出て来るのが Class の作成です。
                   Class が苦手な方!、 是非、マスターして下さいね。.
                   と書きながら、 私も ... 苦手!。.
各要素を、 Class の Property として定義し、
それを ItemsSource にする のです。
そして、WPF の バインド用に調整された Collection である、
ObjectModel.ObservativeCollection を使いましょう。
これで、先の .Refresh() は不要になります。

色々な書き方があると思いますが、
オブジェクトを表す Class のみ宣言したのが、こちら。

Code Class

Public Class SourceObjClass
    Public Property ObjText As String
    Public Property ObjOneMore As String
    Public Property ObjBool As Boolean

    Public Sub New()

    End Sub

    Public Sub New(ByVal addText As String, ByVal addMore As String, ByVal addBool As Boolean)
        ObjText = addText
        ObjOneMore = addMore
        ObjBool = addBool
    End Sub

    Public Overrides Function ToString() As String
        Return ObjText
    End Function
End Class


VB Code-Behind

Class MainWindow

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        SetItemsSource()
    End Sub

    Private Sub SetItemsSource()
        Dim itemsTest As New ObjectModel.ObservableCollection(Of SourceObjClass)
        itemsTest.Add(New SourceObjClass("Data1", "item1", True))
        itemsTest.Add(New SourceObjClass("Data2", "item2", False))
        itemsTest.Add(New SourceObjClass("Data3", "item3", True))
        Me.ListBox2.ItemsSource = itemsTest
    End Sub

End Class


バインドされた項目は ListBox に表示されますが、
その項目は  itemsTest と言う ObservativeCollection に纏められた SourceObjClass の集団です。
Object.ToString は通常オブジェクトのインスタンスを表す文字列を返します。
何も考えずに、 Class を設計すると、 ListBox には この値が反映されます。
つまり、全て、同じクラス名が戻されます。  この例では ProjectName.SourceObjClass 。
これでは困るので、各項目を識別するものを設定するのが  Public Overrides Function ToString() ... です。  (必須?).


さて では、いよいよ、 xaml を見て行きましょう。

WPF の ListBox は自由度が高いです。
.ItemTemplate ( 更にその子要素 DataTemplate ) で見た目を設定可能で、
その中の StackPanel で表示項目の指定や、 DataTemplate.Triggers で条件指定 が出来ます。

ここでは、
StackPanel で、各項目の持つ Property を横に並べて表示しつつ、
DataTemplate.Triggers で、 Property ObjBool の値に応じて、部分的な表示色 (Foregound) を指定しています。
そう、 ObjBool が True なら 青文字、 False なら 赤文字で、 別 Property (ObjText) の表示部分ですね。
どの部分か?を定義する為に、 TextBlock に命名して、 Trigger の中で TargetName を与えている処がポイントです。
Trigger 対象が True/False の2値なので、本来はひとつの Trigger で済むのですが ... 。  ご勘弁を。.

xaml

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>   
        <ListBox x:Name="ListBox2" Height="100" Margin="10,5,10,20" VerticalAlignment="Top">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding}" />
                        <TextBlock Text="  /  " />
                        <TextBlock Text="{Binding ObjText}"
                                   Name="TextBlockColor"/>
                        <TextBlock Text="    " />
                        <TextBlock Text="{Binding ObjOneMore}" />
                        <TextBlock Text="    " />
                        <TextBlock Text="{Binding ObjBool}" />
                    </StackPanel>
                    <DataTemplate.Triggers >
                        <DataTrigger Binding="{Binding ObjBool}" Value="True">
                            <Setter TargetName="TextBlockColor" Property="Foreground" Value="RoyalBlue"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding ObjBool}" Value="False">
                            <Setter TargetName="TextBlockColor" Property="Foreground" Value="Red"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>        
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>                         
    </Grid>
</Window>




はい。  少しは WPF っぽく なって来たみたい ですね。
でも、 横たわる 道 は遠く長い ... 。.



0 件のコメント:

コメントを投稿