Click here to Skip to main content
Click here to Skip to main content

Introducing Apex

, 2 Aug 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Introducing Apex - a lightweight MVVM and utility library for WPF, Silverlight, and WP7.

Apex.png

Introduction

If you are going to work on any kind of WPF, Silverlight, or WP7 application, then you are almost certainly going to be using MVVM. MVVM (Model-View-ViewModel) is a design pattern that encourages you to separate presentation from data. Presentation is done in the View, data access and manipulation is done in the Model, and the two are bound together by a ViewModel.

This article assumes you understand the basic principals of MVVM - if you don't, then I suggest the superb article Model-View-ViewModel (MVVM) Explained, by Jeremy Likness.

If you are going to be using MVVM, there are some great libraries out there - however, if you like to do things from scratch, either as a learning exercise or so that you can build your own custom code base, then you'll want to know how to build an MVVM framework. In this article, I will show you how to build a basic MVVM framework from the ground up. I will also demonstrate how you can create a single common code-base and from this create libraries that support WPF, Silverlight, and WP7.

This library is called Apex. I have a stack of articles ready to be published that use Apex - so until I introduce it, I cannot publish the more interesting stuff!

Existing Frameworks

If you are looking for a fully-functional framework out of the box, then let me first recommend Cinch. Cinch is written by Sacha Barber and is in my opinion the best MVVM framework out there. The main page for v2 is here. Another good alternative is Prism.

Rolling Your Own

Ready to roll your own from scratch? Let's go. What do we need for a basic MVVM framework?

  • A ViewModel Base Class: Something that implements INotifyPropertyChanged and allows us to quickly build ViewModels.
  • A Command Base Class: Something that implements ICommand and allows us to quickly build commands that allow the View to invoke functionality on the View Model.
  • Commonality: A framework that allows us to use consistent patterns in WPF, Silverlight, and WP7.

Let's get started - first, we'll take a look at a class that is used as a ViewModel and implements INotifyPropertyChanged.

The ViewModel

Here's a ViewModel that implements INotifyPropertyChanged.

/// <summary>
/// ViewModel1 - a class that implements
/// INotifyPropertyChanged in the most basic way.
/// </summary>
public class ViewModel1 : INotifyPropertyChanged
{
  #region INotifyPropertyChanged Members

  /// <summary>
  /// The property changed event.
  /// </summary>
  public event PropertyChangedEventHandler PropertyChanged;
  
  #endregion
}

As we can see, the only thing of interest here is that we have provided the PropertyChanged event - this is the event that we can fire to indicate that a specific property has changed. When we fire this event, the WPF framework will know to update the visuals of anything that binds to the specified property.

Next, we should put together a function that calls the event - if it has been registered.

