1

我正在利用绑定到数据对象的 DataGridTextColumn 来显示应用程序中的各种测量值。我正在尝试实现一个允许以不同单位显示测量值的功能,并认为绑定中的转换器可能是我的最佳选择。

这适用于大多数应用程序,但我在 DataGrid 中有另一个字段,用户可以在其中编辑字段的值。在这种情况下,我有一个双向数据绑定,它将用户输入的值(比如说华氏度)转换回数据对象的默认单位(摄氏度)。

我希望转换器从数据对象中检索摄氏度的值,进行转换,然后将此值(以华氏度为单位)发送到 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 中可用的信息有限,我在 setter 中进行了“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");
        }
    }

编辑:这是一个演示问题的示例项目的链接。左列是用户可编辑的,表示用户查看/编辑的数据。右列显示用户进行更改时实际存储的基础值(此值不应更改)。

如果您修改左列中的值,并在同一行中使用“制表符”或选择不同的列,您可以看到值与您输入的值不同。但是当行失去焦点时它会变回来。通过设置断点,您可以看到当单元格本身失去焦点时,使用 DependencyProperty.UnsetValue 调用 Convert 方法,然后调用 Unit 的 get 方法,然后再次使用 DataGrid 中的值调用 Convert 方法。

温度转换器.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 回答