配信タイトルの反映用プログラムの画面
これを例に ListView の使い方を説明する
ListView の設置
xaml に ListView を追記する
下記は 先述のプログラムの中身
<Window x:Class="CSharp_OBSEdit.MainWindow"
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:CSharp_OBSEdit"
mc:Ignorable="d"
Loaded="Event_Loaded"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Name="fbApply" Content ="Apply" Click="Event_fbApply_Click"/>
</StackPanel>
<ListView Name="fList">
<ListView.View>
<GridView>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}" />
<GridViewColumn Header="Subtitle" DisplayMemberBinding="{Binding Subtitle}" />
</GridView>
</ListView.View>
</ListView>
</DockPanel>
</Grid>
</Window>
レイアウトの都合で Dockpanel も使っているが、必要なのは ListView タグから
必要な部分のみ再掲して解説する
<ListView Name="fList">
<ListView.View>
<GridView>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}" />
<GridViewColumn Header="Subtitle" DisplayMemberBinding="{Binding Subtitle}" />
</GridView>
</ListView.View>
</ListView>
ListView タグ
ListView 自体の枠
内容は次から記載していく
属性 Name は プログラム中で使う
ListView.View タグ
決め事、GridView を使うために
GridView タグ
GridView (複数列) を扱うために記述する
GridViewColumn タグ
GridView のうち、列を構成する
後述する 「モデル」という要素のうち、どれを GridView の項目に Bindするかも指定する
属性 Header は 列名称に
属性 DisplayMemberBinding は 例を参考に、後述の「モデル」の対応する項目名を指定する
{Binding <モデルの項目名>}
モデル
最近は MVC (Model/View/Controller) の概念が流行りらしく、C#も似たようなことをする
下記は ListView に Bind するために作ったモデル
余談だが Json からの読み込みにも対応するための構造でもあるので、階層が作られている
Json読み込みについても調べ回ったので、これは別記事にしようと思う
using System.Text.Json.Serialization;
namespace CSharp_OBSEdit
{
public class settingsJson
{
[JsonPropertyName("texts")]
public IList<childTexts> Texts{get; set;}
}
public class childTexts
{
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("subtitle")]
public string Subtitle { get; set; }
[JsonPropertyName("thumbnail")]
public string Thumbnail {get; set; }
}
要は構造を定義する
今回のListView で使っているモデルは childTexts クラスになる
JsonPropertyName や settingsJson クラスは
プログラムのソースコードからそのまま持ってきて記載しているだけで当記事では関係はない
必要な部分のみ抜粋すると下記の通り
public class childTexts
{
public string Title { get; set; }
public string Subtitle { get; set; }
public string Thumbnail {get; set; }
}
クラスの中に public で変数を定義する
これが構造になる
ListView にデータを追記する
長いが当プログラムのソースコードを提示する
一部開示してはいけない情報があるので伏せている
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
namespace CSharp_OBSEdit;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<childTexts> fListData;
public MainWindow()
{
fListData = new ObservableCollection<childTexts>();
}
private void Event_Loaded(object sender, RoutedEventArgs e){
try{
var oFileJson = new StreamReader(@"※Jsonのファイルパス",System.Text.Encoding.UTF8);
var vJson = oFileJson.ReadToEnd();
var oJson = System.Text.Json.JsonSerializer.Deserialize<settingsJson>(vJson);
// 仕様:VSCodeだとエラーになるが無視して正常にコンパイルできる
// 仕様:コンストラクタでマウントすると 起動に失敗する
fList.ItemsSource = fListData;
foreach(childTexts oJsonLine in oJson.Texts){
fListData.Add(oJsonLine);
}
}catch(Exception le){
MessageBox.Show(le.ToString());
}
}
private void Event_fbApply_Click(object sender, RoutedEventArgs e){
var oItems = fList.SelectedItems;
//ダウンキャスト
var oItemLine = oItems[0] as childTexts;
var oFileTitle = new StreamWriter(@"※書き出し",false,System.Text.Encoding.UTF8);
oFileTitle.WriteLine(oItemLine.Title);
oFileTitle.Close();
var oFileSubTitle = new StreamWriter(@"書き出し",false,System.Text.Encoding.UTF8);
oFileSubTitle.WriteLine(oItemLine.Subtitle);
oFileSubTitle.Close();
try{
File.Copy(@"元ファイルの場所" + oItemLine.Thumbnail, @"コピー先",true);
}catch(Exception le){
MessageBox.Show(le.ToString());
}
MessageBox.Show("適用しました。");
}
}
このサンプルから説明する
まず、ListView の値追加の前に必要な手続きがいくらかある
public partial class MainWindow : Window
{
ObservableCollection<childTexts> fListData;
public MainWindow()
{
fListData = new ObservableCollection<childTexts>();
}
private void Event_Loaded(object sender, RoutedEventArgs e){
:中略
fList.ItemsSource = fListData;
:以下略
ListView に表示するためのデータの構造を作り、マウントする手順となる
この場合、childTexts クラス (先のモデル) で作られた ObservableCollection 型の fListdata を、ListView の ItemsSource プロパティに代入することでマウントしている
これで以後 fListData に値を追加することで、ListView に反映されていく
foreach(childTexts oJsonLine in oJson.Texts){
fListData.Add(oJsonLine);
}
追加は Add メソッドで行う
上記は ループ中での全要素を追加するために foreach で代入されたものを追加している
もちろん 別で childTexts クラスを用意して 各値を代入し、Addに渡しても良い
これで画面に値が出る
プログラム中で ListView で選択された値を取る
先の処理の、ボタンを押下時の処理がそう
抜粋すると下記の通り
private void Event_fbApply_Click(object sender, RoutedEventArgs e){
var oItems = fList.SelectedItems;
//ダウンキャスト
var oItemLine = oItems[0] as childTexts;
var oFileTitle = new StreamWriter(@"※書き出し",false,System.Text.Encoding.UTF8);
oFileTitle.WriteLine(oItemLine.Title);
oFileTitle.Close();
var oFileSubTitle = new StreamWriter(@"書き出し",false,System.Text.Encoding.UTF8);
oFileSubTitle.WriteLine(oItemLine.Subtitle);
oFileSubTitle.Close();
try{
File.Copy(@"元ファイルの場所" + oItemLine.Thumbnail, @"コピー先",true);
}catch(Exception le){
MessageBox.Show(le.ToString());
}
MessageBox.Show("適用しました。");
}
各処理中で要素を使っているので、無関係なものが多量入ってしまっているが、
そこは無視するなりで読んでほしい
重要なのは oItemLine のみ
選択されている値は fList (xamlのNameタグで指定したもの) の SelectedItems で取得できるが、
これは Object 型となっている
このままだと中身を解釈できない
※余談:Visual Basic だったら SelectedIndex とかあってやりやすかったのだが、なぜ不便にしたのか・・・
そこで「ダウンキャスト」という方法を用いて Object型という曖昧な型から、明確な型に変換する
今回は使われている構造がわかっているので、それでダウンキャストを行う
上記ソースコード中、ダウンキャストを行っているのは下記処理
var oItemLine = oItems[0] as childTexts;
SelectedItems は複数選択可能なので、配列を持っている
このプログラムでは1つ目だけが必要なので [0] を持ってきている
それを as <モデル> (今回は childTexts クラス) でダウンキャストしている
こうすることで、選択された値の、各値にアクセスできるようになる
childTexts クラスには Titls、Subtitle、Thumbnail を定義したので、
例えば下記のように値を用いている
oFileTitle.WriteLine(oItemLine.Title);