Lecture thumbnail 0:00 / 0:00 Property observers in the.
Net framework are a very common thing and the I notify property changed interface is used quite a lot.
However, there are situations where things get slightly more complicated and one of those situations
is called a bidirectional binding.
Bidirectional binding.
So what is this all about?
Well, let me show you a very simple scenario where this shows up.
Let’s imagine that you have a product, so you have a class for a product, and that product has a name.
So public string name, I’ll just do it like this.
And then you have a window where information about this product is shown.
Public class window.
So a window.
And it has, let’s say it has lots of different controls like edit boxes, for example.
And let’s suppose that you have an edit box which is tied to a string which presents the product name,
deliberately making those named differently.
So you have a product name, which is, let’s say it’s an edit box.
In actual fact, it’s an edit control.
So basically somebody can edit the product name, but you can also change the product name inside the
class itself.
Maybe because somebody has updated the database and all of a sudden the name changes.
So you have a kind of bidirectional dependency.
You basically want this value and this value to be the same value always.
And the question is, how do you do this?
How do you implement this?
Now, obviously, we already have a mechanism for property observers that’s I notify property change.
So we may as well begin by implementing it in both product as well as window.
So let’s have a NPC here.
I notify property change, just generate the default implementation here.
I’m going to clean this up a bit so it fits better and you would have a similar thing.
Oh, let’s not forget that you have to change the property itself.
At the moment it’s just an automatic property.
We need to change it and use the change notification so it would become something like this.
Luckily, if you’re using resharper or rider, then all of these problems are handled for you.
We’ll do the same thing for a window.
So once again, I notify property change.
Just generate the default implementation here which uses the column member name trick, by the way,
and let’s change the product name property to use change notifications like so.
Okay, so now we have the setup and what we can do is we can try to get the two elements to subscribe
to one another.
Surprise, surprise.
So what is the idea behind this implementation?
The idea is the following Let’s suppose you make a product, so you make a product.
Let’s suppose that its name is a book.
I’m just going to write book here and let’s suppose you have a window which presents information about
this product.
So a window is a new window.
Let’s actually make sure that we initialize the the product name with book as well.
So at the moment we can guarantee that the product name here and the name here have the same value.
But how do we keep it this way?
How do we ensure that they are always the same value and when one of them changes the other changes
in kind?
Well, the simple approach would be to simply subscribe to both of these events.
So first of all, we subscribe on the product.
So we say product dot property changed, property changed, and we make a subscription.
So we make a proper subscription out of all of this.
And here we say, well, if the argument is name, so if Eventargs dot property name is in fact equal
to name, then what we do is, well, we can output just some debugging information so that we get to
see that name was changed in product and we can say window dot product name is equal to product dot
name and you will do the same thing in reverse.
So now you would subscribe to the window.
So you would say window dot product property changed and you would once again perform this kind of subscription.
So here once again, you expect Eventargs property name to be product name.
And if that’s the case, we can once again see that name is changed in window this time.
And then you would set the product name.
Product name equals window dot, product name.
So we’re attempting to synchronize two variables, two properties in actual fact.
So when the product changes, we update the window.
When the window changes, we update the product.
So let’s see if this actually works.
Now, I’m going to add a couple of two string implementations as well.
So I will go into product here and I will give it a two string implementation.
Where?
Well, let’s just output the fact that we are in product and here is the name of the product and I’ll
do a similar thing for the window, except that in the window we’ll specify that it’s window and here
it has to be product name.
Okay, so let’s experiment with this.
Let’s take a product and set its name so it’s not going to be book.
It’s going to be a smart book book for really smart people.
And then we can see both the product as well as the window.
And let’s actually run this.
Let’s see what we get.
Okay.
You can see that name is changed in product, name is changed in window.
And then the product is smart book and the window also shows smart book.
So everything is working, everything is fine.
But you might be surprised that it’s working because remember, well, one of the questions you need
to ask is why isn’t this causing an infinite recursion scenario?
Because you changed the product and you update the window.
But when you call this when you update the window, you change the window, which should update the
product, which should get you back here, back here, back here.
And you should get a stack overflow exception.
So why doesn’t a Stack overflow exception actually happen?
The answer is in the change guards in these conditions.
So you’ll notice that we only generate the property changed event if the value has in fact changed.
If the value is the same, then we simply return.
And this is precisely the mechanism that prevents infinite recursion.
So if you forgot to do this and we are lucky to have writer generate this for us, but if you forgot
to do this, you would in fact get infinite recursion and a stack overflow exception.
Probably not something you want in your product.
Okay, so this is a setup that actually does work.
We have synchronization between two properties, but what if you wanted to wrap this synchronization
into a separate object?
What if you want to have some sort of utility object for managing this kind of binding?
Now, we already have this functionality as part of, let’s say, Windows forms.
This is typically called binding, and the idea is that you bind one variable to the value of another
variable, and then you use the I notify property change mechanism to make sure that they are in sync.
And the binding that we are doing here is called bidirectional because it goes both ways.
Because when A changes, it changes B and when B changes, it changes A So let’s see if we can somehow
shrink wrap this binding into a separate class.
So I’m going to have a public sealed class called bidirectional binding, and it’s going to be a disposable.
So that you can sort of unbind things.
In actual fact, the reason why I want disposable is that I want to be able to sort of cancel the notifications
after I call dispose.
So here I’ll have a very simple implementation of dispose in the sense that we’ll have a flag, let’s
call it private bool, disposed.
And when somebody calls dispose, we’ll just set this flag disposed equals true and we’ll use this flag
inside our notifications so all the magic happens in the constructor.
But the constructor is going to be really complicated because remember, we want to be able to specify
what two classes we need to bind.
So we need to bind a class called first and a class called second.
So two objects basically first and second.
But we need to specify which properties to bind.
So we’ll have a first property and a second property.
So we need four arguments.
Let’s see if we can find out the types of those arguments.
So let’s have a constructor bidirectional binding.
Okay, so what is the type of the first object?
We don’t know.
We could write object here, but this doesn’t really help us because then we need to perform the subscriptions.
But we do know that these objects need to implement Inpc the I notify property changed interface.
So that’s going to be the first object.
And now we want to specify which property of the object we’re actually going to bind.
Now Windows forms does it using strings, but that’s a bit outdated, to be honest.
We don’t use strings anymore either.
We use name of.
But in this particular case we cannot use name of.
So instead what we’re going to be using is expression trees.
So we’re going to take an expression tree where we don’t really care what type of object we’re actually
receiving in this tree.
So it’s going to be an expression of func of object and it’s going to be an access of the first property.
So basically somebody is going to write something like they’re going to write a lambda which returns
x dot foo.
So it takes the variable x and it accesses the property foo and we’re going to do the same thing for
the second object.
So we have I and I notify property change for the second object because we know that it has to implement
this interface.
And then we also specify an expression of func of object for the second property.
There we go.
Okay, so now let’s implement this constructor.
Now there are lots of things that we need to ensure.
We need to first of all, ensure that the expressions, the property expressions are actually member
expressions.
So we need to ensure the property expressions.
So x x x property is in fact a member expression.
So that’s one thing that we need to make sure because x dot foo is a member expression.
So we need to do this.
And the second thing we need to do is to make sure that when you get x dot foo, then foo is in fact
a property.
So we basically need to make sure that the member.
Of the above is in actual fact.
So the member of this is a property info.
It has to be a property.
It cannot be a field, for example, because fields don’t generate all of this wonderful I notify property
change information.
So we’re going to have lots of casts here.
So I’m going to say, if so, first of all, first property dot body has to be a member expression first
expert and we need the same for the second.
So we need a second property dot body is member expression, second expression.
So that’s the first set of requirements for what we need to get.
But that’s not all, because now that we have these expressions, we need to make sure that the member
of those expressions are of type property info.
So if first expert dot member is property info first prop.
And we need to import property info.
That’s a reflection thing.
And second export member is property info.
Second prop.
Then and only then can we actually perform the subscription.
So this is where we would bidirectionally subscribe.
So we would take the first object and we would say first dot property changed plus equals.
And then you’ve seen this before.
We’ve done this kind of thing before, but now we also perform a check for the Disposed because remember
we did this dispose thing.
So if the thing has been disposed, then we don’t actually perform the notification.
So if not disposed, then a second prop, second prop dot set value notice.
We’re using reflection API here.
So we’re calling set value.
That’s a reflection thing.
That’s not a direct call.
So we pass the second object and then we take the value of the first property.
So first prop get value first.
So we’re using reflection to get the value of the first property and we’re using it to update the second
property.
So that’s the bidirectional side of things and we’ll do the same thing in a different direction.
So second property changed plus equals generate this whole thing if not disposed, then first prop dot,
first prop dot set value first comma and then second prop dot get value second.
There we go.
So this is how we would set the whole thing up.
And now what you have is you have this binding object and we can start using it.
So we can take all of this and we can comment it out because it’s just too inconvenient now that we’ve
wrapped everything into a single object so we can make a binding, we can say using Var binding equals
new bidirectional binding.
Okay, so what are the arguments?
So we need to specify the first object which participates in the binding that’s product.
We need to specify which property of that object participates.
So here is the expression tree.
We take product dot name, and then the second object, which is window, and once again the expression
tree for window dot product name.
There we go.
This is how we can get everything together.
And now that we have this binding, when I call product name with Smartbook, that’s going to already
be wired up behind the scenes and the same goes for Windows.
So if I say window dot product name equals really smart book, then when I run this, both of these
should output really smart book.
Let’s actually take a look that it is in fact the case.
So as you can see, that’s exactly what we’re getting here.
We’re getting really smart book in both of these cases.
So what I’ve attempted to demonstrate here is two things.
First of all, how to actually set up two variables so that they depend on one another and they are
kind of synchronized.
And that’s one thing.
And notice, we managed to avoid infinite recursion, which is always a risk.
And the second thing is I’ve shown you how to actually wrap this in a separate class called the Bidirectional
Binding, which does it behind the scenes, using a little bit of magic involving expression trees.
Play Play Play Play Play Stop Play Play Start Play information alert