다음을 통해 공유


WPF: Tips - DataContext Special Source

Sometimes what you've got just isn't tasty enough. You want more. How about some special sauce on that DataContext?

Introduction

Often everything in a Window or UserControl will be bound to the one ViewModel.  You instantiate a ViewModel and set the DataContext of the Window to that.

<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>

Then all the bindings can simply be:

<UIElement Property={Binding DataContextProperty}/>

Which is all very good until you find you need something from some other object.


Container DataContext

If there is a convenient group of controls which will all want to use this  object, you can consider putting these in one container and setting the DataContext on that.

<Grid>
    <Grid.DataContext>
        <local:MyObject/>
    </Grid.DataContext>
    ......
</Grid>

It;s fairly common to have an area of UI all about the one (different) thing but all too often it's just not that easy.
What if some of those Controls in that Grid have to still use the Window's DataContext?


Control DataContext

Although you would usually think in terms of inheriting and everything sharing the one DataContext, you can set the DataContext on each control separately.

<TextBox DataContext="{StaticResource MyObject}" Text="{Binding MyObjectProperty}"

You can rarely instantiate that object separately, it's much more likely instantiated as a resource and you're grabbing that out Application.Current.Resources using that StaticResource syntax there.
All the properties on that TextBox now have that DataContext.
What about if some of those properties need a different DataContext?


Special Source to the Rescue

Well it was a long time coming - we're going with the theory you appreciate things more if you have to wait for them.

You can set the Source on a Binding and that effectively over-rides the DataContext just for that Binding.

FontSize="{Binding fontSize, Source={StaticResource FontDetails}}"
FontFamily="{Binding Path=fontFamily, Source={StaticResource FontDetails}}"  
Title="MainWindow" Height="600" Width="525">

Here you can see Source allows us to go dip into an object way over there in Application.Current.Resources without affecting the DataContext of the window.
We can then set DataContext of that window to a ViewModel but use font settings out a shared resource.


Multiple DataContext

Sometimes getting a reference to that other something-or-other is particularly tricky.
Maybe you have two UserControls with variable ViewModels or some such rather mind stretching scenario.
You don't happen to have a convenient resource declared that you can use.
In this case you can potentially go get a reference to whatever the DataContext of another control is.
This is somewhat mind bending in that you're finding an element and getting the DataContext of that element then you can use some property on whatever that DataContext is.  Often this will of course be a ViewModel.
 

{Binding ElementName=OtherElement,
         Path=DataContext.SomeProperty}

The snippet above relies on using a named element, it goes and finds the SomeProperty on the DataContext that element has.
You can get much more complicated and use a family member of Source - RelativeSource.
RelativeSource can search up the visual tree until it finds a control of a particular type or the specific one (say) 3 levels up.
This fellow is the roaming black sheep of the family and quite a complicated chap.


See Also

WPF Tips
WPF Resources on the Technet Wiki


Other Resources

WPF Tutorial DataBinding 
Windows Presentation Foundation Data Binding: Part 1
Data Binding in WPF