How the Presentation Model could look like when using Silverlight 2.0 (Part 4)

I have created a small framework only for fun and to have a programming task ;) The framework I have created (not yet 100% completed) is similar to Prism, Caliburn and Silverlight.FX. So this is not a framework that should replace those other framework, it still only a hobby project to see if I managed to create a similar one. I will still write about it in this Part 4 of my Presentation Model series. The goal with the framework is to leave the code-behind alone and focus on a separate class and declare everything in the XAML. The following is an example of a class (a Presentation Model):

public class LoginPresentationModel : Silverlight.Extension.Model
{
       private string _userName;

       public string UserName
       {
           get { return _userName; }
           set
           {
               _userName = value;
               base.NotifyPropertyChanged("UserName");
           }
       }
       
       public void Login(string userName, string password)
       {
           //Do some validation
           this.UserName = userName;
       }
}

Note: The Model doesn't need to Inherit from the Model base class, I only do it because I have added the NotifyPropertyChanged method to the base class.

The code-behind for my Page.XAML will look like this:

public partial class Page : UserControl
{
      public Page()
      {
          InitializeComponent();
      }
}

Note: I hope Silverlight team will add the InitializeComponents to the page.g.cs file so we can remove the Code-behind.

To use the LoginPresentaitonModel in my View (XAML file) we can use an Attached property added to any FrameworkElement’s called View.Model:

<UserControl x:Class="PresentationModelExample1.Page" xmlns..
    xmlns:e="clr-namespace:Silverlight.Extension;assembly=Silverlight.Extension" xmlns:m="clr-namespace:PresentationModelExample1.Model" Width="400" Height="350"> <e:View.Model> <m:LoginPresentationModel/> </e:View.Model>

Note: Because the View.Model can be used on any FramewrokElement, we can partially add different Presentation Model to the  VIew, but I recommend to only use one per View.


The LoginPresentationModel class will be bound to the UserControl’s DataContext property, to we can now simply use normal data binding to bind FrameworkElements to the Presentation Model’s properties:

<Grid x:Name="LayoutRoot" Background="White">
       <StackPanel Margin="50">

           <TextBox x:Name="userName" Text="{Binding UserName}"/>

           <Password x:Name="password"/>

           <Button e:Attach.Click="Login" Content="Login"/>
      </StackPanel>
</Grid>


The code above will bind the UserName property of the LoginPresentationModel to the TextBox userName. The Button control ha an attached property Attach.Click which will make sure the Login method of the LoginPresentationModel is invoked when the Button  click event is trigged.

public void Login(string userName, string password)
{
}


As you can see the Login method has two arguments, userName and password. Which is not given when the Login method is attached to the Click event. By default the framework will check if there are controls with the same name as the argument, if so, it will get the value from the controls and pass them as an argument. So in this case the userName and Password control’s value will be passed to the Login method. I can also write the following when I attached the method to the event:

<Button e:Attach.Click="Login(userName.Text, password.Text)" Content="Login"/>


The argument mapping in this framework works similar to how it does in the ASP.Net MVC. For example if the Login method instead will take a LoginCredential class:


public
void Login(LoginCredential loginCredential) { }

 

public class LoginCredential
{
    public string UserName { get; set; }
    public string Password { get; set; }
}


The LoginCredential will be created and the framework will first look for controls with the name “loginCredential_UserName” and/or “loginCredential_Password” and get the value and set it to the LoginCredential properties. If the “loginCredential_” prefix can’t be found it will check for controls with the same name as  the properties of the LoginCredential class. A MappingAttribute can also be used for the arguments of a method to also specify a specific prefix. [Mapping(“login”)], and instead of looking for “loginCredential_” prefex, the framework will look for a the prefix “login_”.

We can also make sure our action methods returns a value and set the value to a specific FrameworkElement’s property:

public string Login(string userName, string password)
{
    //Do some validation
    if (faild)
        return "The user name or password is not correct..";

    //..
}

<TextBloc x:Name="errorMsg"/>

<! -- .... --->

<Button e:Attach.Click="Login => errorMsg.Text" Content="Login"/>


By using the “=>” we specify that we will take the value from the Login and move it to the errorMsg.Text property. But if we use a correct Presentation Model we would not use this “expression”, instead have a property in the Presentation Model which we bind to the TextBlock and set the property internally in within the Login method and then use the NofityPropertyChanged to update the TextBlock value:


<TextBloc x:Name="errorMsg" Text=”{Binding ErrorMsg}”/>

<! -- .... --->

<Button e:Attach.Click="Login" Content="Login"/>
public string Login(string userName, string password)
{
    //Do some validation
    if (faild)
        this.ErrorMsg = "The user name or password is not correct..";

    //..
}


The Framework also support Asynchronous calls. We can mark a method with the AsynCall attribute and make sure it will be executed in a background worker thread:

[AsynCall]
public void Login(string userName, string password)
{
    //Do some validation
}


We can also specify a callback method which will be executed in the UI thread:

[AsynCall(CallBack="Login_Completed")]
public string Login(string userName, string password)
{
    //Do some validation
    return "someting";
}

public string Login_Completed(string result)
{
    return result + "and something more";
}


The AsyncCallAttribute is an ActionFilter, so we can create our own ActionFilters, which has at the moment three methods we can use, OnPreAction, OnPostAction and UnhandledException. the ActonFilters are similar to how ActionFilters works within the ASP.NET MVC. Here is an example of a TestActionFitlerAttribute:

 

public class TestActionFilterAttribute : ActionFilterAttribute
{
      public override void OnPreAction(PreActionFilterArgs actionFilterContext)
      {
          if (actionFilterContext.ActionMethodArguments != null && actionFilterContext.ActionMethodArguments.Length > 0)
          {
              var c = actionFilterContext.ActionMethodArguments[0] as Customer;

              c.FirstName = "Fredrik";
          }

          var p = actionFilterContext.PresentationModel as CustomerPresentationModel;
          p.Customer.FirstName = "Lovisa";
      }


      public override void OnPostAction(PostActionFilterArgs actionFilterContext)
      {
          actionFilterContext.ActionMethodReturnValue += "test";
      }


      public override void OnUnhandledException(UnhandledExceptionActionFilterArgs unhandledExceptionActionFilterArgs)
      {
          var c = unhandledExceptionActionFilterArgs.PresentationModel as CustomerPresentationModel;
          c.ErrorMsg = unhandledExceptionActionFilterArgs.Exception.Message;

          unhandledExceptionActionFilterArgs.Handled = true;
      }
}


The Action Filter above will manipulate the Action methods arguments before the action method is executed. The filter will also manipulate the return value of a Action filter (if the Action method will return a value). We can also manipulate the Presentation Model’s stage within the Action Filter, the Action Filters will be executed within the UI thread.

The source code is more or less completed, need to do some more refactoring and also run some more tests, but all the basics will work. If you want a framework like this you can take a look at Prism v 2.0 from Microsoft Patterns & Practices, Caliburn or Nikhil’s Silverlight.FX, my framework is nothing I will support and only created it for fun. If you want the source code, please drop me an e-mail (fnormen [ at ] hotmail.com because I have no place to upload the source code at the moment. Remember the source code is not yet completed, I will add more support for more events and also make sure others can easy add events to attach to etc.

No Comments