Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Silverlight, M-V-VM ... and IoC - part 4

DZone's Guide to

Silverlight, M-V-VM ... and IoC - part 4

· Mobile Zone
Free Resource

Launching an app doesn’t need to be daunting. Whether you’re just getting started or need a refresher on mobile app testing best practices, this guide is your resource! Brought to you in partnership with Perfecto

Let’s move on to the next step and remove all the code on the view that is directly related to the commands the user can give through buttons. To do so we use a new feature of Silverlight 4: Commanding.

Since the release of Silverlight 2 the runtime offered a class -ICommand- that we could use to implement a commanding framework. WPF had a full implementation of a commanding framework while in Silverlight it was completely left to the user (a couple of solutions can be found on the web).

With the release of Silverlight 4 every control that derive from ButtonBase now expose two new properties - Command and CommandParameter - you can use in binding with an ICommand object allowing for a declarative approach when it comes to respond to click events raised by those controls; we can now implement a pure form of MVVM without having to rely on external frameworks.

There are some good resources available out there about the basics of commanding in Silverlight, here are two of them:

SILVERLIGHT 4'S NEW COMMANDING SUPPORT

5 Simple Steps to Commanding in Silverlight

However we will follow a slightly different approach...ICommand is an interface, so it is well suited to be used with an IoC container :D. Defining our own set of commands - be it by registering them with names or by defining new interfaces that derive from ICommand and registering them - we can leave to the container the responsibility to configure the ViewModels with the actual instances of those commands that will be later on used with binding.

This opens up for a lot of possibilities: you can control the lifetime of a command object through the container (transient or singleton, allowing you to share the behavior if needed) and you can ‘spam’ the same command in different sections of your interface easily (binding to the same property of the ViewModel); even better you can use AOP techniques to add behaviors to your commands (like logging or tracing abilities to have some usage stats of you application).

Let’s see a first time implementation; let’s define a base class for our commands:

public abstract class BaseCommand : ICommand
{
    bool canExecuteCache;

    protected abstract bool CanExecuteImpl(object parameter);

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        bool temp = CanExecuteImpl(parameter);

        if (canExecuteCache != temp)
        {
            canExecuteCache = temp;
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, new EventArgs());
            }
        }

        return canExecuteCache;
    }

    public event EventHandler CanExecuteChanged;

    public abstract void Execute(object parameter);
    
    #endregion
}

and (continuing from the previous blog post) the actual implementation of the Search command:

public class SearchCommand : BaseCommand
{
    public SearchCommand(ISearchViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    private ISearchViewModel _viewModel;

    protected override bool CanExecuteImpl(object parameter)
    {
        return !_viewModel.IsBusy;
    }

    public override void Execute(object parameter)
    {
        _viewModel.PerformSearch(parameter.ToString());
    }
}

In this simple example I chose to use the functions exposed by the ViewModel itself, another approach (maybe subject for later refactoring) is to move out the search capabilities from the ViewModel to a search service class and inject that in the command and in the ViewModel, but let’s keep things simple for now.

We now need to modify the interface and the implementation of the ViewModel:

public interface ISearchViewModel : IViewModel
{
    new ISearchView View { get; }

    /// <summary>
    /// Collection that will be used to expose the result of the search operation
    /// </summary>
    PagedCollectionView SearchResults { get; set; }

    /// <summary>
    /// performs an asyncronous search throught the webservice and returns the results filling in the SearchResults collection
    /// </summary>
    /// <param name="query"></param>
    void PerformSearch(string query);

    SearchCommand SearchCommand { get; set; }

    NavigateToAlbumCommand OpenAlbumCommand { get; set; }
}

I’ll omit the code for the ViewModel because we just added the two SearchCommand and OpenAlbumCommand properties for property injection (we cannot have those on the constructor to avoid circular references when resolving the objects).

We can now change the code behind file of the Wiew and wipe-out all the code that was used to wire up to the button events:

public partial class Search : Page, ISearchView
{
    protected Search()
    {
        InitializeComponent();

        Loaded += Search_Loaded;
    }

    public Search(ISearchViewModel viewModel)
        : this()
    {
        (this as IView).ViewModel = viewModel;
    }

