DelegateCommand not Firing inside a Region

Topics: Prism v4 - Silverlight 4
Jun 6, 2012 at 2:41 PM

Hello, I am new to Prism and the MVVM architecture. I believe I have written some code where my shell has a ContentControl on it that is made up of a Region. That region has a button on it. When I click the button, nothing is firing. I have attached my code below. Please let me know what I am doing wrong.

Shell.xaml

    <Grid>
        <ContentControl x:Name="TestButtonRegion" 
                                Regions:RegionManager.RegionName="TestButtonRegion" />
</Grid>

Bootstrapper.cs

    public class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            Shell rootShell = new Shell();
            Application.Current.RootVisual = rootShell;
            return rootShell;
        }


        protected override void ConfigureModuleCatalog()
        {
            base.ConfigureModuleCatalog();

            ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
            moduleCatalog.AddModule(typeof(Geomentum.MediaReach.Modules.TestButton.TestButtonModule));
        }
    }
TestButtonView.xaml
<UserControl x:Class="Geomentum.MediaReach.Modules.TestButton.View.TestButtonView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="312" d:DesignWidth="408">
    
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Height="30" Width="50" Content="Here" Command="{Binding Path=SubmitCommand}" Margin="12,12,338,258" />
    </Grid>
</UserControl>
TestButton.View.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Geomentum.MediaReach.Modules.TestButton.View
{
    public partial class TestButtonView : UserControl, ITestButtonView
    {
        public TestButtonView()
        {
            InitializeComponent();
        }

        #region ILayersView Members
        public ViewModel.ITestButtonViewModel Model
        {
            get
            {
                return this.DataContext as ViewModel.ITestButtonViewModel;
            }
            set
            {
                this.DataContext = value;
            }
        }
        #endregion


    }
}
ITestButtonView.cs (class library)
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Geomentum.MediaReach.Modules.TestButton.ViewModel;

namespace Geomentum.MediaReach.Modules.TestButton.View
{
    public interface ITestButtonView
    {
        ITestButtonViewModel Model { get; set; }
    }
}
ITestButtonViewModel.cs (class library)
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Geomentum.MediaReach.Modules.TestButton.View;

namespace Geomentum.MediaReach.Modules.TestButton.ViewModel
{
    public interface ITestButtonViewModel
    {
        ITestButtonView View { get; set; }
    }
}
TestButtonViewModel.cs (class library)
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.Generic;
using Geomentum.MediaReach.Modules.TestButton.View;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Commands;
using System.Collections.ObjectModel;
using System.Diagnostics;


namespace Geomentum.MediaReach.Modules.TestButton.ViewModel
{
    public class TestButtonViewModel : ITestButtonViewModel, INotifyPropertyChanged
    {
        #region Private Members
            private readonly IEventAggregator eventAggregator;
        #endregion

        #region ITestButtonViewModel Members
            public ITestButtonView View { get; set; }
        #endregion

        #region Data Members
            public DelegateCommand<object> SubmitCommand { get; private set; }
        #endregion

        #region Contructors
            public TestButtonViewModel(ITestButtonView view, IEventAggregator eventAggregator)
            {
                View = view;
                View.Model = this;
                this.eventAggregator = eventAggregator;

                this.SubmitCommand = new DelegateCommand<object>(this.Submit);
                this.SubmitCommand.RaiseCanExecuteChanged();
            }
        #endregion

        #region Subscribed Events
            private void Submit(object parameter)
            {
                return;
            }
        #endregion

        #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        #endregion

    }
}
TestButtonModule.cs (class library)
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Unity;
using Geomentum.MediaReach.Modules.TestButton.View;
using Geomentum.MediaReach.Modules.TestButton.ViewModel;

namespace Geomentum.MediaReach.Modules.TestButton
{
    public class TestButtonModule : IModule 
    {
        #region IModule Members

        public IUnityContainer Container { get; private set; }
        public IRegionManager RegionManager { get; private set; }

        public void Initialize()
        {
            this.RegisterViewsAndServices();
            if (this.RegionManager.Regions.ContainsRegionWithName("TestButtonRegion"))
            {
                this.RegionManager.Regions["TestButtonRegion"].Add(
                    this.Container.Resolve<ITestButtonViewModel>().View);
            }
        }

        public TestButtonModule(IRegionManager regionManager, IUnityContainer container)
        {
            this.Container = container;
            this.RegionManager = regionManager;
        }        

        #endregion
        protected void RegisterViewsAndServices()
        {
            this.Container.RegisterType<ITestButtonView, TestButtonView>();
            this.Container.RegisterType<ITestButtonViewModel, TestButtonViewModel>();
        }

    }
}

Developer
Jun 6, 2012 at 6:29 PM

Hi,

After analyzing the code snippets you provided I believe your problem might be related to the fact that you set the SubmitCommand property in your view model after setting it as the DataContext of the view. As far as I know, after the DataContext of the view is set, all changes in the DataContext (in this case the TestButtonViewModel) have to be notified to the view by raising the PropertyChanged event.

For example, you will see that if you raise the PropertyChanged event at the end of the TestButtonViewModel's constructor, the command will be executed properly:

public TestButtonViewModel(ITestButtonView view, IEventAggregator eventAggregator)
{
    View = view;
    View.Model = this;
    this.eventAggregator = eventAggregator;

    this.SubmitCommand = new DelegateCommand<object>(this.Submit);
    this.OnPropertyChanged("SubmitCommand");
}

Also, another approach could be to set the DataContext of the view at the end of the TestButtonViewModel's constructor:

public TestButtonViewModel(ITestButtonView view, IEventAggregator eventAggregator)
{
    this.View = view;
    this.eventAggregator = eventAggregator;

    this.SubmitCommand = new DelegateCommand<object>(this.Submit);

    this.View.Model = this;
}

As a side note, the Prism library is shipped with a NotificationObject class which provides a basic implementation of the INotifyPropertyChanged. If you wish, your view models can inherit from the aforementioned class to so you don't need to implement the INotifyPropertyChanged interface in each of your view models (of course, this depends mostly on the preferences of each developer.) You can find more information about this in the following chapter of the Prism documentation:

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini