【WPF】「手入力不可能なComboBoxに初期値を表示するには?」の問題点

  • 2014.03.08 Saturday
  • 07:41
JUGEMテーマ:コンピュータ

【WPF】手入力不可能なComboBoxに初期値を表示するには?
http://pro.art55.jp/?eid=1304202


上記の記事でDataGrid中に編集可能なComboBoxを表示させる方法を紹介しましたが、一部問題があることが分かったので、今回は、問題点を考察したいと思います。解決案は別途投稿する予定です。

では、問題点をあげます。

・操作によって参照モードで値があるにも関わらず、値が表示されないことがある。

ということが、わかりました。操作方法は以下の通り。

1.適当にセルを編集する。
2.編集したセルが見えないようにDataGridをスクロールする。
3.編集したセルの値をもとに戻す(DataRow.RejectChangedメソッドを呼び出す)
4.もとに戻したセルを表示させ、もう一度編集する。
5.編集したセルを表示させた状態で、セルの値をもとに戻す。

そうすると、編集したセルに値が空になります。この問題がどうして発生しているかというのが今回の本題となります。RejectChangedを呼び出す処理が入った状態のコードを以下に紹介します。

 

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

        <editableComboBoxDataGrid2014030802: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="OnSelectedRowsRejectChanges">選択行の値を元に戻す。</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 Art55.EditableComboBoxDataGrid20140308_02
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

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

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

            dataTable.AcceptChanges();

            SampleDataGrid.ItemsSource = dataTable.DefaultView;
        }

        private void OnSelectedRowsRejectChanges(object sender, RoutedEventArgs e)
        {
            SampleDataGrid
                .SelectedItems
                .OfType<DataRowView>()
                .Select(rowView => rowView.Row)
                .Where(row => row != null)
                .Where(row => row.RowState == DataRowState.Modified)
                .ToList()
                .ForEach(row => row.RejectChanges());
        }
    }

    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();
        }
    }
}

上記のコードのどこが問題かというとDataGridComboBoxColumn.ElementStyle用に用意したStyleです。

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

        </Style>

ここのSetter部分ですが、DataRowViewを監視し、DataRowViewに変更があった際に、コンバートした値をComboBox.ItemsSourceプロパティに設定するというBindingを定義しています。問題なのはDataRowViewを監視しているという点です。DataRowView["Column1"]の値の変更を監視しているわけではないという点です。DataGridComboBox.TextBindingにより、編集中のComboBoxも編集中でないComboBoxもTextプロパティはDataRowView["Column1"]を監視しているのですが、DataRowView自体を監視しているわけではありません。よって、ComboBox.TextとComboBox.ItemsSourceの同期が取れていないという事になり、たとえばComboBox.Textには何らかの値があるにも関わらずComboBox.ItemsSourceに候補がないとか、ComboBox.ItemsSourceに値はあるけどComboBox.Textと不一致になるという問題が発生しうる事になります。

なので、今回見つけた問題が発生することになります。修正しなければならないのですが、今回の投稿は問題点を指摘するだけにとどめておきます。また、次回。
コメント
管理者の承認待ちコメントです。
  • -
  • 2018/06/08 12:37 PM
コメントする








    
この記事のトラックバック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