1

We have an existing WPF application that uses PRISM (6.x) with Unity (3.5.x) for DI. We are going to be adding more functionality to this application and would like to start utilizing ReactiveUI for any new modules that we need to implement.

We were hoping to minimize the learning curve and continue to utilize Unity and DI for our ViewModels; however, ReactiveUI uses Splat for View location and so far I have not been able to get my ReactiveUI module to actually display in a PRISM region within our main application regardless of what I have tried.

The only article that I have really found was this post Issues with IMutableDependencyResolver and Structuremap in ReactiveUI where someone was writing their own integration.

So given some ViewModel:

public class HelloWorldViewModel : ReactiveObject 
{
    string title = "Hello ReactiveUI";
    public string Title
    {
        get { return title; }
        set { this.RaiseAndSetIfChanged(ref title, value); }
    }
}

And its corresponding View:

<UserControl x:Class="PBI.ObjectMover.ReactSample.Views.HelloWorldView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Grid>
    <TextBlock Text="{Binding Title}" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>            
  </Grid>
</UserControl>

And the view's code behind implementing the IViewFor<T> interface:

public partial class HelloWorldView : UserControl, IViewFor<HelloWorldViewModel>
{
    public HelloWorldView()
    {
        InitializeComponent();

        this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);
    }

    public HelloWorldViewModel ViewModel
    {
        get { return (HelloWorldViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(HelloWorldViewModel), typeof(HelloWorldView), new PropertyMetadata(null));

    object IViewFor.ViewModel { get; set; }
}

What is needed in the IModule initialization to get the ReactiveUI ViewModel and View hooked up?

public class ReactSampleModule : IModule
{
    private readonly IRegionManager RegionManager;
    public ReactSampleModule(IUnityContainer container, IRegionManager regionManager)
    {
        this.RegionManager = regionManager;
    }

    public void Initialize()
    {
        /* What do I need here to initialize ReactiveUI or Unity? */

        /* Register views */
        this.RegionManager.RegisterViewWithRegion("RightRegion", typeof(HelloWorldView)); 
    }
}

I have tried adding the prism locator to the View's XAML:

<UserControl x:Class="PBI.ObjectMover.ReactSample.Views.HelloWorldView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:prism="http://prismlibrary.com/"
         prism:ViewModelLocator.AutoWireViewModel="True">

I have also tried initializing Splat in the module's initialize:

    public void Initialize()
    {
        /* Register types */
        Locator.CurrentMutable.InitializeSplat();
        Locator.CurrentMutable.InitializeReactiveUI();
        Locator.CurrentMutable.Register(() => new HelloWorldView(), typeof(IViewFor<HelloWorldViewModel>));

        /* Register views */
        this.RegionManager.RegisterViewWithRegion("RightRegion", typeof(HelloWorldView)); 
    }

So far nothing has worked but I doubt that we are the only ones that see the benefit of using PRISM's modular architecture with ReactiveUI's framework.

Community
  • 1
  • 1
jigamiller
  • 513
  • 6
  • 7

1 Answers1

3

Prism and RxUI and different semantics on view binding.

Prism tries to resolve a VM type for a given view, whereas RxUI does it the other way around and resolves View for the current VM.

Your implementation is (almost) working:

this.RegionManager.RegisterViewWithRegion("RightRegion", typeof(HelloWorldView));

This is enough to display the view, and if you add AutoWireViewModel=True, Prism will create a VM instance and set it to your view DataContext.

Beware that this line:

this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);

Attempts to bind the ViewModel property to the same DataContext, and will happily do so with null, overriding the instance created/set by Prism (here's your issue I believe).

Simply removing this binding works, but overall it doesn't seem a nice way of mixing Prism and RxUI.

Maybe instead you could look into RxUI RoutedViewHost and ViewModelViewHost and bind one of these into the region instead. Then RxUI view resolving will kick in automatically, and refresh the inner views depending on the current VM set on the control. This is when you need to register your views, like you did:

Locator.CurrentMutable.Register(() => new HelloWorldView(), typeof(IViewFor<HelloWorldViewModel>));
Gluck
  • 2,933
  • 16
  • 28