在.NET 4.0中,OneWayToSource绑定似乎已被破坏

时间:2011-02-02 14:42:51

标签: c# wpf xaml binding

OneWayToSource .NET 4.0中的绑定似乎已被破坏

我有一个简单的Xaml

<StackPanel>
    <TextBox Text="{Binding TextProperty, Mode=OneWayToSource}"/>
    <Button/>
</StackPanel>

我背后的代码看起来像这样

public MainWindow()
{
    InitializeComponent();
    this.DataContext = this;
}
private string m_textProperty;
public string TextProperty
{
    get
    {
        return "Should not be used in OneWayToSource Binding";
    }
    set
    {
        m_textProperty = value;
    }
}

在.NET 3.5 中,除了之外,它可以正常工作。在TextBox中添加一些文字,按Tab键使其失去焦点,并使用TextProperty

中输入的任何文字更新TextBox

在.NET 4.0 中,如果我在TextBox中键入一些文字,然后按Tab键使其失去焦点,则TextBox将恢复为{{TextProperty的值1}}(意思是“不应该在OneWayToSource绑定中使用”)。这个重读是否适用于.NET 4.0中的OneWayToSource绑定?我只希望TextBox将其值推入TextProperty,而不是相反。

更新
为此问题添加赏金,因为这已成为我项目中的市长不便,我想知道这已经改变的原因。似乎在Binding更新源之后调用了get。这是.NET 4.0中OneWayToSource绑定所需的行为吗?

如果是

  • 它在3.5中的工作方式有什么问题?
  • 在哪种情况下这种新行为更好?

这实际上是一个我们希望在将来的版本中修复的错误吗?

6 个答案:

答案 0 :(得分:9)

Karl Shifflett的博客和@ Simpzon的回答已经涵盖了为什么他们添加了这个功能以及为什么它不是一个问题,因为它总是得到设置的属性。在您自己的代码中,您始终使用具有适当语义的中间属性进行绑定,并使用具有所需语义的内部属性。我会将中间属性称为“阻塞”属性,因为它会阻止getter到达您的内部属性。

但是如果您无法访问要设置属性的实体的源代码,并且您想要旧的行为,则可以使用转换器。

这是一个状态为

的阻塞转换器
public class BlockingConverter : IValueConverter
{
    public object lastValue;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return lastValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        lastValue = value;
        return value;
    }
}

您可以将它用于您的示例:

<Grid>
    <Grid.Resources>
        <local:BlockingConverter x:Key="blockingConverter" x:Shared="False"/>
    </Grid.Resources>
    <StackPanel>
        <TextBox Text="{Binding TextProperty, Mode=OneWayToSource, Converter={StaticResource blockingConverter}}"/>
        <Button Content="Click"/>
    </StackPanel>
</Grid>

请注意,由于转换器具有状态,因此每次使用资源时都需要单独的实例,为此我们可以使用资源上的x:Shared="False"属性。

答案 1 :(得分:6)

这确实是设计上的。通常它不应该制造麻烦,但我会说,你的房产实施至少是非常规的。 getter和setter(访问器)应该只是获取和设置,并且每个get应该与最后一个相应的set保持一致。 (对不起,没有任何消息来源,这就是我们在所有开发团队中所谓的良好公民身份)。

此处有关此功能的更多详细信息:http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/

答案 2 :(得分:4)

Bug,当然。

如果它是一个“特征”,那就是一个非常糟糕的......

似乎他们在set()完成后添加了对get()函数的调用,即使在OneWayToSource模式下......任何人都可以解释原因吗?

另外,感谢您指出这一点,它解释了自从我将项目升级到.net 4.0后我遇到的一个问题,直到现在我都无法解释......

只是旁注:我最后通过使用依赖属性来解决这个问题。

答案 3 :(得分:2)

这显然是一个错误。似乎有人至少报告过一次。 https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0

我同意其他许多人的观点,认为单向应该是一种方式。

我的场景很复杂,框架版本之间的功能发生了变化,这让我很头疼。

我有一个绑定到属性的文本框。我有一个转换器,可以动态更改格式。 EG:我在文本框中输入EU5,该属性获得EU005。我有绑定设置触发属性更改,因为我需要在用户键入的ViewModel中进行查找。当我输入时,新实现会更改文本框的值。因此,如果我想输入EU512,我就不能轻易地改变文本框文本。

我尝试了很多东西 - 显式绑定(你决定何时更新以及哪种方式。)这有同样的问题。如果我说,UpdateSource,它确实,但然后重新读取属性并更改目标。

我尝试过OneWayToSource并遇到了同样的问题。我没有办法解决这个问题,而不会对我的虚拟机进行烦人的更改。唯一的另一种方法是删除对该字段的绑定并开始触发可能非常糟糕的事件。

如果MS使绑定行为符合逻辑命名,那么我的问题就会消失。即使绑定的属性选择退出.net4实现并且表现为3.5也适合我。

对于我如何解决这个问题,任何人都有任何建议吗?

答案 4 :(得分:0)

  

这是.NET 4.0中OneWayToSource绑定所需的行为吗?

是。这样做是为了让开发人员能够在没有笨拙的转换器的情况下更改提供的值。

  

它在3.5中的工作方式有什么问题?

没问题。它在3.5中的工作方式并不能纠正提供的值。

  

在哪种情况下这种新行为更好?

需要更正提供的值时。如果你不需要它,那么你应该写出正确的属性的getter和setter。

public string TextProperty
{
    get;
    set;
}

但是,正如我所看到的,将UpdateSourceTrigger更改为&#34; PropertyChanged&#34;保留重读的值(所以你可以留下旧的财产声明):

<StackPanel>
    <TextBox Text="{Binding TextProperty, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
    <Button/>
</StackPanel>

    private string m_textProperty;
    public string TextProperty
    {
        get
        {
            return "Should not be used in OneWayToSource Binding";
        }
        set
        {
            m_textProperty = value;
        }
    }

答案 5 :(得分:0)

对于双向绑定,我有这个问题的变体。我意识到这与正在讨论的问题并不完全相同,但是这个答案在搜索时出现了,它导致了我的解决方案。希望有人觉得它有用。

我的解决方案会阻止重新读取支持属性,但会在其他来源更改时更新UI。我根据Rick Sladkey的答案将我的解决方案基于阻塞转换器。

它只是对转换添加一个检查,以查看lastValue字段是否会转换为相同的后备存储值。如果没有,则后备存储值必须已从其他源更改,并且应更新UI。

public class MyConverter : IValueConverter
{
    public object lastValue;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (LastValue != null && MyConvertBack(LastValue).Equals(value))
            return lastValue;
        else
            return MyConvert(value);

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        lastValue = value;
        return MyConvertBack(value);
    }

    private object MyConvertBack(Object value)
    {
        //Conversion Code Here
    }

    private object MyConvert(Object value)
    {
        //Conversion Code Here
    }
}

在我的特殊用例中,我有一个长度和尺寸后缀存储在文本框中(10米,100毫米等)。转换器将其解析为double值或添加后缀(取决于转换方向)。没有转换器,它会在文本框的每次更新时添加后缀。尝试键入'10'将导致'1m0',因为转换器将在第一次击键后运行。