1

アプリケーションでさまざまな測定値を表示するために、データ オブジェクトにバインドされた DataGridTextColumn を利用しています。測定値をさまざまな単位で表示できる機能を実装しようとしていますが、バインディングのコンバーターが最適なオプションであると考えました。

これはほとんどのアプリケーションでうまく機能しますが、ユーザーがフィールドの値を編集できる DataGrid に別のフィールドがあります。この場合、ユーザーから入力された値 (たとえば華氏) をデータ オブジェクトの既定の単位 (摂氏) に変換する双方向のデータ バインディングがあります。

Converter がデータ オブジェクトからセルシウスの値を取得し、変換を行ってから、この値 (華氏) を DataGridTextColumn に送信することを期待します。ただし、データ オブジェクトに値を設定し、PropertyChanged を通知した後、そのプロパティの「get」メソッドは呼び出されません。代わりに、Convert はユーザーが入力した値 (華氏) を DataGridTextColumn から取得しているようです。

これは通常の動作ですか?もしそうなら、Convert が常にその値をデータ オブジェクトに戻すように強制する方法はありますか?

役立ついくつかのスニペット:

<Window.Resources>
    <myProject:ConvertMetricValuesToEnglish x:Key="ValueMToE" />
</Window.Resources>
.
.
.
<DataGridTextColumn x:Name="maxCol" Header="Maximum" Width="100">
    <DataGridTextColumn.Binding>
        <MultiBinding Mode="TwoWay" StringFormat='0.#####' Converter="{StaticResource ValueMToE}" >
            <Binding Path="UserMax" Mode="TwoWay" />
            <Binding Path="Unit" Mode="OneWay" />
        </MultiBinding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>

コンバーターの方法:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    if (values.Length != 2)
    {
        return null;
    }
    if (values[0].GetType() != typeof(double) || values[1].GetType() != typeof(string))
    {
        return values[0];
    }
    double d = (double)values[0];
    string s = (string)values[1];

    return ConvertValueToEnglish(d, s);
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
    return new object[] { StringToDouble((string)value), Binding.DoNothing };
}

ConvertBack で利用できる情報が限られているため、セッターで「ConvertBack」変換を行います。

    private double? _userMax;
    public double UserMax
    {
        get
        {
            if (_userMax == null)
            {
                _userMax = Max;
            }

            return (double)_userMax;
        }
        set
        {
            if (Metric)
            {
                if (value == _userMax)
                {
                    return;
                }
                _userMax = value;
            }
            else
            {
                double num = ConvertValueToMetric(value, _unit);
                if (num == _userMax)
                {
                    return;
                }
                _userMax = num;
            }
            OnPropertyChanged("UserMax");
        }
    }

編集:これは、問題を示すサンプル プロジェクトへのリンクです。左の列はユーザ​​ーが編集可能で、ユーザーが表示/編集したデータを表します。右側の列には、ユーザーが変更を加えたときに実際に保存される基礎となる値が表示されます (この値は決して変更されません)。

左の列の値を変更し、同じ行の別の列を [Tab] キーで移動するか選択すると、入力した値から値が変更されていることがわかります。ただし、行がフォーカスを失うと元に戻ります。ブレークポイントを設定すると、セル自体がフォーカスを失ったときに、DependencyProperty.UnsetValue を使用して Convert メソッドが呼び出され、次に Unit の get メソッドが呼び出され、続いて DataGrid の値を使用して Convert メソッドが再度呼び出されることがわかります。

TempConverter.zip

4

1 に答える 1

2

自力で解決することができました。問題は、DataGrid の行全体がフォーカスを失うまで、ソース データ オブジェクトが更新されないことでした。これを修正するために、TextBox を ControlTemplate としてセルに適用し、TextBox がフォーカスを失うたびに UpdateSource をトリガーするイベントを設定しました。

XAML:

<DataGridTextColumn x:Name="maxCol" Header="Maximum" Width="100">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <TextBox LostFocus="TextBlock_LostFocus">
                            <TextBox.Text>
                                <MultiBinding StringFormat='0.#####' Converter="{StaticResource ValueMToE}" UpdateSourceTrigger="Explicit">
                                    <Binding Path="UserMax" Mode="TwoWay" />
                                    <Binding Path="Unit" Mode="OneWay" />
                                </MultiBinding>
                            </TextBox.Text>
                        </TextBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

イベントハンドラ:

private void TextBlock_LostFocus(object sender, RoutedEventArgs e)
{
    TextBox b = sender as TextBox;
    if (b == null)
    {
        return;
    }
    MultiBindingExpression mb = BindingOperations.GetMultiBindingExpression(b, TextBox.TextProperty);
    mb.UpdateSource();
}
于 2013-09-25T20:26:56.057 に答える