Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OneWayToSource Binding seems broken in .NET 4.0

Tags:

c#

binding

wpf

xaml

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 (meaning "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?

like image 979
Fredrik Hedblad Avatar asked Feb 02 '11 14:02

Fredrik Hedblad


4 Answers

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.

like image 78
Rick Sladkey Avatar answered Nov 01 '22 18:11

Rick Sladkey


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/

like image 26
Simon D. Avatar answered Nov 01 '22 18:11

Simon D.


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.

like image 4
David Avatar answered Nov 01 '22 19:11

David


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?

like image 2
m1dst Avatar answered Nov 01 '22 17:11

m1dst