Search code examples
c#wpfmvvmbindingreactiveui

Why is my Child View not displayed when I show my Container View in the MainWindow with WPF ReactiveUI?


I am currently learning my way around WPF and ReactiveUI. I try to explain my problem with a simplified example. I have a ContainerView that contains a ChildView and is bound to a ContainerViewModel. The ContainerViewModel has a property Child that is bound to the ChildView control. The ChildView control has a corresponding ChildViewModel.

I also have a style for my ContainerView that sets the Template property to a ControlTemplate with a Grid that contains a ViewModelViewHost control named "Child".

However, when I show the ContainerView in the MainWindow, the ChildView is not displayed.

Here's the code for the ContainerView/ViewModel and its style:

public class ContainerView : Control, IViewFor<ContainerViewModel>
{
    public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
        nameof(ViewModel), typeof(ContainerViewModel), typeof(ContainerView), new PropertyMetadata(default(ContainerViewModel)));

    public ContainerViewModel ViewModel
    {
        get { return (ContainerViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
    object? IViewFor.ViewModel
    {
        get => ViewModel;
        set => ViewModel = (ContainerViewModel)value;
    }

    public ContainerView()
    {
        this.WhenActivated(d =>
        {
            this.OneWayBind(ViewModel, vm => vm.Child, view => view.Child.ViewModel);
        });
    }

    private ViewModelViewHost Child { get; set; }

    public override void OnApplyTemplate()
    {
        Child = GetTemplateChild(nameof(Child)) as ViewModelViewHost;
    }
}

public class ContainerViewModel : ReactiveObject
{
    
     private ChildViewModel _child;

    
     public ChildViewModel Child
    
     { 
        
         get => _child;
        
         set => this.RaiseAndSetIfChanged(ref _child, value);
    
     }
 
    

     public ContainerViewModel()
    {
        
         Child = new ChildViewModel();
    
     } 

}

<Style TargetType="{x:Type views:ContainerView}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="views:ContainerView">
                <Grid>
                    <reactiveUi:ViewModelViewHost x:Name="Child"></reactiveUi:ViewModelViewHost>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And here's the code for the "ChildView" and its style:

public class ChildView : Control, IViewFor<ChildViewModel>
{
    public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
        nameof(ViewModel), typeof(ChildViewModel), typeof(ChildView), new PropertyMetadata(default(ChildViewModel)));

    public ChildViewModel ViewModel
    {
        get { return (ChildViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
    object? IViewFor.ViewModel
    {
        get => ViewModel;
        set => ViewModel = (ChildViewModel)value;
    }

    public ChildView()
    {
        DefaultStyleKey = nameof(ChildView);
    }
}


<Style TargetType="{x:Type views:ChildView}">
    <Setter Property="Background" Value="Brown"></Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="views:ChildView">
                <Ellipse Width="200" Height="200" Fill="Yellow" ></Ellipse>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Any idea why the ChildView is not displayed?


Solution

  • Ok, I think i got it.. I don't no if this is the best solution, but it works for me. I had to instantiate the ViewModel in the View by myself.

    public ContainerView()
    {
        this.ViewModel = new ContainerViewModel();
        this.WhenActivated(d =>
        {
            this.OneWayBind(ViewModel, vm => vm.Child, view => view.Child.ViewModel);
        });
    }