33

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?

Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266

6 Answers6

9

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.

H.B.
  • 166,899
  • 29
  • 327
  • 400
Rick Sladkey
  • 33,988
  • 6
  • 71
  • 95
  • 5
    Thanks for your answer and the converter but I disagree. His answer and Karl Shifflett's blog covered why this feature was added for every mode except `OneWayToSource`. Say that I do some conversion, formatting or whatever in the setter, I still wouldn't want my `TextBox` to update to that value. Not only did they break the way it worked before (which I never heard anyone complain about), it's now also missleading because it isn't `OneWayToSource` anymore. It's somewhere in between like `TwoWay` without update on change notification – Fredrik Hedblad Feb 05 '11 at 00:49
  • 1
    I failed to mention that I find the new behavior for `OneWayToSource` surprising and unhelpful. I didn't mean to appear to be justifying it, just acknowledging that I don't think we'll get any traction because those who want it are probably more vocal than those who don't. So a workaround at least gives us a solution. – Rick Sladkey Feb 05 '11 at 00:55
  • 1
    Also, when would anybody ever want this behavior? You want the `TextBox` to call get after it has been set from the `TextBox`, but not if the property if set from somewhere else? Makes no sense to me.. – Fredrik Hedblad Feb 05 '11 at 00:56
  • Ok :) It appears to me that it just slipped in there along with the rest of the modes. Thanks for the workaround! +1 – Fredrik Hedblad Feb 05 '11 at 00:58
  • Many software crimes have been committed in the name of symmetry. I think the argument would go like this: if you get what you set there is no problem, and if you get something other than what you set, then you probably want that instead, **even with one way to source**. – Rick Sladkey Feb 05 '11 at 03:14
  • This converter always throws me a nullreferenceexpection. – jero2rome Feb 18 '14 at 12:18
  • It is annoying if there is no getter at all. The converter wont prevent the error "can not get" then – Firo Aug 26 '16 at 11:45
6

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/

Simon D.
  • 4,451
  • 2
  • 28
  • 57
  • 1
    It's just an example re-producing my problem, I have no implementation that looks like that. Also, if I remove the setter, the `TextBox` will always be blank everytime `LostFocus` is called. This still doesn't seem right to me – Fredrik Hedblad Feb 04 '11 at 15:46
  • Anyway, +1 for your explanation and the link – Fredrik Hedblad Feb 04 '11 at 15:50
  • 1
    The karlshifflett blog no longer exists. – SezMe Mar 09 '16 at 07:23
  • 3
    Karl Shifflett has deleted his old blog ("too much dated content" https://twitter.com/kdawg02/status/732197390665535488). Here is the archive of the blog entry mentioned: https://web.archive.org/web/20150925210516/https://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/ – Edward Jun 06 '16 at 08:45
4

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.

David
  • 6,014
  • 4
  • 39
  • 55
  • My guess is the intention is to reflect any filtering that you may have applied during the set, so that the contents of the TextBox actually reflect the value that was 'really' set. The only thing different about OneWayToSource, then, is that it would only call get() after applying a set(), rather than calling a get() whenever a property change notification is sent. – Dan Bryant Feb 02 '11 at 15:46
  • On a side note, a property that behaves in this way (it exposes a getter, but the get has absolutely nothing to do with what was set) is a fairly pathological property. It sounds like what you really want is a Method, but packaged as a property to allow the Binding system to trigger the call. It may be worth looking into creating a custom Behavior, as that way you won't have to abuse the property system. – Dan Bryant Feb 02 '11 at 15:51
  • @Dan Bryant: Are you commenting on David's answer or are you answering the question? :) I should add, that if you remove the getter, the `TextBox` will always be blank every time `LostFocus` is raised. Surely this can't be the desired way for a `OneWayToSource` Binding or what do you think? – Fredrik Hedblad Feb 02 '11 at 17:04
  • 1
    it's even a "Great Feature" ;): http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/ – Simon D. Feb 04 '11 at 15:40
  • @Simpzon: Thanks for the link. I can surely see great usage for this in all other modes than `OneWayToSource`. Why should it ever use `get`? – Fredrik Hedblad Feb 04 '11 at 15:43
  • 1
    @Meleak: yeah, I do see your point, but it might be useful when there is some conversion taking place during your set. It should definitely not call the getter if the property is set from "outside". – Simon D. Feb 04 '11 at 15:46
  • @Simpzon: But if some conversion is taking place in `set`, I still don't want it to effect the value in the `TextBox`. If I wanted that, I would have used some other `Mode` than `OneWayToSource` – Fredrik Hedblad Feb 04 '11 at 15:52
  • 1
    I agree with meleak and still do not see this as a good, and certainly not a "great" feature. I certainly understand why the sample code provided could be considered somewhat a "Hack", but I personally do not think It is one. It Could just be used exactly like that as a placeHolder for instance, and the new "feature" goes right in the way of doing this. – David Feb 04 '11 at 16:07
  • @David: +1 for the comment and +1 on your answer for agreeing with me :) – Fredrik Hedblad Feb 04 '11 at 16:57
  • 1
    have you tried using dp and simply setting a default value ? (don't know what your project looks like so it's hard to think of a workaround; but might be worth a try...) – David Feb 04 '11 at 23:07
  • @David: You're right, a DP will not have the same problem because the CLR property wrapper isn't used in a binding. Good thinking! – Fredrik Hedblad Feb 04 '11 at 23:34
