Skip to main content

ViewModel relationships (ISupportParentViewModel)

  • 4 minutes to read

View Models inherited from ViewModelBase can be related to each other with a parent-child relationship. This is achieved with the ISupportParentViewModel interface, implemented in the ViewModelBase class. If you use the POCO mechanism, the ISupportParentViewModel interface is automatically implemented when you create a POCO object with the ViewModelSource class.

The ISupportParentViewModel interface is shown below.

public interface ISupportParentViewModel {
    object ParentViewModel { get; set; }
}

When you create a Detail View Model from another View Model, you can pass the main View Model to the detail using the ISupportParentViewModel.ParentViewModel property. This allows the detail View Model to access Services that are registered for the main View Model.

public class DetailViewModel : ViewModelBase {
    public IMessageBoxService MessageBoxService { get { return GetService<IMessageBoxService>(); } }
}
public class MainViewModel : ViewModelBase {
    public IMessageBoxService MessageBoxService { get { return GetService<IMessageBoxService>(); } }
    public DetailViewModel DetailViewModel { get; private set; }
    public MainViewModel() {
        DetailViewModel = new DetailViewModel();
        ((ISupportParentViewModel)DetailViewModel).ParentViewModel = this;
    }
}

Note

Services (for example, the IDocumentManagerService, the INavigationService, and so on) contain methods with the parentViewModel parameter. This parameter allows you to pass a parent view model to the service.

The ParentViewModel parameter can be set in XAML with the ViewModelExtensions.ParentViewModel attached property. The scheme demonstrating how the Main ViewModel is propagated to a Detail ViewModel is shown in the following diagram:

ISupportParentViewModel-scheme

Here, the Main View contains an instance of a Detail View. The DataContexts of each are the Main ViewModel and Detail ViewModel respectively. The task is to pass the Main ViewModel to the Detail ViewModel. To accomplish this, assign the Main ViewModel to the ViewModelExtensions.ParentViewModel attached property in the Detail View instance.

<UserControl x:Class="Example.View.MainView" ...
    xmlns:ViewModel="clr-namespace:Example.ViewModel"
    xmlns:View="clr-namespace:Example.View"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm">
    <UserControl.DataContext>
        <ViewModel:MainViewModel/>
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot">
        ...
        <!-- It is necessary to use the ElementName binding mode, because the DetailView's DataContext is a DetailViewModel object. 
        That is why the following regular binding cannot be used in this case: dxmvvm:ViewModelExtensions.Parameter="{Binding}"
        Instead, use one of the following constructions:--> 
        <View:DetailView dxmvvm:ViewModelExtensions.ParentViewModel="{Binding DataContext, ElementName=LayoutRoot}"/>
        <ContentControl>
            <ContentControl.ContentTemplate>
                <DataTemplate>
                    <View:DetailView dxmvvm:ViewModelExtensions.ParentViewModel="{Binding DataContext, Source={x:Reference LayoutRoot}}"/>
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl>
        ...
    </Grid>
</UserControl>

The ViewModelExtensions class, which exposes the ParentViewModel attached property in the Detail View instance, performs all necessary actions to pass data to the Detail ViewModel. When the ParentViewModel attached property is set on the Detail View, the ViewModelExtensions class identifies whether the View’s DataContext is a ViewModel (a ViewModelBase object; or to be more precise, whether the DataContext is an object that implements the ISupportParentViewModel interface). And if so, the new value is passed to the Detail View’s ViewModel (it is assigned to the ViewModel’s ISupportParentViewModel.ParentViewModel private property). As a result of changing the ParentViewModel private property, the Detail ViewModel calls its OnParentViewModelChanged protected method, which you can manually override to obtain and use the Main ViewModel.

The following documentation topic contains an example of how to use services when View Models are related to each other with the parent-child relationship: DXMessageBoxService.

You can access the specified parent view model after the corresponding View is loaded. Handle the View’s Loaded event (you can use the EventToCommand behavior) to obtain the parent view model:

<UserControl ...
    x:Class="Example.View.MainView"
    xmlns:ViewModel="clr-namespace:Example.ViewModel"
    xmlns:View="clr-namespace:Example.View"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm">
    <UserControl.DataContext>
        <ViewModel:MainViewModel/>
    </UserControl.DataContext>

    <Grid x:Name="LayoutRoot">
        <TextBlock Text="ChildView: "/>
        <View:ChildView dxmvvm:ViewModelExtensions.ParentViewModel="{Binding DataContext, ElementName=LayoutRoot}">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding OnViewLoadedCommand}" />
            </dxmvvm:Interaction.Behaviors>
        </View:ChildView>
    </Grid>
</UserControl>
public class ChildViewModel : ViewModelBase {
    public ChildViewModel() {
        // Returns null:
        var parentViewModel = ((ISupportParentViewModel)this).ParentViewModel;
    }
    [Command]
    public void OnViewLoaded() {
        // Returns MainViewModel:
        var parentViewModel = ((ISupportParentViewModel)this).ParentViewModel;
    }
}