もくじ
やりたいこと
WPFで画面を作るときにコンバーターをよく使うが、都度既存のコードからコピペしていて身についた感がなかったので、テンプレートとして持っておくために下記にメモしておく。
コンバーターの作り方
今回は、MSの公式サンプルをもとに、
- DateTime型の値を、画面表示の際に日付の文字列に変換する
- 日付の文字列を、プロパティget時にDateTime型の値に変換する
というコンバーターを作ってみる。
コンバーターのクラスを作る
クラス名はDateTimeConverter
のように、〇〇Conveter
というようにするのが慣例っぽい。
その名前で、IValueConverterインターフェースを実装した下記のようなクラスを作る。
- Convertメソッドが「プロパティ→画面表示に反映時」の変換を記述するところ。
- ConvertBackメソッドが「画面表示→プロパティに反映時」の変換記述するところ。
public class DateTimeConverter : IValueConverter
{
// DateTimeを「yyyy/MM/dd HH:mm:ss.fff」形式の文字列に変換
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.ToString("yyyy/MM/dd HH:mm:ss.fff");
}
// 文字列をDateTimeに変換
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string strValue = value as string;
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))
{
return resultDateTime;
}
return DependencyProperty.UnsetValue;
}
}
※個人的経験では、ほとんどの場合「プロパティ→画面表示に反映時」に変換することしかしないので、Converter
メソッドだけ使ってConvertBack
メソッドはほとんど使ったことがない。
そういう場合は、ConvertBack
の中身は下記のようにしておけばいい。
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
画面にリソースとして登録し、それを変換したいところで使う
一番の親(今回はWindow)のリソースとして、下記のようにして登録する。
<Window.Resources>
<converter:DateTimeConverter x:Key="DateTimeConverter"/>
</Window.Resources>
そいつのKeyを使って、変換を行いたいコントロールにバインドしたプロパティのコンバーターとして使用する。
(今回は、ConverterParameterは別に不要だが、パラメータ渡せるということを示すために渡すだけ渡す)
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Dt, ElementName=root, Converter={StaticResource DateTimeConverter}, ConverterParameter=123}"/>
ViewModel等に、バインドするプロパティを作成して、get/setしてみる
下記のように、画面にバインドしたプロパティに
- set(今回は
Dt
に現在の日付をセットした) - get(今回は
Dt
の値をゲットしてyyyyねんMMがつddにち
形式で表示した)
public DateTime Dt
{
get { return _dt; }
set { _dt = value; OnPropertyChanged(nameof(Dt)); }
}
// テキストボックス ← プロパティ
private void Button_Click(object sender, RoutedEventArgs e)
{
// set
Dt = DateTime.Now;
}
// テキストボックス → プロパティ
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// get
AddLog(Dt.ToString("入力した日付は、yyyyねんMMがつddにち です"));
}
そうすると、コンバータを通って、下記のように画面に表示される。
以上で、一通りコンバーターが使えるようになった。
コード全部
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WpfApp48.converter
{
[ValueConversion(typeof(DateTime), typeof(string))]
public class DateTimeConverter : IValueConverter
{
// DateTimeを「yyyy/MM/dd HH:mm:ss.fff」形式の文字列に変換
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.ToString("yyyy/MM/dd HH:mm:ss.fff");
}
// 文字列をDateTimeに変換
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string strValue = value as string;
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))
{
return resultDateTime;
}
return DependencyProperty.UnsetValue;
}
}
}
<Window x:Class="WpfApp48.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:WpfApp48"
xmlns:converter="clr-namespace:WpfApp48.converter"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800"
Loaded="Window_Loaded"
Name="root">
<Window.Resources>
<converter:DateTimeConverter x:Key="DateTimeConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="1" Click="Button_Click"/>
<Button Content="2" Click="Button_Click_1"/>
</StackPanel>
<ListBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Logs, ElementName=root}"/>
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Dt, ElementName=root, Converter={StaticResource DateTimeConverter}, ConverterParameter=123}"/>
</Grid>
</Window>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WpfApp48
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
#region LogFramework
public ObservableCollection<string> Logs { get; set; } = new ObservableCollection<string>();
public void AddLog(string log)
{
DateTime now = DateTime.Now;
Logs.Add(now.ToString("hh:mm:ss.fff ") + log);
OnPropertyChanged(nameof(Logs));
}
#endregion
DateTime _dt = DateTime.Now;
public MainWindow() => InitializeComponent();
private void Window_Loaded(object sender, RoutedEventArgs e) { }
public DateTime Dt
{
get { return _dt; }
set { _dt = value; OnPropertyChanged(nameof(Dt)); }
}
// テキストボックス ← プロパティ
private void Button_Click(object sender, RoutedEventArgs e)
{
Dt = DateTime.Now;
}
// テキストボックス → プロパティ
private void Button_Click_1(object sender, RoutedEventArgs e)
{
AddLog(Dt.ToString("入力した日付は、yyyyねんMMがつddにち です"));
}
}
}
参考
How to: Convert Bound Data
https://docs.microsoft.com/ja-jp/dotnet/desktop/wpf/data/how-to-convert-bound-data?view=netframeworkdesktop-4.8