2

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?

m1dst
  • 333
  • 2
  • 9
0

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.

David Turvey
  • 2,891
  • 6
  • 27
  • 29
0

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;
        }
    }
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
Alex Zhevzhik
  • 3,347
  • 19
  • 19
  • Hello Alex and thanks for your answer. I will read through the details of your answer later tonight to digest. Using `UpdateSourceTrigger=PropertyChanged` seems to work at first, but giving it a closer look reveals that the `Text` property for the `TextBox` does no longer match the displayed Text which is a pretty weird bug that's caused by this behavior (TextBox.Text will still say `"Should not be used in OneWayToSource Binding"` but the displayed Text is whatever you type). – Fredrik Hedblad Feb 07 '11 at 09:11
  • Of course TextBox.Text shows "Should not..." because you ASK for its value, so you force binding to READ value, i.e to call property getter (which returns string "Should not..."). Actually, if you execute app in debug mode, you will see that getter is yet called after every setter, so UpdateSourceTrigger doesn't changes 4.0 behaviour, but is handled by UI in different way (TextBlock.Text isn't reset to previous value). I suppose that eventually Microsoft team will fix this hack, so I'd recommend you to write well-designed getter/setter instead of using UpdateSourceTrigger. – Alex Zhevzhik Feb 07 '11 at 11:06
  • You totally missunderstood my comment. Of course I didn't mean I was using the missmatch between the Text property and the displayed text as a workaround. And the way I expect to use a `OneWayTwoSource` binding doesn't involve using the Property as some sort of "value converter". But I've been over this in all the other answers so no need to do it again. Also, the code in the Question is only there to reproduce my problem and is not something I'm using but apperently that wasn't clear – Fredrik Hedblad Feb 07 '11 at 20:59
  • @Meleak: Sorry for my bad explanation, I was a little bit incorrect, but once more. In 4.0 you couldn't use OneWayToSource binding mode without rereading of source. Ok? During developing object's design you should take into account that getter will be called immidiately after setter. This means that in your scenario TextBox.Text equals "Should not..." permanently. But if you just need to display correct typed word and use m_textProperty in future - you could use UpdateSourceTrigger. If you need correct TextBox.Text value - you should expose well-designed property, which gets what sets. – Alex Zhevzhik Feb 08 '11 at 06:25
  • In the real-world scenario, I have a View for ITestCase, in the View I have a `ComboBox` populated from a List but every value in the List is not valid for every TestCase. By customer request, all the values should be displayed in the `ComboBox` no matter which TestCase is being shown and if an invalid value is being selected, null is set to the backing property in the setter. `OneWayToSource` binding was working great until we upgraded the project but now, selecting an invalid value resets the ComboBox to null so we had to do some re-design. I guess that's just the way it works now.. – Fredrik Hedblad Feb 08 '11 at 07:52