【WPF】DataGridに編集可能なComboBoxを表示するには?

  • 2014.03.01 Saturday
  • 14:06
JUGEMテーマ:コンピュータ
-----------------------------------------------------------------------------------
本投稿で紹介した内容に問題があったため以下の投稿で修正版を紹介しています。
【WPF】手入力不可能なComboBoxに初期値を表示するには?【修正版】
-----------------------------------------------------------------------------------

 久々にWPFネタを書いてみようと思います。今回は「DataGridに編集可能なComboBoxを表示するには?」というタイトルです。ターゲットは.NET Framework 4.5.1です。大昔にWPFTookit(最新版ではないと思います)で同じことを実現するコードは紹介したような気がしまうが、正式に.NET FrameworkがDataGridをサポートするようになってからは、技術情報を載せた記憶がないので、これが初めてだと思います。そんなことはどうでもいいのですが、重要な要点は、「過去の記事は古いから参考にならないよ」ってところですかね。まるで他人事みたいな言い方でごめんなさい。

 前置きは、これぐらいにしておいて、今回実現することは、以下の要件を満たすことです。

1.DataGridで値を表示する。
2.セルは編集可能であること。
3.編集中のセルはComboBox(ドロップダウンリストが表示されるコントロール)であること。
4.編集中のセルは手入力可能であること。

この要件を満たすにあたり技術的な問題が発生します。いや、無知だと発生するというだけです。何かしらの解決策をご存知の方は発生しません。いや、本当。無知な私は以下の問題が発生しました。

いろいろ試行錯誤したんですが、「いろいろ」なのでとりあえず、シンプルに書いたコードだけ紹介します。

<Window x:Class="ComboBoxDataGrid20140301_001.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:y="clr-namespace:ComboBoxDataGrid20140301_001"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 既定のDataGridのスタイル -->
        <Style TargetType="DataGrid">
            <Setter Property="AutoGenerateColumns" Value="False" />
            <Setter Property="CanUserAddRows" Value="False" />
            <Setter Property="CanUserDeleteRows" Value="False" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <DataGrid x:Name="SampleDataGrid">
            <DataGrid.Columns>
                <DataGridComboBoxColumn Header="Column1"
                                        TextBinding="{Binding Column1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                        />
            </DataGrid.Columns>
        </DataGrid>
       
        <Button Grid.Row="1" Click="OnContentChanged">メモリ上のデータを表示する。</Button>
    </Grid>
</Window>

using System.Data;
using System.Linq;
using System.Text;
using System.Windows;

namespace ComboBoxDataGrid20140301_001
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            var dataTable = new DataTable() { TableName = "SampleData" };
            dataTable.Columns.Add("Column1");
            dataTable.Columns.Add("Column2");
            dataTable.Columns.Add("Column3");

            Enumerable.Range(0, 10)
                .Select(n => new object[] { "A" + n, "B" + n, "C" + n })
                .ToList()
                .ForEach(item => dataTable.Rows.Add(item));

            SampleDataGrid.ItemsSource = dataTable.DefaultView;
        }

        private void OnContentChanged(object sender, RoutedEventArgs e)
        {
            string message = SampleDataGrid
                .ItemsSource
                .OfType<DataRowView>()
                .Select(rowView => string.Join(", ", rowView.Row.ItemArray))
                .Aggregate(new StringBuilder(), (sb, line) => sb.AppendLine(line))
                .ToString();
            MessageBox.Show(message);
        }
    }
}

上記のコードを実行してみると、要件を全然満たせていないことがわかります。

1.参照モードで値が表示されていない。
2.編集開始時に値が表示さていない。
3.編集中のComboBoxコントロールは手入力できない。

唯一編集中はセルがComboBoxになるという点だけ要件を満たせています。うん。どうしたものかといところです。この問題を解決するには、大きく分けて二つ手があると思います。DataGridComoBoxColumnのEditingElementStyleおよびElementStyleからStyleを変更し、要件を満たす。もう一つはDataGridComboBoxColumnの利用をやめて、DataGridTemplateColumnを利用する。今回はStyleを編集する方向で、実現することにしました。

変更したソースは以下です。

<Window x:Class="ComboBoxDataGrid20140301_001.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:y="clr-namespace:ComboBoxDataGrid20140301_001"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>

        <y:TextBlockComboBoxItemsSourceConverter x:Key="TextBlockComboBoxItemsSourceConverterKey" />

        <!-- 編集モード時のComboBox -->
        <Style TargetType="ComboBox" x:Key="EditingElementStyle">
            <Setter Property="IsEditable" Value="True" />
        </Style>

        <!-- 参照モード時のComboBox -->
        <Style TargetType="ComboBox" x:Key="TextBlockComboBoxStyle">
            <!--<Setter Property="Text" Value="{Binding Column1}" />-->
            <Setter Property="ItemsSource"
                    Value="{Binding .,
                            Converter={StaticResource TextBlockComboBoxItemsSourceConverterKey},
                            ConverterParameter=Column1}" />
        </Style>

        <!-- 既定のDataGridのスタイル -->
        <Style TargetType="DataGrid">
            <Setter Property="AutoGenerateColumns" Value="False" />
            <Setter Property="CanUserAddRows" Value="False" />
            <Setter Property="CanUserDeleteRows" Value="False" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <DataGrid x:Name="SampleDataGrid">
            <DataGrid.Columns>
                <DataGridComboBoxColumn Header="Column1"
                                        TextBinding="{Binding Column1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                                        EditingElementStyle="{StaticResource EditingElementStyle}"
                                        ElementStyle="{StaticResource TextBlockComboBoxStyle}"

                                        />
            </DataGrid.Columns>
        </DataGrid>

        <Button Grid.Row="1" Click="OnContentChanged">メモリ上のデータを表示する。</Button>
    </Grid>
