简体   繁体   中英

Trouble with showing a Mahapps.Metro Dialog with a ReactiveUi Command

I am trying to use the Mahapps dialog boxes in my project but have been unable to get them to work when trigger from a ReactivUI command in my ViewModel.In the view's XAML, I have registered the dialog.

 xmlns:dialogs="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
    dialogs:DialogParticipation.Register="{Binding}"

I also have a button which is bound to the ShowDialog command.

this.BindCommand(viewModel, vm => vm.ShowDialog, x => x.button);

Finally, in my ViewModel, I have the RxUI command and the dialogcoordinator instance set.

 public MainWindowViewModel(IDialogCoordinator dialogCoordinator)
    {

        _dialogCoordinator = dialogCoordinator;

        ShowDialog = ReactiveCommand.CreateFromTask(async () =>
        {
            await _dialogCoordinator.ShowMessageAsync(this, "Message from VM", "MVVM based dialogs!");
        });

        ShowDialog.ThrownExceptions.Subscribe(ex => Console.WriteLine(ex.ToString()));

    }

No matter what I have tried it always throw the same error which is

System.InvalidOperationException: Context is not registered. Consider using DialogParticipation.Register in XAML to bind in the DataContext.

I am not sure if there is anything else needed to get the dialog to work or if I am just using the commands in RxUI incorrectly

I had the same problem with another MVVM framework (MVVM Light Toolkit). But the framework doesn't seem the problem. It's a matter of timing. The DialogCoordinator isn't accessable from the constructor. I personally moved all the code from the constructor to RelayCommand that is being fired

<controls:MetroWindow
x:Class="UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dialog="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
dialog:DialogParticipation.Register="{Binding}"
DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
d:DataContext="{d:DesignInstance vm:MainViewModel}"
mc:Ignorable="d">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="ContentRendered">
        <i:InvokeCommandAction Command="{Binding StartupCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

I tried different events like Loaded, which worked in some cases but "ContentRendered" always worked in the VM I have the following code:

    public ICommand StartupCommand
    {
        get
        {
            return new RelayCommand(async ()=>
            {
                this.AccountName = await
                        dialogCoordinator.ShowInputAsync(this, "Welcome", "Please insert your account name");                    
            });
        }
    }

So basically it seems to be not yet registered directly but after the content is loaded it is. (In this example i used ShowInputAsync but it should work the same with ShowMessageAsync)

It does seem to be a timing issue - when I first started playing with this, I got it to work by setting the VM's DialogCoordinator instance from the View's Loaded event, rather than it's constructor.

I've put together a minimal working application demonstrating this at https://github.com/dwarry/ReactiveUiMahAppsDialog . It uses ReactiveUi's normal Interaction mechanism to trigger displaying the dialog. In keeping with that, I've put this in the View, but I don't see why it couldn't be adapted to do it from the ViewModel if you'd rather. One other thing to be aware of is that you need to wait for the dialog to close before leaving the Interaction Handler, or ReactiveUI will throw an UnhandledInteractionException.

For me, using the ContentRendered event did not do anything; I still got the exception InvalidOperationException: Context is not registered. Consider using DialogParticipation.Register in XAML to bind in the DataContext InvalidOperationException: Context is not registered. Consider using DialogParticipation.Register in XAML to bind in the DataContext .

I based my solution on Kent Boogaart's book samples .

View:

using System.Reactive;
using System.Reactive.Disposables;
using System.Windows;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using ReactiveUI;
using Splat;

namespace ReactiveUIMahAppsDialog
{
    public partial class ChildView
    {
        public ChildView()
        {
            InitializeComponent();

            this.WhenActivated(disposables =>
            {
                ViewModel ??= Locator.Current.GetService<ChildViewModel>() ?? new ChildViewModel();

                this.BindCommand(ViewModel,
                        viewModel => viewModel.LogInUser,
                        view => view.Test)
                    .DisposeWith(disposables);

                this.ViewModel.Message
                    .RegisterHandler(async interaction =>
                    {
                        MetroWindow window = (MetroWindow) Window.GetWindow(this);

                        await window.ShowMessageAsync("test", "test");

                        interaction.SetOutput(Unit.Default);
                    })
                    .DisposeWith(disposables);
            });
        }
    }
}

<reactiveUi:ReactiveUserControl
    x:Class="ReactiveUIMahAppsDialog.ChildView"
    x:TypeArguments="viewModels:ChildViewModel"
    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:reactiveUi="http://reactiveui.net"
    xmlns:viewModels="clr-namespace:ReactiveUIMahAppsDialog"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Button x:Name="Test" Content="Show Dialog" Width="100" Height="100"/>
    </Grid>
</reactiveUi:ReactiveUserControl>

Host the reactive user control in a MetroWindow .

ViewModel:

using System.Reactive;
using System.Reactive.Linq;
using ReactiveUI;

namespace ReactiveUIMahAppsDialog
{
    public class ChildViewModel : ReactiveObject
    {
        public ChildViewModel()
        {
            Message = new Interaction<Unit, Unit>();

            LogInUser = ReactiveCommand.CreateFromTask(async _ => await Message.Handle(Unit.Default));
        }

        public ReactiveCommand<Unit, Unit> LogInUser { get; }

        public Interaction<Unit, Unit> Message { get; }
    }
}

Full code can be found here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM