Wpf 将OneWayToSource与目标的一次性初始化绑定

Wpf 将OneWayToSource与目标的一次性初始化绑定,wpf,data-binding,Wpf,Data Binding,我有一个DataGrid,其中可编辑的单元格绑定到各个项目的视图模型中各自的值 最初,数据被加载并显示给用户,然后用户可以在网格中编辑数据 绑定工作正常(在我的例子中是UpdateSourceTrigger=OnPropertyChanged),但是由于double(视图模型)和string(UI)之间的转换,双向绑定会导致恼人的UI错误,比如当用户键入时,使小数点分隔符或小数点后的零消失 两个错误的解决方案是: <TextBox Style="{StaticResource E

我有一个
DataGrid
,其中可编辑的单元格绑定到各个项目的视图模型中各自的值

最初,数据被加载并显示给用户,然后用户可以在网格中编辑数据

绑定工作正常(在我的例子中是
UpdateSourceTrigger=OnPropertyChanged
),但是由于
double
(视图模型)和
string
(UI)之间的转换,双向绑定会导致恼人的UI错误,比如当用户键入时,使小数点分隔符或小数点后的零消失

两个错误的解决方案是:

<TextBox Style="{StaticResource ErrorStyle}">
    <TextBox.Text>
        <MultiBinding UpdateSourceTrigger="PropertyChanged" 
                      Mode="TwoWay"
                      Converter="{StaticResource DoubleUserStringConverter}">
            <Binding Path="TheProperty" ValidatesOnDataErrors="True"/>
            <Binding Path="ThePropertyUserString"/>
         /MultiBinding>
    </TextBox.Text>
</TextBox>
/// <summary>
/// multibinding, first binding is double? and second is string, both representing the same value
/// the double? value is for the viewmodel to use as normally intended
/// the string value is for the user not to have ui bugs
/// </summary>
class DoubleUserStringConverter : IMultiValueConverter
{
    private OriginalConverterYouWanted converter;
    public DoubleUserStringConverter()
    {
        converter = new OriginalConverterYouWanted(); //single binding, not multi
        //for types "double" in the view model and "string" in the UI
        //in case of invalid strings, the double value sent to UI is null
    }

    //from view model to UI:
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[1] == null)  //null string means UI initialization, use double
            return converter.Convert(values[0], targetType, parameter, culture);
        else 
            return values[1]; //in the rest of the time, send user string to UI
    }

    //from UI to view model
    public object[] ConvertBack(object value, Type[] targetTypes, 
                                object parameter, CultureInfo culture)
    {
        return new object[] { 
            converter.ConvertBack(value, targetTypes[0], parameter, culture), //can be null
            value //string is always sent as is, no changes to what the user types
        };
    }
}
  • 在视图模型中将属性设置为
    字符串
    ,并在视图模型内进行必要的转换。
    • 问题:给我带来了一个奇怪的问题,即UI和视图模型之间的区域性不兼容(我不希望视图模型知道UI的区域性)
  • 使用
    OneWayToSource
    绑定。当VM停止发回解析和重新转换的值时,这消除了所有UI错误。
    • 问题:我无法(或不知道如何)使用加载的数据初始化网格中的值
那么,我可以在“a
OneTime
绑定”之后使用一个
OneWayToSource
绑定,或者以某种方式将两者相加吗


我尝试将
FallbackValue
TargetNullValue
绑定到源值,但它们不接受绑定。

小数点消失是它们在尝试修复其他内容时引入的“功能”。我以为是.NET4.0引入了它,人们开始注意到这是一个突破性的变化,但文档似乎暗示了.NET4.5

这通常是因为您设置了updatesourcetrigger=propertychanged

简单的解决方法通常是将其移除

因为

, UpdateSourceTrigger=LostFocus
是文本框文本绑定的默认行为

或者,您可以尝试使用KeepTextBoxDisplaySynchronizedWithTextProperty


您可以在显示任何内容之前在Mainwindow中进行设置。

我发现了一个黑客解决方案,它涉及使用两个属性,原始属性和一个专用于用户的字符串以实现平滑行为。为此,请使用特定的转换器。(我想我会将此作为未来案例的模式)

这适用于视图模型不更改属性,只有用户更改属性的情况。(如果您想要视图模型也更改属性的真正双向交互,则需要在需要更改属性时将string属性设置为null)

在视图模型中:

<TextBox Style="{StaticResource ErrorStyle}">
    <TextBox.Text>
        <MultiBinding UpdateSourceTrigger="PropertyChanged" 
                      Mode="TwoWay"
                      Converter="{StaticResource DoubleUserStringConverter}">
            <Binding Path="TheProperty" ValidatesOnDataErrors="True"/>
            <Binding Path="ThePropertyUserString"/>
         /MultiBinding>
    </TextBox.Text>
</TextBox>
/// <summary>
/// multibinding, first binding is double? and second is string, both representing the same value
/// the double? value is for the viewmodel to use as normally intended
/// the string value is for the user not to have ui bugs
/// </summary>
class DoubleUserStringConverter : IMultiValueConverter
{
    private OriginalConverterYouWanted converter;
    public DoubleUserStringConverter()
    {
        converter = new OriginalConverterYouWanted(); //single binding, not multi
        //for types "double" in the view model and "string" in the UI
        //in case of invalid strings, the double value sent to UI is null
    }