</Window>

using System;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace ComboBoxDataGrid20140301_001
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            var dataTable = new DataTable { TableName = "SampleData" };
            dataTable.Columns.Add("Column1");
            dataTable.Columns.Add("Column2");
            dataTable.Columns.Add("Column3");

            Enumerable.Range(0, 10)
                .Select(n => new object[] { "A" + n, "B" + n, "C" + n })
                .ToList()
                .ForEach(item => dataTable.Rows.Add(item));

            SampleDataGrid.ItemsSource = dataTable.DefaultView;
        }

        private void OnContentChanged(object sender, RoutedEventArgs e)
        {
            string message = SampleDataGrid
                .ItemsSource
                .OfType<DataRowView>()
                .Select(rowView => string.Join(", ", rowView.Row.ItemArray))
                .Aggregate(new StringBuilder(), (sb, line) => sb.AppendLine(line))
                .ToString();
            MessageBox.Show(message);
        }
    }

    public class TextBlockComboBoxItemsSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string columnName = (parameter as string) ?? string.Empty;
            var dataRowView = value as DataRowView;
            if (dataRowView == null
                || dataRowView.Row == null
                || !dataRowView.Row.Table.Columns.Contains(columnName))
            {
                return DependencyProperty.UnsetValue;
            }
            return new[] { dataRowView.Row[columnName] };
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

}

すべてのを説明するのは面倒なので、要点だけ書くと

1.ComboBoxを手入力可能な設定にした。IsEditable=True
2.参照モードのComboBoxに値を表示できる値は、候補の値に限られているため
  参照モードのみ候補に値を一件仕込んだ。
  <Setter Property="ItemsSource" ... />

 上記のことをすれば、参照モードでは、値が表示され、編集中は手入力可能なComboBoxが表示されるようになります。補足としては、データソースにDataTableを利用しています。それぞれのカラムにプリミティブな値しか設定しませんが、データソース側に多少の努力を加えると、IValueConverterあたりの下りは不要になるかもしれません。この辺は開発・保守の観点から費用対効果で結論を出すべきところですかね。今回は、知ってる知識で最短で組める方法でがんばってみました。


 今回はComboBoxに候補を出すということはしていませんが、これに関しても色々方法があり、満たしたい要件で実装方法、難易度(知ってれば難易度なんてあがりませんが)が上がります。

 以上で、今回は以上で終わります。久々にBlogを書くこと書き方忘れますね。

-----------------------------
(追記 2014/03/05)
Styleに不要なSetterがあったため修正しました。
誤)
<Setter Property="Text" Value="{Binding Column1}" />
正)
<!--<Setter Property="Text" Value="{Binding Column1}" />-->

TextBindingで上書きされる項目なので指定しても無効となります。

コメント
管理者の承認待ちコメントです。
  • -
  • 2018/05/24 10:53 AM
コメントする








    
この記事のトラックバックURL
トラックバック

calendar

S M T W T F S
      1
2345678
9101112131415
16171819202122
23242526272829
30      
<< September 2018 >>

あわせて読みたい

あわせて読みたいブログパーツ

selected entries

categories

archives

recent comment

  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    art55 (05/22)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    分解大好き (05/18)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/04)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    Gen (02/04)
  • 【キーボード】RealForce が壊れて帰ってきた。
    art55 (04/29)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/23)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    かるあ (02/22)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    art55 (01/16)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    karuakun (01/16)
  • 【NetOffice】【Excel】死なないExcelプロセスをKillする。
    art55 (12/05)

recent trackback

recommend

recommend

recommend

C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION)
C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION) (JUGEMレビュー »)
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー

recommend

ろんりと集合
ろんりと集合 (JUGEMレビュー »)
中内 伸光
とてもわかりやすいです。

recommend

recommend

シャノン・ノイマン・ディジタル世界
シャノン・ノイマン・ディジタル世界 (JUGEMレビュー »)
市川 忠男
4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。

recommend

recommend

東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS
東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS (JUGEMレビュー »)

テンキーレス、静音のRealForce91UBK-S。スコスコ感がたまらなく気持ちいいです。家と会社で2台持ってます。

recommend

recommend

プログラミング.NET Framework 第4版 (プログラミングシリーズ)
プログラミング.NET Framework 第4版 (プログラミングシリーズ) (JUGEMレビュー »)
Jeffrey Richter
発売予定美 2013年10月10日。.NET Frameworkとお付き合いする人のバイブルですね。

recommend

recommend

キャット・シッターの君に。
キャット・シッターの君に。 (JUGEMレビュー »)
喜多嶋 隆
私のイラストレータデビュー本です。

recommend

Essential .NET ― 共通言語ランタイムの本質
Essential .NET ― 共通言語ランタイムの本質 (JUGEMレビュー »)
ドン・ボックス,クリス・セルズ,Don Box,Chris Sells,吉松 史彰

links

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM