17
20

More than 1 year has passed since last update.

[C#/WPF] コンバーターの書き方

Last updated at Posted at 2020-12-23

もくじ

やりたいこと

WPFで画面を作るときにコンバーターをよく使うが、都度既存のコードからコピペしていて身についた感がなかったので、テンプレートとして持っておくために下記にメモしておく。

コンバーターの作り方

今回は、MSの公式サンプルをもとに、

  • DateTime型の値を、画面表示の際に日付の文字列に変換する
  • 日付の文字列を、プロパティget時にDateTime型の値に変換する

というコンバーターを作ってみる。

コンバーターのクラスを作る

クラス名はDateTimeConverterのように、〇〇Conveterというようにするのが慣例っぽい。
その名前で、IValueConverterインターフェースを実装した下記のようなクラスを作る。

  • Convertメソッドが「プロパティ→画面表示に反映時」の変換を記述するところ。
  • ConvertBackメソッドが「画面表示→プロパティに反映時」の変換記述するところ。
DateTimeConverter.cs
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)のリソースとして、下記のようにして登録する。

MainWIndowの一部.xaml
<Window.Resources>
    <converter:DateTimeConverter x:Key="DateTimeConverter"/>
</Window.Resources>

そいつのKeyを使って、変換を行いたいコントロールにバインドしたプロパティのコンバーターとして使用する。
(今回は、ConverterParameterは別に不要だが、パラメータ渡せるということを示すために渡すだけ渡す)

MainWIndowの一部.xaml
<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にち形式で表示した)
MainWindow.xaml.cs
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にち です"));
}

そうすると、コンバータを通って、下記のように画面に表示される。
image.png

以上で、一通りコンバーターが使えるようになった。

コード全部

DateTimeConverter.cs
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;
        }
    }
}
MainWindow.xaml
<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>

MainWindow.xaml.cs
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

17
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
20