Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make content dynamic without changing DataContext

Tags:

c#

wpf

I got a MVVM set up to switch between views. To accomodate for the design, the MainWindow holds a tabcontroller which shows a page accordingly. One of the inner pages is changed when the user presses a button. A visual representation of the setup: A visual representation of the setup

I set a Presenter viewmodel to as the datacontext of StudentView to handle the button event thrown in StudentOverview. That works, but when I want to switch the view I have to set a new datacontext of a specific type. But since Presenter is my datacontext, switching it removes the button's functionality.

What I want is to change the datatemplate without relying on the datacontext.

StudentView.xaml

<Page {...}>
    
    <Page.DataContext>
        <viewModels:Presenter/>
    </Page.DataContext>
    <Page.Resources>
        <DataTemplate x:Key="Overview" DataType="{x:Type models:StudentOverviewModel}">
            <local:StudentOverview/>
        </DataTemplate>
        <DataTemplate x:Key="Add" DataType="{x:Type models:StudentAddModel}">
            <local:AddStudentControl/>
        </DataTemplate>
    </Page.Resources>
    <ContentPresenter Content="{Binding}"/>
</Page>

StudentView.xaml.cs

public partial class StudentView : Page
    {
        public StudentView()
        {
            InitializeComponent();

            // This switches the view but disables the button
            this.DataContext = new StudentOverviewModel();

            if (this.DataContext is Presenter presenter)
            {
                presenter.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
                {
                    // This switches the view but disables the button
                    this.DataContext = new StudentAddModel();
                };
            }
        }
    }
like image 863
Demiën Drost Avatar asked Dec 04 '25 18:12

Demiën Drost


1 Answers

I can suggest two solutions:

First solution (recommended) would be to add a SelectedContent property of type object (or any other common base type for all view models e.g., IContentModel) to the Presenter view model. Then bind the SelectedContent to the ContentPresenter.Content property:

Presenter.cs

public partial class Presenter : INotifyPropertyChanged
{
  public Presenter()
  {
    // Set default content
    this.SelectedContent = new StudentOverviewModel();
  }

  private object selectedContent;
  public object SelectedContent
  {
    get => this.selectedContent; 
    set
    { 
      this.selectedContent = value; 
      OnPropertyChanged();
    }
  }

  // Use ICommand implementation like DelegateCommand
  public ICommand LoadContentCommand => new LoadContentCommand(ExecuteLoadContent, CanExecuteLoadContent);

  private void ExecuteLoadContent(object param)
  {
    // Do something ...

    // Load the new content on Button clicked
    this.SelectedContent = new StudentAddModel();
  }

  private bool CanExecuteLoadContent => true;
}

StudentView.xaml.cs

public partial class StudentView : Page
{
  public StudentView()
  {
    InitializeComponent();
  }
}

StudentView.xaml

<Page {...}>
  <Page.DataContext>
    <viewModels:Presenter/>
  </Page.DataContext>
  <Page.Resources>
    <DataTemplate DataType="{x:Type models:StudentOverviewModel}">
      <local:StudentOverview/>
    </DataTemplate>
    <DataTemplate ="{x:Type models:StudentAddModel}">
      <local:AddStudentControl/>
    </DataTemplate>
  </Page.Resources>

  <ContentPresenter Content="{Binding SelectedContent}"/>
</Page>

StudentOverview.xaml

<UserControl{...}>

  <!--- content -->

  <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StudentView}}, Path=DataContext.LoadContentCommand}"/>
</UserControl>

You can safely remove the Key attribute of the DataTemplate (if the DataType is not the same) so that they will apply automatically to any matching data type (implicit DataTemplate).


Another solution would be to move the SelectedContent to StudentView and turn it into a DependencyProperty:

StudentView.xaml.cs

public partial class StudentView : Page
{
  public static readonly DependencyProperty SelectedContentProperty = DependencyProperty.Register(
    "SelectedContent", 
    typeof(object), 
    typeof(StudentView));

  public object SelectedContent
  {
    get => GetValue(SelectedContentProperty); 
    set => SetValue(SelectedContentProperty, value); 
  }

  public StudentView()
  {
    InitializeComponent();

    // This switches the view without disabling the button
    this.SelectedContent = new StudentOverviewModel();

    if (this.DataContext is Presenter presenter)
    {
      presenter.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
      {
        // This switches the view without disabling the button
        this.SelectedContent = new StudentAddModel();               
      };
    }
  }
}

StudentView.xaml

<Page {...}>
  <Page.DataContext>
    <viewModels:Presenter/>
  </Page.DataContext>
  <Page.Resources>
    <DataTemplate DataType="{x:Type models:StudentOverviewModel}">
      <local:StudentOverview/>
    </DataTemplate>
    <DataTemplate ="{x:Type models:StudentAddModel}">
      <local:AddStudentControl/>
    </DataTemplate>
  </Page.Resources>

  <ContentPresenter Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StudentView}}, Path=SelectedContent}"/>
</Page>
like image 110
BionicCode Avatar answered Dec 06 '25 09:12

BionicCode



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!