Search code examples
c#wpfxamldatacontext

DataContext is not populated inside a local user control


I have the following files, App.xaml, DataTemplates.xaml, ExcelReport.xaml and MainWindow.xaml and their code behind .cs file, all on the same folder.

Here are the contents of the files:

DataTemplates.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   ...
                    xmlns:local="clr-namespace:WpfApplication1">

    <DataTemplate DataType="{x:Type local:ExcelReportVM}">
        <local:ExcelReport DoubleClickHandler="{Binding}">
          </local:ExcelReport>
    </DataTemplate>
</ResourceDictionary>

App.xaml

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            ....
             xmlns:local="clr-namespace:WpfApplication1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="DataTemplates.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

ExcelReport.xaml

<UserControl x:Class="WpfApplication1.ExcelReport"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            ....
             xmlns:local="clr-namespace:WpfApplication1"
             mc:Ignorable="d" 
             xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <DataTemplate x:Key="HeaderTemplate">
            <TextBlock Text="{Binding .}"  />
        </DataTemplate>
    </UserControl.Resources>
    <syncfusion:SfDataGrid ItemsSource="{Binding Entries}" x:Name="grid">
    </syncfusion:SfDataGrid>
</UserControl>

ExcelReport.xaml.cs

 public partial class ExcelReport : UserControl
{
        public ExcelReport()
        {
            InitializeComponent();
            this.Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if(DataContext==null)
            {
                   throw new Exception("DataContext shouldn't be null");
            }
        }

}

MainWindow.xaml

<Syncfusion:RibbonWindow x:Class="WpfApplication1.MainWindow"
        ....
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" WindowState="Maximized"
        Syncfusion:SkinStorage.VisualStyle="Office2013"
        xmlns:Syncfusion="http://schemas.syncfusion.com/wpf">

    <Grid x:Name="ExcelReport">
        <local:ExcelReport />
    </Grid>
</Syncfusion:RibbonWindow>

However when I launch the application, I found that the DataContext for ExcelReport is always null, even in the onLoaded method. I would expect that since I already declare the DataTemplate in DataTemplates.xaml, then the ExcelReport DataContext will be ExcelReportVM. Why this is not the case?


Solution

  • A DataTemplate only specifies how WPF will display a particular object. It doesn't actually create the object (in this case the VM).

    You will still need to set the DataContext in your view to be a new object of type ExcelReportVM.

    public ExcelReport()
    {
        InitializeComponent();
        DataContext = new ExcelReportVM();
    
        this.Loaded += OnLoaded;
    }
    

    Refer to this StackOverflow question for a more in-depth answer: https://stackoverflow.com/questions/2407917/what-is-the-difference-between-datatemplate-and-datacontext-in-wpf