    public ILogger Logger { get; set; }

    IViewModel IView.ViewModel { get { return DataContext as IViewModel; } set { DataContext = value; } }

    public ISearchViewModel ViewModel { get { return DataContext as ISearchViewModel; } }

    void Search_Loaded(object sender, RoutedEventArgs e)
    {
        // check if we asked for this search directly by navigating to this page
        // this should be maybe moved into the ViewModel 
        if (NavigationContext.QueryString.Count > 0)
        {
            object query = NavigationContext.QueryString["q"];
            if (query != null)
                ViewModel.PerformSearch(query.ToString());
        }
    }
}

Much shorter and cleaner than before, we now have to change the XAML (and bind to the ICommand objects) from:

...
<StackPanel Orientation="Horizontal" >
   <TextBox x:Name="TxtSearch" HorizontalAlignment="Left" Margin="0,8" Width="256" />
   <Button x:Name="BtnSearch" Margin="10,8" Width="100" Content="Search" Click="BtnSearch_Click"/>
   <Button x:Name="BtnAddNewAlbum" Margin="10,8" Width="100" Content="Add New Album" Click="BtnAddNewAlbum_Click"/>
</StackPanel>
...
<Border Tag="{Binding Id}" MouseLeftButtonDown="ItemClick" HorizontalAlignment="Center" VerticalAlignment="Center">
 	<Border.Effect>
   	<DropShadowEffect BlurRadius="100" ShadowDepth="0" Opacity="1" Color="#FFFFFF" />
  	</Border.Effect>
  	<Image Source="{Binding Image}" Width="60" Height="60" Cursor="Hand"/>
</Border>

To:

...
<StackPanel Orientation="Horizontal" >
   <TextBox x:Name="TxtSearch" HorizontalAlignment="Left" />
   <Button x:Name="BtnSearch" Content="Search" Command="{Binding SearchCommand}" CommandParameter="{Binding ElementName=TxtSearch, Path=Text}" />
   <Button x:Name="BtnAddNewAlbum" Content="Add New Album" Command="{Binding OpenAlbumCommand}"/>
</StackPanel>
...
 <Button Command="{Binding ElementName=LayoutRoot, Path=DataContext.OpenAlbumCommand}" CommandParameter="{Binding Id}">
    <Button.Template>
        <ControlTemplate>
            <Border HorizontalAlignment="Center" VerticalAlignment="Center">
                <Border.Effect>
                    <DropShadowEffect BlurRadius="100" ShadowDepth="0" Opacity="1" Color="#FFFFFF" />
                </Border.Effect>
                <Image Source="{Binding Image}" Width="60" Height="60" Cursor="Hand"/>
            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>
...

We’ve removed all the click events and we’ve had to add a button around the image of the album to support drilldown to the detail page (because Border isn’t inherited from ButtonBase and thus does not have support for commanding).

The last step is to register the commands in the IoC container:

Container.Register(
   // search Feature
   Component.For<SearchCommand>(),
   Component.For<NavigateToAlbumCommand>(), 
   Component.For<ISearchViewModel>().ImplementedBy<SearchViewModel>().LifeStyle.Custom<Structura.Castle.Windsor.Lifecycle.SingletonLifestyleManager>(), 
   Component.For<ISearchView>().ImplementedBy<Search>().Named("Search")
);

The next time we resolve an ISearchViewModel it will come up together with all the commands configured.

Following this pattern you have to be careful again about the dependencies an object have: the command now have a mandatory dependency on the ViewModel; it’s better if you do not perform any action on the ViewModel itself in the command constructor, mainly because you aren’t sure that the ViewModel is fully constructed yet (the command’s instance is being generated while resolving optional dependencies of the ViewModel, so maybe more references have to be satisfied before the object is fully configured and ready to be used). This behavior is different if you rely on an external service class instead of the ViewModel to implement the search capability.

To see it in action refer to my test project on CodePlex: http://dnmmusicstoresl.codeplex.com/ Changeset 42274

Keep up with the latest DevTest Jargon with the latest Mobile DevTest Dictionary. Brought to you in partnership with Perfecto.

Topics:

Published at DZone with permission of Alessandro Giorgetti, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}