Navigate between UserControls with Event Aggegator

2019-08-08 20:30发布

I have a MainWindow where I navigate between UserControls by clicking on a menu and it works fine.

I am using this following pattern:

https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/

In one of those usercontrol there is a button. By clicking on this button I want to navigate to another usercontrol.

How do I do that?

MainView

<UserControl.Resources>
    <DataTemplate DataType="{x:Type cvm:FirstViewModel}">
        <cv:FirstView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type cvm:SecondViewModel}">
        <cv:SecondView/>
    </DataTemplate>
    <cvm:MainViewModel x:Key="main"/>
</UserControl.Resources>

<Grid DataContext="{Binding Source={StaticResource main}}">
    <Border Grid.Row="0">
        <Menu Height="58">
             <ItemsControl ItemsSource="{Binding PageViewModels}" Width="289" Height="58">
                 <ItemsControl.ItemTemplate>
                     <DataTemplate>
                         <TextBlock>
                             <Hyperlink Command="{Binding ChangePageCommand, Mode=OneWay, Source={StaticResource main}}" CommandParameter="{Binding}" TextDecorations="{x:Null}">
                                 <InlineUIContainer>
                                     <TextBlock Text="{Binding Name}"/>
                                 </InlineUIContainer>
                             </Hyperlink>    
                         </TextBlock>
                      </DataTemplate>
                 </ItemsControl.ItemTemplate>
             </ItemsControl>
        </Menu>
    </Border>       
    <Border Grid.Row="1" >
        <ContentControl Content="{Binding CurrentUserControl}"/>
    </Border>
</Grid>

MainViewModel

public class MainViewModel : ViewModelBase
{
        public MainViewModel()
        {
            PageViewModels.Add(new FirstViewModel());
            PageViewModels.Add(new SecondViewModel());

            // Set starting page
            CurrentUserControl = PageViewModels[0];
        }

        #region Fields

        private List<IUserContentViewModel> _pageViewModels;
        public List<IUserContentViewModel> PageViewModels
        {
            get
            {
                if (_pageViewModels == null)
                    _pageViewModels = new List<IUserContentViewModel>();

                return _pageViewModels;
            }
        }

        private IUserContentViewModel _currentUserControl;
        public IUserContentViewModel CurrentUserControl
        {
            get { return _currentUserControl; }
            set
            {
                if (value != _currentUserControl)
                {
                    _currentUserControl = value;
                    OnPropertyChanged("CurrentUserControl");
                }
            }
        }

        #region Methods

        private void ChangeViewModel(IUserContentViewModel viewModel)
        {
            if (!PageViewModels.Contains(viewModel))
                PageViewModels.Add(viewModel);

            CurrentUserControl = PageViewModels
                .FirstOrDefault(vm => vm == viewModel);


        }

        #endregion

        private ICommand _changePageCommand;
        #endregion
        public ICommand ChangePageCommand
        {
            get
            {
                if (_changePageCommand == null)
                {
                    _changePageCommand = new RelayCommand(
                        p => ChangeViewModel((IUserContentViewModel)p),
                        p => p is IUserContentViewModel);
                }

                return _changePageCommand;
            }
        }
    }

SecondView

<Grid Background="Blue">
    <Button /> <!-- Going to ThirdView?????????-->
</Grid>

2条回答
Emotional °昔
2楼-- · 2019-08-08 20:40

You have to call the ChangePageCommand from your button:

<Button DataContext="{Binding Source={StaticResource main}}"
        Command="{Binding ChangePageCommand"} 
        CommandParameter="{Binding PageViewModels[2]}">

I am assuming that you have your FirstViewModel stored at PageViewModels[0], and your SecondViewModel at PageViewModels[1].

You also have to create a ThirdView link to your ThirdViewModel, as your other two Views/ViewModels:

<UserControl.Resources>
    ...
    <DataTemplate DataType="{x:Type cvm:ThirdViewModel}">
        <cv:ThirdView/>
    </DataTemplate>
</UserControl.Resources>

And just as an advice, you can set your UserControl DataContext at the start of your code, instead of using it in any UIElement(Button and Grid in your case), like this:

<UserControl.DataContext>
    <cvm:MainViewModel />
</UserControl.DataContext>

EDIT>>>>

Forgot to say that you also have to add your ThirdViewModel to your PageViewModels collection:

PageViewModels.Add(new ThirdViewModel());
查看更多
走好不送
3楼-- · 2019-08-08 20:51

I finally have my solution.

I use Event Aggregator with Prism 6.

First I create a Singleton.

internal sealed class ApplicationService
    {
        private ApplicationService() { }

        private static readonly ApplicationService _instance = new ApplicationService();

        internal static ApplicationService Instance { get { return _instance; } }

        private IEventAggregator _eventAggregator;
        internal IEventAggregator EventAggregator
        {
            get
            {
                if (_eventAggregator == null)
                    _eventAggregator = new EventAggregator();

                return _eventAggregator;
            }
        }
    }

Then public class GoToThird : PubSubEvent<TEvent> { }

In MainViewModel I subscribe to the event and add my ThirdViewModel().

public class MainViewModel : ViewModelBase
{
        protected readonly IEventAggregator _eventAggregator;

        public MainViewModel(IEventAggregator eventAggregator)
        {
            PageViewModels.Add(new FirstViewModel());
            PageViewModels.Add(new SecondViewModel(ApplicationService.Instance.EventAggregator)));

            PageViewModels.Add(new ThirdViewModel());

            // Set starting page
            CurrentUserControl = PageViewModels[0];

            this._eventAggregator = eventAggregator;
        }

        private void GoToThird()
        {
            CurrentUserControl = PageViewModels[2];
        } 
}

At the end I publish the event in SecondViewModel()

public class SecondViewModel
    {

        protected readonly IEventAggregator _eventAggregator;

        public SecondViewModel(IEventAggregator eventAggregator)
        {
            this._eventAggregator = eventAggregator;

        }



        private void Go()
        {
            _eventAggregator.GetEvent<GoToThird>().Publish();
        }


        private ICommand goToThirdCommand;

        public ICommand GoToThirdCommand
        {
            get
            {
                return goToThirdCommand ?? (goToThirdCommand = new RelayCommand(p => this.Go(), p => this.CanGo()));
            }
        }

        private bool CanGo()
        {
            return true;
        }
}

Big Thanks to Rachel and Kirenenko

查看更多
登录 后发表回答