Pass parameter to a constructor in the ViewModel

后端 未结 1 1670
甜味超标
甜味超标 2021-01-25 10:12

I am building a WPF browser application with MVVM pattern.

I have a first page (ConsultInvoice) with a dataGrid. When I double click on one of the row I want to navigate

1条回答
  •  情话喂你
    2021-01-25 10:32

    Basically you should avoid passing such parameters into the ViewModels constructor, as wiring it with Inversion of Control/Dependency Injection becomes a pain. While you can use Abstract Factory pattern to resolve objects with runtime parameters, it's imho not suitable for ViewModels.

    Instead I always suggest using a form of navigation pattern, similar to how Microsoft's Patterns & Practices team has done with Prism. There you have an INavigationAware interface which your ViewModels can implement. It has 2 methods, NavigateTo and NavigateFrom.

    And there is a navigation service. The navigation service will switch the views and before switching calling NavigateFrom in the current ViewModel (if it implements it. One can use it to check if data is saved and if necessary cancel the navigation. After the new View has been loaded and the ViewModel assigned to it, call NavigateTo in the newly navigated ViewModel.

    Here you'd pass the parameters required for the ViewModel, in your case invoiceId. Try avoid passing whole models or complex objects. Use the invoiceid to fetch the invoice data and to populate your editing ViewModel.

    A basinc implementation from my former answer (can be found here):

    public interface INavigationService 
    {
        // T is whatever your base ViewModel class is called
        void NavigateTo() where T ViewModel;
        void NavigateToNewWindow();
        void NavigateToNewWindow(object parameter);
        void NavigateTo(object parameter);
    }
    
    public class NavigationService : INavigationService
    {
        private IUnityContainer container;
        public NavigationService(IUnityContainer container) 
        {
            this.container = container;
        }
        public void NavigateToWindow(object parameter) where T : IView
        {
            // configure your IoC container to resolve a View for a given ViewModel
            // i.e. container.Register(); in your
            // composition root
            IView view = container.Resolve();
    
            Window window = view as Window;
            if(window!=null)
                window.Show();
    
            INavigationAware nav = view as INavigationAware;
            if(nav!= null)
                nav.NavigatedTo(parameter);
        }
    }
    
    // IPlotView is an empty interface, only used to be able to resolve
    // the PlotWindow w/o needing to reference to it's concrete implementation as
    // calling navigationService.NavigateToWindow(userId); would violate 
    // MVVM pattern, where navigationService.NavigateToWindow(userId); doesn't. There are also other ways involving strings or naming
    // convention, but this is out of scope for this answer. IView would 
    // just implement "object DataContext { get; set; }" property, which is already
    // implemented Control objects
    public class PlotWindow : Window, IView, IPlotView
    {
    }
    
    public class PlotViewModel : ViewModel, INotifyPropertyChanged, INavigationAware
    {
        private int plotId;
        public void NavigatedTo(object parameter) where T : IView
        {
            if(!parameter is int)
                return; // Wrong parameter type passed
    
            this.plotId = (int)parameter;
            Task.Start( () => {
                // load the data
                PlotData = LoadPlot(plotId);
            });
        }
    
        private Plot plotData;
        public Plot PlotData {
            get { return plotData; }
            set 
            {
                if(plotData != value) 
                {
                    plotData = value;
                    OnPropertyChanged("PlotData");
                }
            }
        }
    }
    

    An example of the INavigationAware interface used in Prism can be found on the projects github repository.

    This makes it easy to pass parameter and async load your data (where there isn't any clean way to do this via constructor, as you can't await an async operation inside the constructor without locking, and doing this kind of things in the constructor is very discouraged).

    0 讨论(0)
提交回复
热议问题