I was updating a Tips & Tricks post on setting the DataContext via XAML and realised the post was getting way too large for a Tips & Tricks, so here’s the content in its own post.
So to set the DataContext in XAML we can do the following
<Window.DataContext> <Thermometer:ConversionViewModel /> </Window.DataContext>
This is simple enough, but there are various other ways to do this in XAML
ObjectDataProvider
The ObjectDataProvider allows us to create an object for use as a binding source within XAML. Unlike the Datacontext shown above, we declare the XAML for the ObjectDataProvider within the Window.Resources section as per
<Window.Resources> <ObjectDataProvider ObjectType="{x:Type Thermometer:ConversionViewModel}" x:Key="data"> <ObjectDataProvider.ConstructorParameters> <system:Double>30</system:Double> </ObjectDataProvider.ConstructorParameters> </ObjectDataProvider> </Window.Resources>
In the example above you’ll notice we can supply constructor parameters also.
We give the object a key for use in our bindings where we could just do something like
<Grid DataContext="{StaticResource data}"> ... </Grid>
or
<TextBlock Text={Binding Source={StaticResource data}, Path=Name}" />
The ObjectDataProvider also allows us to bind to a Method return value. So for example, what if we have the ObjectDataProvider similar to the one previously declared and we have a method named GetAge which takes a parameter (the name of the person who’s age we want returned). Like the previous example we can declare our ObjectDataProvider in the Resources section and it might look like this
<ObjectDataProvider ObjectInstance="{StaticResource data}" MethodName="GetAge" x:Key="age"> <ObjectDataProvider.MethodParameters> <system:String>Bob</system:String> </ObjectDataProvider.MethodParameters> </ObjectDataProvider>
Now we can bind to this data in the following way
<Label Content="{Binding Source={StaticResource age}}" />
Note: Just remember that the binding source is one way. So if you were to assign it to a two way by default control such as a TextBox, you’d get an error requiring you to set the binding to one way (not much use really on a text box).
But it would be much cooler if you could bind to the method and yet interact with the parameters at runtime, i.e. the user enters a person’s name (in our example) and the label updates to show the age of that person.
This can be achieved by creating a binding to the ObjectDataProvider object itself. By default we’ve seen that the interaction with the binding source allows us to pass straight through to the actual data the binding source contains, i.e. we do not appear to be using an ObjectDataProvider to bind to but instead the data source we assigned to it. If we could bind somehow to the ObjectDataProvider itself we could change the arguments passed to the method. This is how we do this…
<TextBox> <TextBox.Text> <Binding Source="{StaticResource age}" Path="MethodParameters[0]" BindsDirectlyToSource="true" /> </TextBox.Text> </TextBox>
As you can see above, we bind to the source as we’ve done previously but we now have a Path that points to the MethodParameters list and we’ve set the BindsDirectlyToSource to true. This tells the binding to evaluate the path on DataSourceProvider object (the ObjectDataProvider) as opposed to our data which is wrapped within the ObjectDataProvider.
Another thing to note about the code above, is that this works fine if the method parameter is a string (as in our example), but if it’s not then you’ll need to convert the type within the TextBox.Text binding source to the type expected by the method or you’ll simply find the method doesn’t get called.
For example
<TextBox.Text> <Binding Source="{StaticResource name}" Path="MethodParameters[0]" BindsDirectlyToSource="true" Converter="{MyConverters:ConvertTypeConverter To={x:Type system:Int32}}"/> </TextBox.Text>
And finally…
Just to conclude this tip by also stating that you can also declare your view model in XAML as a resource as in the following
<Window.Resources> <Thermometer:ConversionViewModel InitialValue="30" x:Key="data" /> </Window.Resources>
The above shows how we can set properties on the view model as well.