/// <summary>
/// Raises the property changed event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
public virtual void NotifyPropertyChanged(string propertyName)
{
    //  Store the event handler - in case it changes between
    //  the line to check it and the line to fire it.
    PropertyChangedEventHandler propertyChanged = PropertyChanged;

    //  If the event has been subscribed to, fire it.
    if (propertyChanged != null)
        propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

Now we have a function we can call to fire the property changed event. With this as a starting point, we can bulk the ViewModel out to actually do something - present a first and last name.

/// <summary>
/// Gets or sets the first name.
/// </summary>
/// <value>The first name.</value>
public string FirstName
{
    get { return firstName; }
    set
    {
        if (firstName != value)
        {
            firstName = value;
            NotifyPropertyChanged("FirstName");
        }
    }
}

/// <summary>
/// Gets or sets the second name.
/// </summary>
/// <value>The second name.</value>
public string SecondName
{
    get { return secondName; }
    set
    {
        if (secondName != value)
        {
            secondName = value;
            NotifyPropertyChanged("SecondName");
        }
    }
}

So there we have it - a ViewModel to represent a first and last name. What are the problems?

  1. We have to implement INotifyPropertyChanged for every ViewModel.
  2. Every property has to do the check to see if it has changed and call the NotifyPropertyChanged function.

How will we resolve this?

  1. Create a new class called 'ViewModel' which will be the base for all future ViewModels. This is fairly standard for MVVM frameworks.
  2. Create a new class called 'NotifyingProperty' which will represent a property of a ViewModel that calls NotifyPropertyChanged. This is a little bit different so we will go into some detail here.

If you are coding as you read, now is the time to start the actual project. I will describe this in terms of the Apex solution - if you are using this code as a basis for your own project, here's where you can start to customise things.

Create a new solution named Apex. Add a WPF Class Library project called Apex. Add a folder in Apex called MVVM - this is where we'll put all the classes relating to MVVM. Now we can take the class above and use it as a starting point for our base ViewModel class. The ViewModel class is shown below. Create a new file, ViewModel.cs.

using System.ComponentModel;

namespace Apex.MVVM
{
    /// <summary>
    /// Standard viewmodel class base, simply allows
    /// property change notifications to be sent.
    /// </summary>
    public class ViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// The property changed event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public virtual void NotifyPropertyChanged(string propertyName)
        {
            //  Store the event handler - in case it changes between
            //  the line to check it and the line to fire it.
            PropertyChangedEventHandler propertyChanged = PropertyChanged;

            //  If the event has been subscribed to, fire it.
            if (propertyChanged != null)
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Bingo. We have a ViewModel base class that we can use. Now what about the NotifyingProperties? As an inspiration for this, let's take a look at how a dependency property is defined.

public class SomeDependencyObject : DependencyObject
{
    private static readonly DependencyProperty ageProperty =
        DependencyProperty.Register("Age", 
        typeof(int), typeof(SomeDependencyObject));
    
    public int Age
    {
        get { return (int)GetValue(ageProperty);}
        set { SetValue(ageProperty, value);}
    }
}

Although a bit wordy, what is nice about this is that it is familiar. In any kind of custom control work, we'll be using dependency properties, so the more we use WPF and Silverlight, the more familiar we'll become with the above. Can we do something similar for properties of ViewModels?

Well, yes, we can - we can start off with a NotifyingProperty class. What will it need? The name of the property, as a string, the property value itself (as an object), and the type of the property. With this to go on, we can create the NotifyingProperty class.

using System;
namespace Apex.MVVM
{
    /// <summary>
    /// The NotifyingProperty class - represents a property of a viewmodel that
    /// can be wired into the notification system.
    /// </summary>
    public class NotifyingProperty
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="NotifyingProperty"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="type">The type.</param>
        /// <param name="value">The value.</param>
        public NotifyingProperty(string name, Type type, object value)
        {
            Name = name;
            Type = type;
            Value = value;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the type.
        /// </summary>
        /// <value>The type.</value>
        public Type Type { get; set; }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public object Value { get; set; }
    }
}

This seems like a good starting point - now how about the GetValue and SetValue functions of DependencyObject? We can use exactly the same syntax in the ViewModel class if we add two functions to our ViewModel base class. Add the following two functions to the ViewModel class:

/// <summary>
/// Gets the value of a notifying property.
/// </summary>
/// <param name="notifyingProperty">The notifying property.</param>
/// <returns>The value of the notifying property.</returns>
protected object GetValue(NotifyingProperty notifyingProperty)
{
    return notifyingProperty.Value;
}

/// <summary>
/// Sets the value of the notifying property.
/// </summary>
/// <param name="notifyingProperty">The notifying property.</param>
/// <param name="value">The value to set.</param>
/// <param name="forceUpdate">If set to <c>true</c> we'll force an update
/// of the binding by calling NotifyPropertyChanged.</param>
protected void SetValue(NotifyingProperty notifyingProperty, 
               object value, bool forceUpdate = false)
{
    //  We'll only set the value and notify that it has changed if the
    //  value is different - or if we are forcing an update.
    if (notifyingProperty.Value != value || forceUpdate)
    {
        //  Set the value.
        notifyingProperty.Value = value;
        //  Notify that the property has changed.
        NotifyPropertyChanged(notifyingProperty.Name);
    }
}

GetValue is simple - it just returns the value of the property. SetValue is easy too - it checks to see if the value has changed and then if so, it sets it and fires the NotifyPropertyChanged function that we added earlier. We have also added a 'forceUpdate' parameter that allows us to force an update even if the value hasn't changed. We may need this in certain odd scenarios but in general, we won't, so we default the value to false.

We've got it! The ViewModel is done and the NotifyingProperty is done! Let's see it in action.

The MVVMSample

Create a samples solution folder in the Apex solution, and add a new WPF Application to it, called MVVMSample. We'll use this sample as a demo of the ViewModel.

Add a reference to the Apex library and create a new class in MVVMSample called MainViewModel, just as below.

using Apex.MVVM;
namespace MVVMSample
{
    /// <summary>
    /// An example view model that uses our new ViewModel base.
    /// </summary>
    public class MainViewModel : ViewModel
    {
        /// <summary>
        /// The first name property.
        /// </summary>
        private NotifyingProperty firstNameProperty = 
            new NotifyingProperty("FirstName", typeof(string), string.Empty);

        /// <summary>
        /// The second name property.
        /// </summary>
        private NotifyingProperty secondNameProperty =
            new NotifyingProperty("SecondName", typeof(string), string.Empty);

        /// <summary>
        /// Gets or sets the first name.
        /// </summary>
        /// <value>The first name.</value>
        public string FirstName
        {
            get { return (string)GetValue(firstNameProperty); }
            set { SetValue(firstNameProperty, value); }
        }

        /// <summary>
        /// Gets or sets the name of the second.
        /// </summary>
        /// <value>The name of the second.</value>
        public string SecondName
        {
            get { return (string)GetValue(secondNameProperty); }
            set { SetValue(secondNameProperty, value); }
        }
    }
}

Things are looking good so far - we have a ViewModel that implements the base class and notifying properties that are syntactically similar to DependencyProperties. Let's wire it up to the MainWindow. Here's the main window code, with the things that have been added in bold.

<Window x:Class="MVVMSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMSample"
        Title="MainWindow" Height="200" Width="300">
    
    <!-- The datacontext for the window is an instance of our viewmodel. -->
    <Window.DataContext>
        <local:MainViewModel x:Name="viewModel" />
    </Window.DataContext>
    
    <Grid>
        <StackPanel>
            <TextBlock Text="First Name" />
            <TextBox Margin="4" Text="{Binding FirstName}" />
            <TextBlock Text="Second Name" />
            <TextBox Margin="4" Text="{Binding SecondName}" />
        </StackPanel>
    </Grid>
</Window>

The XAML is tight and tidy - things are definitely going in the right direction. The next step will be to handle commands.

The ViewModel Command

Implementing a Command function has been done in many articles on this site. The implementation I want should be able to do this:

  1. Allow a Command object to be added to a ViewModel so the View can bind to it.
  2. Allow a Command object to somehow be associated with a function of the ViewModel, so that we can allow a View to invoke functionality of a ViewModel.
  3. Allow a Command object to send events to some arbitrary object, so that we can let a View respond to commands. This is an important one. Let's say we have a command that saves the current contact of an address book to the database. We want to make sure that when the command is executed the View moves the focus to the 'first name' textbox, i.e., the focus logic (or any logic relating to the UI) happens in the View and the business logic happens in the ViewModel.

Let's start off. Add a new class to the Apex MVVM folder called 'ViewModelCommand', implementing ICommand.

public class ViewModelCommand : ICommand
{
    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        throw new NotImplementedException();
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Here we see the most basic things we need. Let's bulk this out. Remove the code in the ICommand members region, we'll replace it now. Here's what we're adding, blow by blow.

First, we're going to need to keep track of whether the command can execute.

/// <summary>
/// Boolean member indicating whether the command can execute.
/// </summary>
private bool canExecute = false;

Look at the initial implementation above - we need a CanExecuteChanged event and the CanExecute function - we can add these now.

/// <summary> 
/// Occurs when can execute is changed.
/// </summary>
public event EventHandler CanExecuteChanged; 

/// <summary>
/// Defines the method that determines whether
/// the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command.
/// If the command does not require data to be passed,
/// this object can be set to null.</param>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
bool ICommand.CanExecute(object parameter)
{
  return canExecute;
}

Let's add a property that will allow us to set whether the command can execute and automatically fire the event.

/// <summary>
/// Gets or sets a value indicating whether this instance can execute.
/// </summary>
/// <value>
/// <c>true</c> if this instance can execute; otherwise, <c>false</c>.
/// </value>
public bool CanExecute
{
  get { return canExecute; }
  set
  {
    if (canExecute != value)
    {
      canExecute = value;
      EventHandler canExecuteChanged = CanExecuteChanged;
      if (canExecuteChanged != null)
        canExecuteChanged(this, EventArgs.Empty);
    }
  }
}

When the command gets called, Execute will be invoked. We'll add a function called 'DoExecute' which will actually execute the command. This means that the code can invoke the command manually. DoExecute will first fire an 'Executing' event, which will allow the command to be cancelled. This will be very useful - for example, let's say that we have a save to database command but we want to pop a warning box up first. The View can subscribe to the 'Executing' event, show the message box, and set the cancelled flag if the user cancels the operation. This keeps the UI logic in the View. We'll also add an 'Executed' event which will be called when the command is done. Each of these events will have a delegate written by hand and an 'EventArgs' class written by hand - we'll need this to make it Silverlight and WP7 compatible.

Finally, the Command will allow an action to be specified - either a plain old function or a function that takes an object as a parameter - this is what will be called when the Command is executed. I'm going to drop in the completed class - it'll become more clear in the example afterwards.

The full ViewModelCommand.cs file:

using System.Windows.Input;
using System;
namespace Apex.MVVM
{
    /// <summary>
    /// The ViewModelCommand class - an ICommand that can fire a function.
    /// </summary>
    public class ViewModelCommand : ICommand
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelCommand"/> class.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="canExecute">if set to <c>true</c> [can execute].</param>
        public ViewModelCommand(Action action, bool canExecute)
        {
            //  Set the action.
            this.action = action;
            this.canExecute = canExecute;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelCommand"/> class.
        /// </summary>
        /// <param name="parameterizedAction">The parameterized action.</param>
        /// <param name="canExecute">if set to <c>true</c> [can execute].</param>
        public ViewModelCommand(Action<object> parameterizedAction, bool canExecute)
        {
            //  Set the action.
            this.parameterizedAction = parameterizedAction;
            this.canExecute = canExecute;
        }
        /// <summary>
        /// Executes the command.
        /// </summary>
        /// <param name="param">The param.</param>
        public virtual void DoExecute(object param)
        {
            //  Get copies of the two event handlers we'll be using.
            //  We get them here to protect against the event timing anti-pattern.
            CancelCommandEventHandler executing = Executing;
            CommandEventHandler executed = Executed;

            //  Do we have an 'executing' event?
            if (executing != null)
            {
                //  Call the event.
                CancelCommandEventArgs args = 
                    new CancelCommandEventArgs() { Parameter = param };
                executing(this, args);
                //  If the event has been cancelled, bail now.
                if (args.Cancel)
                    return;
            }
            //  Call the action or the parameterized action, whichever has been set.
            if (action != null)
                action();
            else if (parameterizedAction != null)
                parameterizedAction(param);
            //  Call the executed event.
            if (executed != null)
                executed(this, new CommandEventArgs() { Parameter = param });
        }

        /// <summary>
        /// The action (or parameterized action) that
        /// will be called when the command is invoked.
        /// </summary>
        protected Action action = null;
        protected Action<object> parameterizedAction = null;
        /// <summary>
        /// Bool indicating whether the command can execute.
        /// </summary>
        private bool canExecute = false;
        /// <summary>
        /// Gets or sets a value indicating whether this instance can execute.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance can execute; otherwise, <c>false</c>.
        /// </value>
        public bool CanExecute
        {
            get { return canExecute; }
            set
            {
                if (canExecute != value)
                {
                    canExecute = value;
                    EventHandler canExecuteChanged = CanExecuteChanged;
                    if (canExecuteChanged != null)
                        canExecuteChanged(this, EventArgs.Empty);
                }
            }
        }
        #region ICommand Members
        /// <summary>
        /// Defines the method that determines whether
        /// the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.
        /// If the command does not require data to be passed,
        /// this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        bool ICommand.CanExecute(object parameter)
        {
            return canExecute;
        }
        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.
        /// If the command does not require data to be passed,
        /// this object can be set to null.</param>
        void ICommand.Execute(object parameter)
        {
            this.DoExecute(parameter);
        }
        #endregion

        /// <summary>
        /// Occurs when can execute is changed.
        /// </summary>
        public event EventHandler CanExecuteChanged;
        /// <summary>
        /// Occurs when the command is about to execute.
        /// </summary>
        public event CancelCommandEventHandler Executing;
        /// <summary>
        /// Occurs when the command executed.
        /// </summary>
        public event CommandEventHandler Executed;
    }
    /// <summary>
    /// The CommandEventHandler delegate.
    /// </summary>
    public delegate void CommandEventHandler(object sender, CommandEventArgs args);
    /// <summary>
    /// The CancelCommandEvent delegate.
    /// </summary>
    public delegate void CancelCommandEventHandler(object sender, 
                    CancelCommandEventArgs args);
    /// <summary>
    /// CommandEventArgs - simply holds the command parameter.
    /// </summary>
    public class CommandEventArgs : EventArgs
    {
        /// <summary>
        /// Gets or sets the parameter.
        /// </summary>
        /// <value>The parameter.</value>
        public object Parameter { get; set; }
    }
    /// <summary>
    /// CancelCommandEventArgs - just like above but allows the event to 
    /// be cancelled.
    /// </summary>
    public class CancelCommandEventArgs : CommandEventArgs
    {
        /// <summary>
        /// Gets or sets a value indicating whether this
        /// <see cref="CancelCommandEventArgs"/> command should be cancelled.
        /// </summary>
        /// <value><c>true</c> if cancel;
        /// otherwise, <c>false</c>.</value>
        public bool Cancel { get; set; }
    }
}

There's rather a lot here, but nothing we haven't described. Let's add a command to the MVVMSample that builds a full name from the first and last name properties. The additions to MVVMSample's ViewModel are below:

using Apex.MVVM;
namespace MVVMSample
{
    /// <summary>
    /// An example view model that uses our new ViewModel base.
    /// </summary>
    public class MainViewModel : ViewModel
    {
        public MainViewModel()
        {
            //  Create the build name command.
            buildNameCommand = new ViewModelCommand(BuildName, true);
        }
        private void BuildName()
        {
            //  Set the full name.
            FullName = FirstName + " " + SecondName;
        }
        /// <summary>
        /// The first name property.
        /// </summary>
        private NotifyingProperty firstNameProperty = 
            new NotifyingProperty("FirstName", typeof(string), string.Empty);
        /// <summary>
        /// The second name property.
        /// </summary>
        private NotifyingProperty secondNameProperty =
            new NotifyingProperty("SecondName", typeof(string), string.Empty);
        /// <summary>
        /// The full name property.
        /// </summary>
        private NotifyingProperty fullNameProperty =
            new NotifyingProperty("FullName", typeof(string), string.Empty);
        /// <summary>
        /// Gets or sets the first name.
        /// </summary>
        /// <value>The first name.</value>
        public string FirstName
        {
            get { return (string)GetValue(firstNameProperty); }
            set { SetValue(firstNameProperty, value); }
        }
        /// <summary>
        /// Gets or sets the second name.
        /// </summary>
        /// <value>The second name.</value>
        public string SecondName
        {
            get { return (string)GetValue(secondNameProperty); }
            set { SetValue(secondNameProperty, value); }
        }
        /// <summary>
        /// Gets or sets the full name.
        /// </summary>
        /// <value>The full name.</value>
        public string FullName
        {
            get { return (string)GetValue(fullNameProperty); }
            set { SetValue(fullNameProperty, value); }
        }
        /// <summary>
        /// The build name command.
        /// </summary>
        private ViewModelCommand buildNameCommand;
        /// <summary>
        /// Gets the build name command.
        /// </summary>
        /// <value>The build name command.</value>
        public ViewModelCommand BuildNameCommand
        {
            get { return buildNameCommand; }
        }
    }
}

We've added a Command that can be bound to and we've tied it up to the 'BuildName' function. Now we can update the View to use it. Additions to MainWindow.xaml are shown below in bold.

<Window x:Class="MVVMSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MVVMSample"
        Title="MainWindow" Height="200" Width="300">
    
    <!-- The datacontext for the window is an instance of our viewmodel. -->
    <Window.DataContext>
        <local:MainViewModel x:Name="viewModel" />
    </Window.DataContext>
    
    <Grid>
        <StackPanel>
            <TextBlock Text="First Name" />
            <TextBox Margin="4" Text="{Binding FirstName}" />
            <TextBlock Text="Second Name" />
            <TextBox Margin="4" Text="{Binding SecondName}" />
            <Button Margin="4" Width="80" 
               Content="Build Name" Command="{Binding BuildNameCommand}" />
            <TextBox Margin="4" Text="{Binding FullName}" />
        </StackPanel>
    </Grid>
</Window>

Run the code. It works, we have exactly what we need. Let's show off the 'CanExecute' capabilities. Modify the first and second name properties as below.

/// <summary>
/// Gets or sets the first name.
/// </summary>
/// <value>The first name.</value>
public string FirstName
{
    get { return (string)GetValue(firstNameProperty); }
    set 
    { 
        SetValue(firstNameProperty, value);
        BuildNameCommand.CanExecute = string.IsNullOrEmpty(FirstName) == 
            false && string.IsNullOrEmpty(SecondName) == false ;
    }
}
/// <summary>
/// Gets or sets the second name.
/// </summary>
/// <value>The second name.</value>
public string SecondName
{
    get { return (string)GetValue(secondNameProperty); }
    set
    {
        SetValue(secondNameProperty, value);
        BuildNameCommand.CanExecute = string.IsNullOrEmpty(FirstName) == 
          false && string.IsNullOrEmpty(SecondName) == false ;
    }
}

Also tweak the constructor so the command is disabled by default.

buildNameCommand = new ViewModelCommand(BuildName, false);

Finally, tweak the first and second name boxes so that they bind when the text changes rather than when the box is tabbed out of.

<TextBox Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
... 
<TextBlock Text="Second Name" />
<TextBox Margin="4" Text="{Binding SecondName, UpdateSourceTrigger=PropertyChanged}" />

We have it. A solid ViewModel base and a solid command base for tying in functionality.

ScreenshotWPF.png

Commonality

What's great about this is how easy it is to tie in to Silverlight and WP7. Add a Silverlight Class Library called Apex.Silverlight. Add an MVVM folder. Right click and choose Add Files. Select the files from the MVVM folder of Apex and choose 'Add as Link'.

Now create a WP7 Class Library called Apex.WP7. Add the MVVM file links as before. To keep the article of a sensible length, I won't include the sample applications - guess what - they work the same and use the same Apex code base.

ScreenshotSilverlight.png

ScreenshotWP7.png

In many cases, not everything can be common - some controls will only work on Silverlight or WPF. However, the framework code, ViewModels, drag and drop, etc., should be common for each platform.

Next Steps

I have a huge amount of useful code that is based on Apex and adds to Apex - design time code, drag and drop code, custom controls, validation, etc. I'll add each of these items over the next few weeks - there's some really great stuff on the way.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dave Kerr
Software Developer
United Kingdom United Kingdom
Follow my blog at www.dwmkerr.com and find out about my charity at www.childrenshomesnepal.org.
Follow on   Twitter

Comments and Discussions

 
SuggestionGeneric classes PinmemberGoom28-Aug-11 10:16 
GeneralRe: Generic classes PinmemberDaveKerr31-Aug-11 2:41 
GeneralMy vote of 5 PinmemberDieter Buecherl3-Aug-11 0:11 
BugFirstName/SecondName small error in example. PinmemberAnton Levshunov2-Aug-11 1:58 
GeneralRe: FirstName/SecondName small error in example. PinmemberDaveKerr2-Aug-11 3:44 
QuestionGood article PinmvpSacha Barber1-Aug-11 2:29 
AnswerRe: Good article PinmemberDaveKerr1-Aug-11 11:58 
GeneralRe: Good article PinmvpSacha Barber1-Aug-11 22:43 
QuestionThe monstrous timing based event anti-pattern PinprotectorPete O'Hanlon30-Jul-11 10:43 
AnswerRe: The monstrous timing based event anti-pattern PinmemberDaveKerr30-Jul-11 12:07 
AnswerRe: The monstrous timing based event anti-pattern [modified] Pinmembergarymcleanhall30-Jul-11 13:36 
GeneralRe: The monstrous timing based event anti-pattern PinprotectorPete O'Hanlon31-Jul-11 5:35 
GeneralRe: The monstrous timing based event anti-pattern Pinmembergarymcleanhall31-Jul-11 20:15 
GeneralRe: The monstrous timing based event anti-pattern PinprotectorPete O'Hanlon1-Aug-11 1:37 
GeneralRe: The monstrous timing based event anti-pattern Pinmembergarymcleanhall1-Aug-11 4:09 
GeneralRe: The monstrous timing based event anti-pattern PinprotectorPete O'Hanlon1-Aug-11 7:38 
GeneralRe: The monstrous timing based event anti-pattern PinmemberDaveKerr31-Jul-11 8:19 
GeneralRe: The monstrous timing based event anti-pattern Pinmembergarymcleanhall31-Jul-11 20:18 
QuestionMy vote of 5 PinmvpMarcelo Ricardo de Oliveira30-Jul-11 8:45 
AnswerRe: My vote of 5 PinmemberDaveKerr30-Jul-11 9:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 2 Aug 2011
Article Copyright 2011 by Dave Kerr
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid