【WPF】手入力不可能なComboBoxに初期値を表示するには?【修正版】

  • 2014.03.11 Tuesday
  • 23:01
JUGEMテーマ:コンピュータ

Source and Project

.NET Framework 4.5.1を利用して、DataGrid中のセルに手入力可能なComboBoxを表示させる方法を先週紹介しましたが、問題があることが分かったので、今回は修正版のソースコードを紹介したいと思います。

以前紹介した投稿は以下となります。
【WPF】DataGridに編集可能なComboBoxを表示するには?

問題点の指摘は以下の投稿となります。
【WPF】「手入力不可能なComboBoxに初期値を表示するには?」の問題点

問題点は、挙動のおかしさを指摘したにとどまっていますが、実装・設計面から見てもStyleの定義でテーブルのカラム名を指定しているなど、ちょっと汎用性に欠ける点も問題点といえば問題点ではないかと思います。今回はそれを含めて解決してみました。

ソースコードを紹介します。

<Window x:Class="Art55.DataGridComboBoxDemo20140311_01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:demo="clr-namespace:Art55.DataGridComboBoxDemo20140311_01"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{StaticResource MainWindowViewModelKey}">
    <Window.Resources>
        <demo:FromTextToItemsSourceConvter x:Key="FromTextToItemsSourceConvterKey" />
        <demo:FromItemsSourceToSelectedIndexConverter x:Key="FromItemsSourceToSelectedIndexConverterKey" />

        <Style TargetType="ComboBox" x:Key="EditingElementStyleKey">
            <Setter Property="IsEditable" Value="True" />
        </Style>

        <Style TargetType="ComboBox" x:Key="ElementStyleKey">
            <Setter Property="ItemsSource"
                    Value="{Binding Path=Text,
                                    RelativeSource={RelativeSource Self},
                                    Converter={StaticResource FromTextToItemsSourceConvterKey}}" />
            <Setter Property="SelectedIndex"
                    Value="{Binding Path=ItemsSource,
                                    RelativeSource={RelativeSource Self},
                                    Converter={StaticResource FromItemsSourceToSelectedIndexConverterKey},
                                    Mode=OneWay}"></Setter>
        </Style>

    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <DataGrid CanUserAddRows="False"
                  CanUserDeleteRows="False"
                  AutoGenerateColumns="False"
                  DataContext="{Binding Path=Source}"
                  ItemsSource="{Binding .}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="No" Binding="{Binding No}"></DataGridTextColumn>
                <DataGridComboBoxColumn Header="ComboBox Column"
                                        TextBinding="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
                                        EditingElementStyle="{StaticResource EditingElementStyleKey}"
                                        ElementStyle="{StaticResource ElementStyleKey}">

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

        <Button Grid.Row="1" Command="{Binding RejectChangedAllRowsCommand}">変更取消</Button>
    </Grid>
</Window>

using System;
using System.ComponentModel;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using Art55.DataGridComboBoxDemo20140311_01.Annotations;

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

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public MainWindowViewModel()
        {
            _source = new DataTable {CaseSensitive = true};
            _source.Columns.Add("No", typeof(int));
            _source.Columns.Add("Value");
            _source.Rows.Add(new object[] { 1, DBNull.Value });
            _source.Rows.Add(new object[] { 2, "A2" });
            _source.Rows.Add(new object[] { 3, "A3" });
            _source.Rows.Add(new object[] { 4, DBNull.Value });
            _source.Rows.Add(new object[] { 5, DBNull.Value });
            _source.Rows.Add(new object[] { 6, "A6" });
            _source.Rows.Add(new object[] { 7, "A7" });
            _source.Rows.Add(new object[] { 8, DBNull.Value });

            _source.AcceptChanges();

            RejectChangedAllRowsCommand = new CommandObject(this,
                _ => _source.RejectChanges());
        }

        public ICommand RejectChangedAllRowsCommand { get; private set; }

        // TODO コマンドを用意してDataTableを操作してみると面白いよ。

        private readonly DataTable _source;

        public DataTable Source
        {
            get { return _source; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class FromTextToItemsSourceConvter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || value == DependencyProperty.UnsetValue)
            {
                return DependencyProperty.UnsetValue;
            }
            return new[] {value};
        }

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

    public class FromItemsSourceToSelectedIndexConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var items = value as object[];
            return items == null ? -1 : 0;
        }

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


    public class CommandObject : ICommand
    {
        public CommandObject(INotifyPropertyChanged viewModel, Action<object> execute)
            : this(viewModel, execute, null)
        {
        }

        public CommandObject(INotifyPropertyChanged viewModel, Action<object> execute, Func<object, bool> canExecute)
        {
            if (viewModel == null)
                throw new ArgumentNullException("viewModel");

            if (execute == null)
                throw new ArgumentNullException("execute");

            viewModel.PropertyChanged += OnViewModelPropertyChanged;
            _execute = execute;
            _canExecute = canExecute;
        }

        void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            CheckCanExecute();
        }

        public void CheckCanExecute()
        {
            InvokeCanExecuteChanged(new EventArgs());
        }

        public void Execute(object parameter)
        {
            _execute.Invoke(parameter);
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute != null)
                return _canExecute.Invoke(parameter);
            return true;
        }

        public event EventHandler CanExecuteChanged;

        private void InvokeCanExecuteChanged(EventArgs e)
        {
            EventHandler changed = CanExecuteChanged;
            if (changed != null)
                changed(this, e);
        }

        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;
    }
}

ポイントは、赤字で書いた部分です。

        <Style TargetType="ComboBox" x:Key="EditingElementStyleKey">
            <Setter Property="IsEditable" Value="True" />
        </Style>

        <Style TargetType="ComboBox" x:Key="ElementStyleKey">
            <Setter Property="ItemsSource"
                    Value="{Binding Path=Text,
                                    RelativeSource={RelativeSource Self},
                                    Converter={StaticResource FromTextToItemsSourceConvterKey}}" />
            <Setter Property="SelectedIndex"
                    Value="{Binding Path=ItemsSource,
                                    RelativeSource={RelativeSource Self},
                                    Converter={StaticResource FromItemsSourceToSelectedIndexConverterKey},
                                    Mode=OneWay}"></Setter>
        </Style>


1.編集中は、ComboBox.IsEditableにTrueを指定する。
2.参照中は、ComboBox.Textの変更を監視し、変更のあったタイミングでItemsSourceに入力文字を設定する。
3.参照中は、ComboBox.ItemsSourceを監視し、ComboBox.ItemsSourceの数に応じてSelectedIndexを設定する。

監視対処が

Text ← ItemsSource ← SelectedIndex

と、XAMLで定義しました。以前書いたコードは、DataRowの特定のカラム値やDataRowViewの変更を監視していたりと、タイミングに不整合が起きていたのですが、今回は、それを解消してみました。

 手入力可能なComboBoxを表示させる方法はいくつかあります。前に書いた方法としてDataGridTemplateColumnを利用するというのも一つの手です。WpfToolkit時代はDataGridComboBoxColumnは編集時はComboBox、参照時はTextBlockを利用していたと記憶していますが、DataGridTemplateColumnを利用すれば、近いものが実装できると思います。
 それと、今回の実装では、まだ機能的にはお粗末です。すぐにわかるのが、一つは入力開始制御がF2とダブルクリック以外でできていない点です。ほかにもあるかもしれません。という事で、ないと思いますが、このコードを利用する場合は注意してください。

Source and Project

コメント
コメントする








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

calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
<< June 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