Change View from Ribbon inside a Module

Topics: Prism v4 - WPF 4
Jan 31, 2013 at 10:14 PM
Edited Jan 31, 2013 at 10:28 PM
OK,
Lets say this is my ModuleA.cs:
    public class MyModuleA : IModule
    {
        private readonly IUnityContainer _container;
        private readonly IRegionManager _regionManager;

        public MyModuleA (IUnityContainer container, IRegionManager regionManager)
        {
            _container = container;
            _regionManager = regionManager;
        }

        public void Initialize()
        {
            // Register ViewModels
            _container.RegisterType<MyModuleAViewModel>();

            //Register Views
            _container.RegisterType<Object, MyModuleARibbonTab>();
            _container.RegisterType<Object, MyModuleAView>();
            _regionManager.RegisterViewWithRegion("RibbonRegion", ResolveRibbonTabView);
            _regionManager.RegisterViewWithRegion("MainRegion", ResolveMainView);
        }

        private object ResolveRibbonTabView()
        {
            return _container.Resolve<MyModuleARibbonTab>();
        }

        private object ResolveMainView()
        {
            return _container.Resolve<MyModuleAView>();
        }
    }
and this is my ModuleAViewModel.cs:
    public class MyModuleAViewModel : NotificationObject
    {
        public MyModuleAViewModel()
        {
            SaveCommand = new DelegateCommand(Save, CanSave);
        }


        private string _message = "Hello from My Module!";

        public string Message
        {
            get { return _message; }
            set
            {
                if (_message != value)
                {
                    _message = value;
                    RaisePropertyChanged(() => Message);
                }
            }
        }

        public static ICommand SaveCommand { get; private set; }
        
        public void Save()
        {
            this.Message = "Save Command Executed.";
        }

        public bool CanSave()
        {
            return true;
        }
    }
I also have a View in my module, named MyModuleAView.xaml:
<TabItem x:Class="PrismApp.Modules.MyModule.Views.MyModuleAView"
             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" 
             mc:Ignorable="d" Header="My First Module"
             xmlns:vm="clr-namespace:PrismApp.Modules.MyModuleA.ViewModels"
             d:DesignHeight="300" d:DesignWidth="300" IsSelected="True">
    <Grid>
        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Path=Message, Mode=TwoWay}"></TextBlock>
        <Button VerticalAlignment="Center" HorizontalAlignment="Center" Content="Save" Command="{x:Static vm:MyModuleAViewModel.SaveCommand}"></Button>
    </Grid>
</TabItem>
and code behind:
    public partial class MyModuleAView : TabItem
    {
        public MyModuleAView(MyModuleAViewModel viewModel)
        {
            InitializeComponent();
            this.Loaded += (obj, e) => this.DataContext = viewModel;
        }
    }
And finally, this is my RibbonTab:
<ribbon:RibbonTab x:Class="PrismApp.Modules.MyModuleA.Views.MyModuleARibbonTab"
             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:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
             xmlns:vm="clr-namespace:PrismApp.Modules.MyModuleA.ViewModels"
             xmlns:prism="http://www.codeplex.com/prism"
             Header="My Module"
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <ribbon:RibbonGroup Header="My Module Items">
        <ribbon:RibbonButton Label="Save" Command="{x:Static vm:MyModuleAViewModel.SaveCommand}"></ribbon:RibbonButton>
    </ribbon:RibbonGroup>
</ribbon:RibbonTab>
Now, when I click on Button inside MyModuleAView.xaml, SaveCommand execute and every thing works fine, but when I click on RibbonButton, SaveCommand not execute. What should I do for this? What is best practice?

And, I have another question: Should I map Command to methods inside my ViewModel, or It's better to keep them in another class which implements ICommand interface?

(I'm using Ribbon for WPF October 2010 in .NET 4)
Developer
Feb 1, 2013 at 4:56 PM
Hi,

Based on my understanding of the code snippets you provided, this behavior could be caused by a timing issue, as by the time you resolve the ResolveRibbonTabView instance, no ICommand object instance has been passed to the SaveCommand property, as this will be done in your MyModuleAViewModel constructor:
 public MyModuleAViewModel()
        {
            SaveCommand = new DelegateCommand(Save, CanSave);
        }
Which in your case, I believe it will be called after this instance is resolved when registering the ResolveMainView . As a quick test you could try changing the order on how these views are registered, in order to ensure the view model instance is resolved before retrieving the ResolveRibbonTabView:
(...)
_regionManager.RegisterViewWithRegion("MainRegion", ResolveMainView);
_regionManager.RegisterViewWithRegion("RibbonRegion", ResolveRibbonTabView);
(...)
On the other hand, in my opinion a cleaner approach could be to define a globally available command in a static class, for example benefiting of the CompositeCommand class provided by Prism. Which will allow you to dynamically register child commands to this globally available command (e.g you could register your save commands methods in your view model to this static class and in your different views bind to this global command), this way once the command is registered all your views should execute it without problems.

For more detailed information on this approach, I believe you could check the following section of the documentation:
I hope you find this helpful,

Agustin Adami
http://blogs.southworks.net/aadami