Mrb
Mrb

Reputation: 21

How to use ReactiveUI MessageBus Pattern to comunicate between two UserControls?

I'm trying to work with ReactiveUI framework in wpf project. In detail I'm trying to use the MessageBus pattern to comunicate between two different viewmodel. In the simplified example below I try to update MainWindowView Label when I click on the UserControl Button insert into MainWindowView. The Button click is managed by ReactiveUI BindCommand that call UserControlViewModel method. This method send a message by MessageBus ReactiveUI mechanism. The MainWindowViewModel listen message and update the Label caption on MainWindowView that show the title of the window. I don't know why this doesn't work.

Below there are the the code:

MainWindowView (Xaml):

<rxui:ReactiveWindow x:Class="TestMessageBus.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestMessageBus"
        xmlns:rxui="http://reactiveui.net"
        x:TypeArguments="local:MainWindowViewModel"             
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <Label x:Name="TitleLabel" HorizontalAlignment="Center"/>
        <local:UserControlView/>
    </StackPanel>
</rxui:ReactiveWindow>

MainWindowView (Code Behind):

namespace TestMessageBus
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindowView : ReactiveWindow<MainWindowViewModel>
    {
        public MainWindowView()
        {
            InitializeComponent();

            ViewModel = new MainWindowViewModel();

            this.WhenActivated(disposable =>
            {
                this.OneWayBind(ViewModel, vm => vm.Title, v => v.TitleLabel.Content)
                    .DisposeWith(disposable);
            });
        }
    }
}

MainWindowViewModel:

namespace TestMessageBus
{
    public class MainWindowViewModel : ReactiveObject
    {
        public string Title { get; set; }

        public MainWindowViewModel()
        {

            MessageBus.Current.Listen<string>("Contract1").Subscribe(x => MessageSend(x));

        }

        private void MessageSend(string title)
        {
            Title = title;
        }
    }
}

UserControlView (Xaml):

<rxui:ReactiveUserControl x:Class="TestMessageBus.UserControlView"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TestMessageBus"
             xmlns:rxui="http://reactiveui.net"
             x:TypeArguments="local:UserControlViewModel" 
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="200">
    <Grid>
        <Button x:Name="ButtonTest">TEST</Button>
    </Grid>
</rxui:ReactiveUserControl>

UserControlView (Code behind):

namespace TestMessageBus
{
    /// <summary>
    /// Interaction logic for UserControlView.xaml
    /// </summary>
    public partial class UserControlView : ReactiveUserControl<UserControlViewModel>
    {
        public UserControlView()
        {
            InitializeComponent();

            ViewModel = new UserControlViewModel();

            this.WhenActivated(disposable =>
            {
                this.BindCommand(ViewModel, vm => vm.ClickCommand, v => v.ButtonTest).DisposeWith(disposable);
            });
        }
    }
}

UserControlViewModel:

namespace TestMessageBus
{
    public class UserControlViewModel: ReactiveObject
    {
        public ReactiveCommand<Unit, Unit> ClickCommand { get; set; }

        public UserControlViewModel()
        {
            ClickCommand = ReactiveCommand.Create(ClickButton);


        }

        private void ClickButton()
        {
            MessageBus.Current.SendMessage("TITOLO", "Contract1");
        }
    }
}

I would expect to see the "TITOLO" string in the MainWindow Label but this doesn't happen. Someone can explain me why? Thanks.

Upvotes: 0

Views: 1673

Answers (2)

Christian Findlay
Christian Findlay

Reputation: 7692

This is one potential approach, but I'm not sure if it fits with Reactive UI best practice. The issue for me is that Subject smashes the IObservable and the IObserver together.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        private static string GetSomeData() => "Hi";

        [TestMethod]
        public async Task TestMethod8()
        {
            var subject = new Subject<string>();

            //Create a class and inject the subject as IObserver
            new Publisher(subject);

            //Create a class and inject the subject as IObservable
            new Subscriber(subject);
            new Subscriber(subject);
            new Subscriber(subject);

            //Run the loop for 10 seconds
            await Task.Delay(10000);
        }

        class Publisher
        {
            public Publisher(IObserver<string> observer)
            {
                Task.Run(async () =>
                {
                    //Loop forever
                    while (true)
                    {
                        //Get some data, publish it with OnNext and wait 500 milliseconds
                        observer.OnNext(GetSomeData());
                        await Task.Delay(500);
                    }
                });
            }
        }

        class Subscriber
        {
            //Listen for OnNext and write to the debug window when it happens
            public Subscriber(IObservable<string> observable) => observable.Subscribe((s) => Debug.WriteLine(s));
        }
    }
}

Upvotes: 0

Rodney Littles
Rodney Littles

Reputation: 564

First I want to note, that even though the framework provides a MessageBus implementation, we don't recommend it's use.

I don't see you Registering a message source anywhere in your example.

You should have a call somewhere to RegisterMessageSource which takes an IObservable and in this case I would gather it should be your button click.

something like:

MessageBus.Current.RegisterMessageSource(ButtonTest.Events().Clicked)

Where Events().Clicked is just an Observable wrapper around the button click event.

There is an example on the ReactiveUI Website

Upvotes: 1

Related Questions