开发者

OneWayToSource Binding seems broken in .NET 4.0

OneWayToSource Binding seems broken in .NET 4.0

I have this simple piece of Xaml

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

And my code behind looks like this

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

In .NET 3.5 this works as you might except. Put some text in the TextBox, press Tab to make it lose Focus, and the TextProperty updates with whatever text that was entered in the TextBox

In .NET 4.0, if I type some text in the TextBox and then press Tab to make it lose Focus, the TextBox reverts to the value for TextProperty (meanin开发者_如何转开发g "Should not be used in OneWayToSource Binding"). Is this re-reading intended for a OneWayToSource Binding in .NET 4.0? I just want the TextBox to push its value into the TextProperty and not the other way around.

Update

Adding a Bounty to this question as this has become a mayor inconvenience in my project and I would like to know the reason that this has changed. It seems that get is called after the Binding has updated the source. Is this the desired behavior for a OneWayToSource Binding in .NET 4.0?

If Yes

  • What was the problem with the way it worked in 3.5?
  • In what scenarios is this new behavior better?

Or is this in fact a bug that we can hope to get fixed in a future release?


Karl Shifflett's blog and @Simpzon's answer already cover why they added this feature and why it is not a problem for properties that always get what was set. In your own code you always use an intermediate property that has the proper semantics for binding and use an internal property that has the semantics you want. I would call the intermediate property a "blocking" property because it blocks the getter from reaching your internal property.

But in the case where you don't have access to the source code for the entity that you are setting the property on and you want the old behavior, you can use a converter.

Here is a blocking converter with state:

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

and you can use it for your example like this:

<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>

Note that because the converter has a state you need a separate instance each time the resource is used and for this we can use the x:Shared="False" attribute on the resource.


This is indeed by design. Usually it should not make trouble, but your property implementation is at least unconventional, I would say. Getters and setters (accessors) should really do not much more than get and set, and each get should be consistent with the last corresponding set. (sorry, no source for this, it's just what we have called good citizenship in all development teams I've been in).

More details about the feature here: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/


Bug, definitely.

if it's a "feature", it's a very bad one...

it seems they have added a call to the get() function after the set() is done, even in the OneWayToSource mode... can anybody explain why ?

also, thanks for pointing this out, it explains an issue I have had since I upgraded my project to .net 4.0 and that I could not explain until now...

just a side note: I have solved this by using dependency properties in the end.


This is quite clearly a bug. It seems to have been reported at least once by someone. https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0

I agree with many of the others that one way should be one way period.

My scenario is complicated and having functionality changed between framework versions has caused me a real headache.

I have a textbox bound to a property. I have a converter which changes the format on the fly. EG: I enter EU5 in the textbox, the property gets EU005. I have got the binding set to trigger on property changed as I need to do lookups within the ViewModel as the user types. The new implementation changes the value of the textbox as I type. So if I wish to type EU512 I couldn't easily as the textbox text would keep changing.

I have tried many thing - Explicit binding (where you decide when to update and which way.) This has the same problem. If I say, UpdateSource, it does, but also then rereads the property and changes the target too.

I tried OneWayToSource and had the same problem. I have found no way to work around this without making annoying changes to my VM. The only other way would be to remove binding on this field and start firing events which would be awful.

If MS made the binding behave as it is logically named then my problem would disappear. Even a property on the binding to opt out of the .net4 implementation and behave as 3.5 would work for me.

Anyone have any suggestions for me on how I can get around this?


I had a variation of this issue for a two way binding. I realise that this is not exactly the same as the problem being discussed but this answer came up top while searching and it lead to my solution. Hope someone finds it helpful.

My solution blocks the re-read of the backing property but will update the UI when it is changed by some other source. I based my solution on the blocking converter in Rick Sladkey's answer.

It just adds a check to the convert to see if the lastValue field would convert to the same backing store value. If not, the backing store value must have changed from another source and the UI should be updated.

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
    }
}

In my particular usecase for this I had a length and dimension suffix stored in a textbox (10m, 100mm etc). The converter parsed this to a double value or added the suffix (depending on direction of conversion). Without the converter it would add a suffix on each update of the textbox. Trying to type '10' would result in '1m0' as the converter would run after the first key stroke.


Is this the desired behavior for a OneWayToSource Binding in .NET 4.0?

Yes. This is done for developer's ability to change provided value without clumsy converters.

What was the problem with the way it worked in 3.5?

No problem. The way it worked in 3.5 didn't allow to correct provided values.

In what scenarios is this new behavior better?

When you need to correct provided values. If you don't need it, then you should just write correct property's getter and setter.

public string TextProperty
{
    get;
    set;
}

However, as I can see, changing UpdateSourceTrigger to "PropertyChanged" preserves values from being reread (so you could leave old property declaration):

<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;
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