Suppose you have a window like this:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
And you set the DataContext in the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Now remember that the DataContext is an inheritable dependency property, i.e. it flows down. (In general, dependency properties are not inheritable by default, unless you explicitly state it)
So, you set the DataContext once on the root of the logical tree (the window) and all of its children will "see" it. (the UniformGrid and the custom controls in our case)
Yes, that means you can directly bind to the view model in your user control's XAML:
<UserControl x:Class="Example.MyUserControlA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding PropertyFromMainViewModel}"/>
</Grid>
</UserControl>
Now, this approach works well, until your control gets so complicated that it needs to have its own ViewModel and DataContext reespectively.
Usually this happens when the control is not a passive, but maintains a state (validates input, button state, etc.)
1.Declare all properties that you want to bind to the main view model as dependency properties and pay attention to the default value you specify.
2.Locate the main panel of your UserControl and name it, for example "LayoutRoot":
<UserControl x:Class="Example.MyUserControlA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding MyDependencyProperty}"/>
</Grid>
</UserControl>
3.Now, you set the DataContext on the LayoutRoot
public MyUserControlA()
{
InitializeComponent();
LayoutRoot.DataContext = new MyUserControlViewModel();
}
4.You bind to the main view model in this way
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA MyDependencyProperty="{Binding MainViewModelProperty}"/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
The other way around is to bind using RelativeSource, but this would break the encapsulation and reusability of your UserControl.
WPF has a steep learning curve, I hope my tips were helpful...