简体   繁体   中英

Show dialog from ViewModel in ReactiveUI Avalonia (MVVM)

I have Window , inheriting from ReactWindow , and it's connected to its Viewmodel .

Everything is working as expected (binding, clicking stuff, hitting commands). So it's working. (code is below)

When wanting to display a file browser dialog, the API wants a Window object as parameter of ShowAsync(window) which I do not have in the ViewModel class.

An option would be to do everything from the window itself but that defeats the purpose of MVVM, so my other option is to forward the view to the viewmodel, but that seems odd as well.

I also tried (as you can see in the code below) adding a parameter to the command so the view can forward it, but you can't provide a parameter when you set it up in WhenActivated , so I'm kinda stuck here.

What do I do ?

Here's the viewmodel

 public class BatchManagementViewModel : ViewModelBase
    {
       
        public string BrowseText => "Browse/Edit files...";

        public ReactiveCommand<BatchManagementView, Unit> BrowseCommand { get; set; }

        public BatchManagementViewModel()
        {   
           BrowseCommand = ReactiveCommand.CreateFromTask( async () => ShowFileDialog(/*THE VIEW HERE ?*/));
        }
        
        async Task<string[]?> ShowFileDialog(BatchManagementView view)
        {
            // view is null here so it obviously crashes on ShowAsync
            //but it's a param of the Command
            var dialog = new OpenFileDialog();
            dialog.AllowMultiple = true;
            dialog.Title = DialogTitle;
            dialog.Filters.AddRange(
                new FileDialogFilter[]
                {
                    new() {Name = "image files", Extensions = { ".png", ".jpg", ".jpeg", ".tiff" }}
                }
            );
                
            return await dialog.ShowAsync(view);
        }    
    }

The view/window

public partial class BatchManagementView : ReactiveWindow<BatchManagementViewModel>
{
    public BatchManagementView()
    {
        InitializeComponent();

        this.WhenActivated(d =>
        {
            this.OneWayBind(ViewModel, 
                    viewModel => viewModel.BrowseText, 
                    view => view.BrowseButton.Content)
                .DisposeWith(d);
            
            this.OneWayBind(ViewModel, 
                    viewModel => viewModel.BrowseCommand, 
                    view => view.BrowseButton.Command)
                .DisposeWith(d);
        });
    }
}

What am I supposed to do here? I feel like I'm trying to achieve something simple but I can't find an example with reactive / MVVM in the documentation.

It is possible to retrieve the main window from any place in your application:

var mainWindow = Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop ? desktop.MainWindow : null;

Then if you wish to keep up with the MVVM pattern you could move this implementation of displaying a dialog to a view service .

Read up on interactions. Basically what you do is create an interaction in your viewmodel that includes the inputs to the dialog and the outputs. Then in the code behind of the window you register a handler to this interaction which handles opening the dialog. See here: https://docs.avaloniaui.net/tutorials/music-store-app/opening-a-dialog

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