    //from view model to UI:
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[1] == null)  //null string means UI initialization, use double
            return converter.Convert(values[0], targetType, parameter, culture);
        else 
            return values[1]; //in the rest of the time, send user string to UI
    }

    //from UI to view model
    public object[] ConvertBack(object value, Type[] targetTypes, 
                                object parameter, CultureInfo culture)
    {
        return new object[] { 
            converter.ConvertBack(value, targetTypes[0], parameter, culture), //can be null
            value //string is always sent as is, no changes to what the user types
        };
    }
}
与标准代码的区别在于:

  • 添加不带逻辑的字符串属性
  • 在原始属性更改时为此属性添加通知
代码:

在XAML中:

<TextBox Style="{StaticResource ErrorStyle}">
    <TextBox.Text>
        <MultiBinding UpdateSourceTrigger="PropertyChanged" 
                      Mode="TwoWay"
                      Converter="{StaticResource DoubleUserStringConverter}">
            <Binding Path="TheProperty" ValidatesOnDataErrors="True"/>
            <Binding Path="ThePropertyUserString"/>
         /MultiBinding>
    </TextBox.Text>
</TextBox>
/// <summary>
/// multibinding, first binding is double? and second is string, both representing the same value
/// the double? value is for the viewmodel to use as normally intended
/// the string value is for the user not to have ui bugs
/// </summary>
class DoubleUserStringConverter : IMultiValueConverter
{
    private OriginalConverterYouWanted converter;
    public DoubleUserStringConverter()
    {
        converter = new OriginalConverterYouWanted(); //single binding, not multi
        //for types "double" in the view model and "string" in the UI
        //in case of invalid strings, the double value sent to UI is null
    }

    //from view model to UI:
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[1] == null)  //null string means UI initialization, use double
            return converter.Convert(values[0], targetType, parameter, culture);
        else 
            return values[1]; //in the rest of the time, send user string to UI
    }

    //from UI to view model
    public object[] ConvertBack(object value, Type[] targetTypes, 
                                object parameter, CultureInfo culture)
    {
        return new object[] { 
            converter.ConvertBack(value, targetTypes[0], parameter, culture), //can be null
            value //string is always sent as is, no changes to what the user types
        };
    }
}

/多重绑定>
转换器:

<TextBox Style="{StaticResource ErrorStyle}">
    <TextBox.Text>
        <MultiBinding UpdateSourceTrigger="PropertyChanged" 
                      Mode="TwoWay"
                      Converter="{StaticResource DoubleUserStringConverter}">
            <Binding Path="TheProperty" ValidatesOnDataErrors="True"/>
            <Binding Path="ThePropertyUserString"/>
         /MultiBinding>
    </TextBox.Text>
</TextBox>
/// <summary>
/// multibinding, first binding is double? and second is string, both representing the same value
/// the double? value is for the viewmodel to use as normally intended
/// the string value is for the user not to have ui bugs
/// </summary>
class DoubleUserStringConverter : IMultiValueConverter
{
    private OriginalConverterYouWanted converter;
    public DoubleUserStringConverter()
    {
        converter = new OriginalConverterYouWanted(); //single binding, not multi
        //for types "double" in the view model and "string" in the UI
        //in case of invalid strings, the double value sent to UI is null
    }

    //from view model to UI:
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[1] == null)  //null string means UI initialization, use double
            return converter.Convert(values[0], targetType, parameter, culture);
        else 
            return values[1]; //in the rest of the time, send user string to UI
    }

    //from UI to view model
    public object[] ConvertBack(object value, Type[] targetTypes, 
                                object parameter, CultureInfo culture)
    {
        return new object[] { 
            converter.ConvertBack(value, targetTypes[0], parameter, culture), //can be null
            value //string is always sent as is, no changes to what the user types
        };
    }
}
//
///多重绑定,第一个绑定是双绑定?第二个是字符串,两者都表示相同的值
///替身?值是供viewmodel正常使用的
///字符串值用于用户不存在ui错误
/// 
类DoubleUserStringConverter:IMultiValueConverter
{
私有原始转换器您想要的转换器;
公共DoubleUserStringConverter()
{
converter=new originalconverteryouwant();//单绑定,而不是多绑定
//对于视图模型中的“double”和UI中的“string”类型
//如果字符串无效,则发送到UI的双精度值为null
}
//从视图模型到用户界面:
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
如果(values[1]==null)//null字符串表示UI初始化,则使用double
返回converter.Convert(值[0],目标类型,参数,区域性);
其他的
返回值[1];//在剩余时间内,将用户字符串发送到UI
}
//从UI到视图模型
公共对象[]转换回(对象值,类型[]targetTypes,
对象参数,CultureInfo(区域性)
{
返回新对象[]{
converter.ConvertBack(值,targetTypes[0],参数,区域性),//可以为null
值//字符串始终按原样发送,不更改用户键入的内容
};
}
}

设置UpdateSourceTrigger=LostFocus或Explicit部分解决了这个问题。“双向绑定会导致恼人的UI错误,比如用户键入时小数点分隔符消失”-你的确切意思是什么?它应该是固定的,因为双向绑定是一种方式。我处理了这个问题,发现这种情况的发生不是由于属性改变。即使事件没有发生(因为值没有改变),绑定仍然会丢弃最后一个点和点后的零。这很可能是由于PropertyDescriptor的内部实现——绑定通过它工作。如果你说俄语,我最近在这里解释了他们的工作,与设置一个属性相比,这似乎是一个很大的工作。是的,但这是一个完整的解决方案,所有其他的都有恼人的UI错误。考虑到您通常已经有了一个转换器和一个get/set/notify逻辑,它实际上并不多。我尝试了您的解决方案,
LostFocus
一个是最好的,但不幸的是,如果用户带着无效字符串离开,它仍然会删除整个文本。如果网格只有一行,单击该行外部不会更改焦点,则必须单击某些特定位置。-对于兼容性解决方案,每当验证失败时,它也会删除整个文本。