Search code examples
c#wpfmvvmdatacontext

WPF MVVM: Postpone rendering of View until DataContext is set


In our MVVM application, in a View, DataContext is initially null and is set later. The View is first rendered without the DataContext set, so for bindings the default or FallbackValues are used. This causes a lot of changes in the UI once the DataContext is set and all bindings are updated. The UI is a bit 'bouncy' and I can imaging that quite a few CPU cycles are wasted. Is there a way to postpone rendering of the View until the DataContext is set?

Our code to find a View for a ViewModel:

<ContentControl
     DataContext="{Binding Viewodel}"
     Content="{Binding}"
     Template="{Binding Converter={converters:ViewModelToViewConverter}}"/>

ViewModelToViewConverter.cs:

  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
     ViewModel viewModel = value as ViewModel;

     if (viewModel == null)
     {
        return null;
     }

     string modelName = viewModel.ToString();

     string mappingId = viewModel.MappingId;
     if (!string.IsNullOrEmpty(mappingId))
     {
        modelName += "_" + mappingId;
     }

     ControlTemplate controlTemplate = new ControlTemplate();

     MappingEntry mappingEntry = ApplicationStore.SystemConfig.GetMappingEntryOnModelName(modelName); // lookup View definition for ViewModel

     Type type = mappingEntry != null ? mappingEntry.ViewType : null;

     if (type != null)
     {
        controlTemplate.VisualTree = new FrameworkElementFactory(type);
     }
     else
     {
        Logger.ErrorFormat("View not found: {0}", modelName);
     }

     return controlTemplate;
  }

Solution

  • Yes, you can do that

    1. Using FrameworkElement.DataContextChanged event.

    2. Using Trigger.

      Schematic sample eg;

      <ContentControl>
      <ContentControl.Resources>
          <DataTemplate x:Key="MyTmplKey">
              <TextBlock Text="Not null"/>
          </DataTemplate>
          <DataTemplate x:Key="DefaultTmplKey">
              <StackPanel>
                  <TextBlock Text="null"/>
                  <Button Content="Press" Click="Button_Click_1"/>
              </StackPanel>
          </DataTemplate>
      </ContentControl.Resources>
      <ContentControl.Style>
          <Style TargetType="ContentControl">
              <Setter Property="ContentTemplate" Value="{StaticResource MyTmplKey}"/>
              <Style.Triggers>
                  <Trigger Property="DataContext" Value="{x:Null}">
                      <Setter Property="ContentTemplate" Value="{StaticResource DefaultTmplKey}"/>
                  </Trigger>
              </Style.Triggers>
          </Style>
      </ContentControl.Style>
      </ContentControl>