Hello~! ミョウガです!
早くもニアは、俳句カードを出力するVBAマクロを作ったみたい。
わたしだって負けませんっ!
ということで、今回は俳句を縦書きで表示するWPFアプリを作ってみました。
目次
1. WPF(言語はC#とXAML)で俳句を縦書きで表示してみたよ
まずは、俳句を表示するXAMLファイルと文字列を縦書きの俳句変換するC#ソースコードを作りましたよっ!
<Window x:Class="WPFHaiku.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:loc="clr-namespace:WPFHaiku" Title="俳句" Height="450" Width="230" ResizeMode="NoResize"> <Window.Resources> <!-- 俳句のテキストの設定です。 --> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="50"/> <Setter Property="FontFamily" Value="HGSGyoshotai"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </Window.Resources> <!-- WindowのデータコンテキストにHaikuクラスを設定します。 --> <Window.DataContext> <loc:Haiku/> </Window.DataContext> <Border Background="DarkGreen"> <Grid Margin="10" Background="Ivory"> <!-- 横に3分割します。 --> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <!-- 俳句を表示します。 --> <TextBlock Grid.Column="2" Text="{Binding Phrase[0]}"/> <TextBlock Grid.Column="1" Text="{Binding Phrase[1]}"/> <TextBlock Grid.Column="0" Text="{Binding Phrase[2]}"/> </Grid> </Border> </Window>
using System.Text; using System.Windows; namespace WPFHaiku { // このクラスをWindow.DataContextに設定します。 class Haiku { // 俳句を格納する文字列です。TextBlock.Textにバインディングします。 public string[] Phrase { get; set; } // 文字列を縦書きになるようにカンマ以外の各文字に改行記号を入れて、 // カンマで区切ります。 public Haiku() { string s = "初桜,折しも今日は,よき日なり"; StringBuilder sb = new StringBuilder( s ); for( int i = sb.Length - 1; i >= 0; i-- ) if( sb[i] != ',' ) sb.Insert( i + 1, "\n" ); Phrase = sb.ToString().Split( ',' ); } } public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
※このプログラムは https://gist.github.com/Myoga1012/c61584bf5a408af1129c で公開されています。
動作としてXAML側では、Grid.ColumnDefinition系でウィンドウ(というよりは、ウィンドウ内に設置したGrid)を横3つに分割して、それぞれ中にTextBlockを設置し、C#側では、文字列をカンマ以外の各文字の後に改行記号「\n」を入れた後、カンマで区切って、文字列配列のPhraseプロパティに代入します。こうすることで、各句の文字列が縦書きになります。
文字列を数回以上操作する時は、StringBuilderクラスを使うと効率がよいですよっ!
あとは、TextBlockのTextプロパティを先ほどのPhraseプロパティでバインディングします。WindowのデータコンテキストにHaikuクラスを設定することを忘れずにね。
2. ユーザーから俳句を入力できるようにしてみよう
せっかくなので、ユーザーからテキストボックスに俳句を入力し、ウィンドウに俳句をリアルタイムで表示できるようにしてみましたっ!
<Window x:Class="WPFHaiku.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:loc="clr-namespace:WPFHaiku" Title="俳句" Height="500" Width="230" ResizeMode="NoResize"> <Window.Resources> <!-- 俳句のテキストの設定です。 --> <Style x:Key="HaikuText" TargetType="TextBlock"> <Setter Property="FontSize" Value="50"/> <Setter Property="FontFamily" Value="HGSGyoshotai"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> <!-- 文字列を縦書きに変換するコンバーターです。 --> <loc:HaikuConverter x:Key="HaikuConverter"/> </Window.Resources> <Grid Background="DarkGreen"> <!-- 上部を俳句、下部を入力ボックスとします。 --> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <!-- 俳句を入力するテキストボックスです。 --> <TextBox Grid.Row="1" x:Name="HaikuInput" Margin="5"/> <!-- 俳句を表示するエリアです。 --> <Border Grid.Row="0"> <Grid Margin="10" Background="Ivory"> <!-- HaikuConverterを使って、HaikuInputに入力した文字列をカンマで区切り、 要素の並びを反転した時のIEnumerable<string>型のリストに変換します。 これをItemsControlのItemsSourceにバインディングし、StackPanelを使って TextBlockを横方向に並べます。 --> <ItemsControl ItemsSource="{Binding Text, Converter={StaticResource HaikuConverter}, ElementName=HaikuInput, Mode=OneWay}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock Style="{StaticResource HaikuText}" Margin="5" Text="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Border> </Grid> </Window>
using System; using System.Windows.Data; using System.Globalization; using System.Text; using System.Linq; namespace WPFHaiku { // 文字列をカンマで区切り、リストを反転するコンバーターです。 class HaikuConverter : IValueConverter { // このプログラムではConvertを使用します。 public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { // StringBuilderのインスタンスを生成し、コンバーターソースの文字列で初期化します。 StringBuilder sb = new StringBuilder( ( string )value ); // カンマ以外の文字の後に改行記号を挿入します。 for( int i = sb.Length - 1; i >= 0; i-- ) if( sb[i] != ',' ) sb.Insert( i + 1, "\n" ); // カンマで分割し、リストを反転します。 return sb.ToString().Split( ',' ).Reverse(); } // ConvertBackは使用しません。 public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { return null; } } }
※Haiku2.xaml用の分離コードはコンポーネントの初期化のみなので省略します。
※このプログラムは https://gist.github.com/Myoga1012/c61584bf5a408af1129c で公開されています。
動作としてXAML側では、入力用のTextBoxと俳句を表示するためのItemsControlを設置します。ItemsSourceプロパティをTextBoxのTextプロパティでバインディングして、StackPanelで水平方向に並べたTextBlockのTextにItemsSourceの各要素を設定するのですが、その文字列を俳句用に変換するために、C#側で作成したHaikuConverterクラスを使います。
HaikuConverterクラスではIValueConverterインターフェースを継承して、ConvertメソッドとConvertBackメソッドを実装していきます。今回はTextBox.Text→ItemsControl.ItemsSourceへの一方通行なので、Convertメソッドの方をコーディングしていきます。
TextBoxで入力した文字列がConvertメソッドのvalueに渡されるので、文字列型にキャストしてStringBuilderクラスのコンストラクターに渡します。そしてHaiku.csと同じ様に、各文字の後ろに改行記号を入れてカンマで分割し、文字列の配列にします。
この状態でItemsSourceに渡すと、StackPanel上で左から順に句が並び、左縦書きのように表示されてしまいます。そこでReverseメソッドを呼び出し、要素の並びを反転します。
最終的にIEnumerable<string>型になりますが、ターゲットであるItemsSourceのIEnumerable型です。IEnumerable<T>インターフェースはIEnumerableインターフェースを継承しているので問題ナッシングですよっ!
ちなみに、C#の配列はIEnumerable<T>とIEnumerableを実装したArray型の派生なので、Splitメソッドの戻り値(文字列の配列)をItemsSourceに渡しても大丈夫だよ。
実行結果はこんな感じ。
やっぱWPFでプログラミングするなら、データバインディングしたいよねっ!
ではでは、See youですよっ♪
3. 【追記】String.JoinとReplaceでHaikuConverterを最適化
各文字の後に改行記号を入れるもう1つの手段として、TextBoxから取得した文字列をToCharArrayメソッドで一旦char型配列にばらし、String.Joinメソッドで改行記号で区切った文字列を生成する方法を思いつきました。
using System; using System.Windows.Data; using System.Globalization; using System.Linq; namespace WPFHaiku { // 文字列をカンマで区切り、リストを反転するコンバーターです。 class HaikuConverter : IValueConverter { // このプログラムではConvertを使用します。 public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { // 文字列をchar型配列に一旦分割し、各文字を改行記号で区切った文字列を生成します。 // カンマの直後の改行記号を削除してからカンマで分割し、リストを反転します。 return string.Join( "\n", ( ( string )value ).ToCharArray() ).Replace( ",\n", "," ).Split( ',' ).Reverse(); } // ConvertBackは使用しません。 public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { return null; } } }
あとは同じようにカンマで分割して要素の並びを反転すればよいのだけど、句の先頭にある改行記号を取り除くために、分割する前にReplaceメソッドでカンマの直後の改行記号を取っ払います。
これで変換処理が1文になり、スッキリしましたよっ!