今更ながらWPFに置き換えてみる(9)


本来の機能である毎日のPC起動時間と終了時間をテキストに保存して、リスト表示する部分のロジックをVB→C#に置き換え。

ListviewへのCSVファイル内容のセット

読み込んだテキストバッファをCRLF区切りでSPLITして行配列化、さらに各行をカンマでSPLITして出来上がったフィールド配列をアイテムに突っ込むというベタな手順。
VB+Formsの時はこんな感じ


       Dim i As Integer
        For i = 0 To UBound(lines) - 1

            Dim cols() = Split(lines(i), ",")
            Dim itemx As New ListViewItem
            Dim e_w() = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}

            'アイテムの作成
            itemx.Text = Mid(cols(0), 5, 2) & "/" & Mid(cols(0), 7, 2)        '日付
            'アイテムの追加
            itemx.SubItems.Add(e_w(Int(cols(1))))     '曜日

            If Int(cols(1)) = 0 Then
                itemx.UseItemStyleForSubItems = False
                itemx.SubItems(1).ForeColor = Color.Red
            End If
            If Int(cols(1)) = 6 Then
                itemx.UseItemStyleForSubItems = False
                itemx.SubItems(1).ForeColor = Color.Blue
            End If
            itemx.SubItems.Add(cols(2))     '開始時間
            itemx.SubItems.Add(cols(3))     '終了時間
            Me.ListView1.Items.Add(itemx)

        Next i

おんなじListviewだし、Formsと大して変わらんだろうと思ってましたが考えが甘かった。結構迷う。
なによりWPFのListviewはサブアイテムなどというものがない模様。
でさらにItem内容を書き換えてもRefreshしてやらないと画面更新されない。
リスト作成はWPF+C#の場合、

        private void MakeListView(string Buff)
        {
            //ListViewを作成
            //曜日
            string[] e_w = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};

            //行配列化、さらにフィールド分解してListViewItemとして追加
            string[] crlf = { "\r\n" };
            string[] lines = Buff.Split(crlf, StringSplitOptions.None);
            string[] dlm = { "," };
            for (int i = 0; i < lines.Length-1; i++)
            { 
                string[] fld = lines[i].Split(dlm, StringSplitOptions.None);
                fld[0] = fld[0].Substring(4, 2) + "/" + fld[0].Substring(6,2);
                fld[1] = e_w[int.Parse(fld[1])];
                this.listView.Items.Add(fld);
            }
            this.listView.Items.Refresh();
        }

さらにリスト内のアイテムを動的に更新する場合ベタなループで、

            //Listview上で特定条件の行の特定項目を更新する
            for(int i=0;i<listView.Items.Count;i++)
            {
                string[] fld = (string[])listView.Items[i];     //1行分を配列に入れる
                if (fld[0] == "AAA")
                {
                    fld[0]="xxx"
                    listView.Items[i] = fld;      //こんな感じで配列をそのまま突っ込む
                }
            }
            listView.Items.Refresh();

てな感じで対応。foreachを使ったほうが早い模様ですが、どのみち更新するためのインデックスがいるので、わかりやすさからforで処理記述しています。

追記

当初上のようにstringの配列を直接listviewのアイテムに入れてましたが、最終的にはリスト1行のデータ(日付、曜日、起動時刻、終了時刻)をclassにして代入してます。
単にlistviewに入れるだけなら問題ないのですが、土曜日を青文字、日曜を赤文字にするためにはxaml側でtrigger設定してstyle適用しないと無理なようで、そのため仕方なく・・。
ここまで極力xamlを触らないやり方で進めてきたんですがこれはいかんともしがたい模様。formsの場合はitem内でスタイル指定できてたんで、どうとでもなったんですが。

    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="BorderBrush" Value="LightGray"/>
            <Setter Property="BorderThickness" Value="0,0,0,0.5"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding dow}" Value="SUN">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding dow}" Value="SAT">
                    <Setter Property="Foreground" Value="Blue" />
                </DataTrigger>
                <Trigger Property="IsSelected" Value="True" >
                    <Setter Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
                    <Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListView.ItemContainerStyle>

xaml内のlistview内のスタイル指定はこんな感じ
実際の表示はこのように。