User controls are a great way couple logic and XAML in an encapsulated way. You can reuse a user control over and over and the logic is isolated from the general flow of your program. This article is not about creating a user control. This article is about binding to a user control.
Not a custom control
A user control contrasts with a custom control in that a custom control contains no XAML. Custom control can be (and is sometimes are required to be) templated (for example, a DataTemplate). Conversely, user controls cannot be templated as the XAML is embedded.
I wrote more about custom controls here: http://blog.jerrynixon.com/2013/01/walkthrough-custom-control-in-xaml-isnt.html
The simplest user control might look like this:
Using a user control
The simplest use of a user control would be like this:
In the code above, I simply declare the user control and the rest is done for me. What isn’t done in the code is passing information to set the value of any properties or custom properties.
Please note: the data context property of the user control inherits from the parent. A DataTemplate might like this. But user controls don’t anticipate a data context type. Instead, they want properties explicitly set. And we want to bind those properties.
Adding properties
To set a property you need a property. If you are a .Net developer then you will likely start with simple CLR properties. This is a good idea. Like this:
Setting such a property is a snap, and it works just fine. You can reference it in the code behind or you can reference it in the XAML. Like this:
Trouble binding
But that is a literal value. What about dynamic binding?
Let’s assume your MainPage.xaml.cs loads like this:
With that data context set, you might try this:
But this fails. It results in “An exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred”. And though it says you may safely continue, it doesn’t continue safely at all.
What does that mean? It means the binding target isn’t a dependency property!
Confused?
This is confusing to XAML developers because they are accustomed to binding to CLR properties that implement INotifyPropertyChanged in their view models.
In reality, this is simply reversed. A data binding source source CAN be a CLR-type property. A data binding target MUST be a dependency property. This is an immutable XAML rule.
Tip: Visual Studio includes the “propdp” snippet to create dependency properties.
Another type of property
Once we know the property must be a dependency property (which simply means the property must be backed by a dependency property object) we can start to build it out:
In the code above, we create our Text property as a dependency property and we introduce a reusable method called SetValueDp() which uses the CallMemberName introduced in .Net 4.5. CallMemberName let’s you reference your property name without creating a literal string in your code-behind.
After you change the type of property to a dependency property you can bind to the same property from the XAML of the user control and the XAML of the parent. Writing to one writes to the other and the dependency property itself keeps the coalesced value.
One more thing
Binding inside your user control isn’t exactly obvious either. The reason is, you get an error when you attempt to set the data context of the user control. Let’s say you try this:
You will not receive an error for trying to set the user control’s data context. You will, however, discover that all data binding has stopped working.
Workaround or solution?
Rather than explain why this is happening, let me simply share the solution. Since data context is a dependency property and dependency properties inherit down the visual tree, we can set the data context of a container and intercept the data context’s inheritance. Like this:
In the code above we don’t set the data context of the user control, we set the data context of the first child in the user control. Presto! This one little workaround can save you dozens of hours pulling your hair. And, the best part, it works and gets the job done.
Your user control can be as simple as this:
Now your user control can bind; and it can bind one or two way! It’s up to use to use the mode that is appropriate for the controls you are embedding in your user control. The dependency property does the rest – and that’s that!
Best of luck!
0 comments:
Post a Comment