The story of a un-bindable string


Prologue

Few days ago I was working on a simple WPF application using all the best practices I’ve learned during these years:

  • Singleton

  • Code grouping

  • Inversion of Control

  • Mvvm pattern

  • Properties binding

but at some point I faced with an issue that made me crazy!

The problem

In my simple application I had to load a list of words from a file and then show them into a simple list, giving the possibility to the user to change one of these words by clicking a “Edit” button. I know… You are thinking “come on! this is the simplest exercise I’ve ever done using Mvvm Pattern! What’s wrong with this?” Okay, let me explain it showing you some screenshots: This is the code I wrote in XAML page for the list

image

As you may notice I’ve used a simple ListView with a binded ItemSource (I’m not showing you the details of the Mvvm implementation since it doesn’t matter) and I’ve defined a simple DataTemplate for the list items.

Note: since the MyStringList object is an ObservableCollection<string> I binded the Text property of my TextBox items to the object itself (.) with a Mode=Two-way behavior. Nothing special, right? Okay… this is the result when I run the application:

image My first thoughts:

  • is the list correctly initialized?
  • is the binding process working?
  • am I using a “raisable” object?

So I started debugging the View Model initialization to be sure that the ObservableCollection was correctly instantiated and then I checked in the View’s code behind that the DataContext property was correctly setted. All was fine… To be sure about the binding process I decided to remove the Mode=TwoWay from the Binding definition, just to see if the values were at least showed in read-only mode. image And… I was totally confused! What’s wrong with that code? Why binding a string is not working with the TwoWay mode? Here a quick video to show it: 

Eureka!

After a quick chat with a colleague he suggested to me to encapsulate that string in a container object, and bind the property to Container.Value instead of the value itself, because the binding problem could be related to the code notation:

{Binding Path=., Mode=TwoWay}

As a result of this test, the bi-directional binding was now working… for an unexplainable reason:

  • binding the Text property to a string variable DOES NOT WORK in TwoWay
  • binding the Text property to a Value field (of string type) inside a container object WORKS in TwoWay

Honestly I have no idea if it’s a bug/problem or it’s just something wrong with my approach, by the way I decided to define a BindableObject class in order to bypass this issue.

The solution

Since this issue made me mad for 45 minutes, I decided to implement an efficient and reusable solution!

image

As a result of this experience, I’ve coded a BindableObject<T> container class which can be used with any property type (simple or complex), and since the object itself implements the INotifyPropertyChanged interface, the contained property will raise a changed event for binding.

So the new line of code for binding a value to the Text property would be:

<TextBox Text=”{Binding Path=Value, Mode=TwoWay}” />

In addition to the Value property, which is the core objective of this utility class, I’ve also added a OldValue property (which is automatically set when the Value property changes) and a FormattedValue property which would return a formatted version of the Value property itself (the formatting method must be implemented and customized).

And here’s another quick video with the working solution:

Conclusion

I have uploaded the code of this helper object to my Github repository and additionally I’ve created a Nuget package for this and all the future helpers/utilities I usually use for Mvvm projects.

Update (16/03/2017)

As suggested by Tyrrrz in a comment, this issue could be related to the immutable behavior of the string type (as also described in the official documentation), so whereas binding a single string property to a TextBox works (probably because after the RaisePropertyChanged event the Text property of the component uses the new string object reference) if you define a List of strings then the TextBoxe(s) inside the ListView would always refer to the original string objects (whose value is not changing).

Comments (7)

  1. Hannes says:

    just {Binding } – no path – should work

    1. Hi Hannes, just {Binding } was not working for me when I edited the values, so I supposed that the default “Mode” taken by the TextBox isn’t TwoWay

  2. I believe the control you are actually wanting to use is a ListBox. While ListView does indeed derive from ListBox, and the ItemTemplate property is left exposed, to actually change how the data is presented you should have changed “ListView.View” (finding out why requires some more reading on ListViews). The problem with this is that ListView.View is of type ViewBase and the only concrete implementation of ViewBase that ships with WPF is “GridView”. Creating a list that looks like your picture, with a ListView, would require something like this:

    (The styling is necessary due to the fact that GridView has column headers, much like a DataGrid)

    Doing the same with a ListBox looks like this:

    The necessity of {Binding Path=.} could be a bug, as it should mean the exact same thing as {Binding} (according to MSDN documentation).

    1. It turns out commenting did not work as nicely as I expected… The examples from my previous comment can be found here: https://gist.github.com/CAV3MANN/37849efed80c8f53db6623cf713badb8

    2. Hi Joshua, thanks for you reply. The problem I’m getting (and for which I’ve found a solution only using my “Container” object) is about the TwoWay Binding. I’ve tried your code (using Listbox instead of ListView) with the {Binding } syntax but when I edit a value in one of the TextBox inside the list, the new value is not updated in the String item inside MyStringList.
      Look again in the post, I’ve added a quick video to reproduce the missing TwoWay binding problem

  3. Tyrrrz says:

    Strings are immutable in C#. You were binding using two-way, so every time you changed the string it was a different object.

    1. Hi Tyrrrz, yeah I supposed that there should be something about the immutable behaviour of string type, but I hoped it would work because it works when you bind a single string property to a TexBox

Skip to main content