Posts tonen met het label MVVM. Alle posts tonen
Posts tonen met het label MVVM. Alle posts tonen

woensdag 29 juni 2011

Using the codebehind in MVVM

Today I saw a topic on stackoverflow where a user asked a question on how to solve a certain piece of code that needed a UI element in MVVM.  Just an hour later a bunch of answers popped up telling him that it’s no problem putting this code in the code behind.  I disagree!

The piece of code the user was talking about was this:

xamDataGridExcelExporter.xamDataGridExcelExporter xamDataGridExcelExporter1 =      
new xamDataGridExcelExporter.xamDataGridExcelExporter();  
xamDataGridExcelExporter1.Export(this.xamDataGrid1,  
    @"C:\Excel\ExportFile.xls");

An argument to putting this kind of code in the code behind was that this is UI code.  UI code?  It’s not that because the Export method of XamDataGridExcelExporter accepts a UI element that it suddenly turned into UI code. 

When I see this piece of code I can think of a number of solutions that can isolate this piece of code from the UI.  We can for example inherit from XamDataGrid and make it responsible for its own export to excel.  Should the XamDataGrid be sealed then we can make a wrapper that does exactly the same thing.

So that just moves the problem you might think.  We still need a mechanism so the ViewModel can trigger this action.  This is where the Messenger class of MVVM light can be very handy.  On the initialization of the object we can register ourselves to a message and initiate our export action.   

Here is a quick example:

    public class XamDataGridWrapper : XamDataGrid
    {
        public XamDataGridWrapper()
            : base()
        {
            Messenger.Default.Register<string>(this, "ExportExcel", ExportFile);
        }

        private void ExportFile(string file)
        {
            xamDataGridExcelExporter.xamDataGridExcelExporter xamDataGridExcelExporter1 =      
            new xamDataGridExcelExporter.xamDataGridExcelExporter();  
            xamDataGridExcelExporter1.Export(this,  
                @"C:\Excel\ExportFile.xls");

        }
    }

The reason I would not encourage anyone for putting code in the codebehind unless it’s absolutely necessary is because of the broken window effect. A developer will see the code in the codebehind and quickly think, why not add a little more to make my life easy?  Granted that doing everything the MVVM way can be a brain teaser sometimes, but fragmenting your code between the ViewModel and the codebehind will give you more trouble than it’s worth afterwards.

To finish I’ll give a few examples in what (in my opinion are) cases you should use/not use the codebehind:
Not use codebehind:

  • Lack of dependency property
  • Invoking methods on a UI element
  • Laziness ;)
  •  …
Use codebehind:
  • Interactive animations / storyboards (eg: animation based on mousespeed)
  • Accessing navigationservice
Till next time

maandag 16 mei 2011

MVVM navigation between pages in Silverlight

I see allot of question on the internet asking how we can perform navigation in MVVM for Silverlight.  There are allot of answers but most of them involve some coupling between the View and ViewModel.  The following answer I will provide will guarantee the following:
  • Completely loose coupled
  • View is responsible for calling another view
  • No need for knowing the URI of each page
So what is the basic setup.  Since a picture is worth more than a thousand words, I made on for you with my expert paint skills:


As you can see I’ve made both a base ViewModel as a Base View.  Both of them encapsulate the requests and handling of the request for navigating to another View.  The request is executed by using the MVVMlight Messenger class.  Enough gibberish here is the code of the 2 base classes:


    public class ViewModelBase : GalaSoft.MvvmLight.ViewModelBase
    {
        public static string RequestedViewName { get; private set; }

        public static void NavigateToView(string viewName)
        {
            RequestedViewName = viewName;
            Messenger.Default.Send<string>(viewName, "NavigationRequest");
        }
    }

    public partial class NavigationPage : Page
    {
        public NavigationPage()
        {

        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            Messenger.Default.Unregister<string>(this);
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Listen for navigation requests and redirect to the requested page
            Messenger.Default.Register<string>(this, "NavigationRequest", (viewName) => ViewManager.Navigate(viewName, this));
        }
    }

The base ViewModel will send a request with the name of view in it.  The View will the capture the request and ask the ViewManager to handle the logic.  The main reason why we want to do this in the page is because we have easy access to the NavigationService.

Then the ViewManager will parse the view string and navigate to the corresponding Uri.  The Uri is determined by using a class I wrote a while back called the PageUriLocator:

    public class ViewManager
    {
        public static Uri GetUriFromViewName(string viewName)
        {
            string uri;
            switch (viewName)
            {
                case "About":
                    uri = PageUriLocator.GetUri<about>();
                    break;
                case "Home":
                    uri =PageUriLocator.GetUri<home>();
                    break;
                default:
                    throw new ArgumentException("Page does not exist");
            }
            return new Uri(uri, UriKind.RelativeOrAbsolute);
        }

        public static void Navigate(string viewName, NavigationPage page)
        {
            Uri uri = GetUriFromViewName(viewName);
            if (uri != null)
                page.NavigationService.Navigate(uri);
        }
    }

Then when we want to navigate in our ViewModel we can just do this:

    public class HomeViewModel: ViewModelBase
    {
        public RelayCommand NavigateToAbout
        {
            get
            {
                return new RelayCommand(() => NavigateToView("About"));
            }
        }
    }

I’ve included a sample here.
Have fun coding ;)

maandag 9 mei 2011

How to perform Cross Field validation in silverlight

When validating properties in Silverlight in some rare occasions we want to only validate properties referential to other properties in our viewmodel / entity.  If you do not know how to implement validation using data annotations in Silverlight then please take a look at this article .  It provides a good lead on how to do this.

So imagine we have some kind of parking application that allows a user to park using a beneficial rate when he has a child younger than 5 years old.  So if we would have a simple viewmodel that would accommodate this it would look something like this:

    public class ParkingViewModel : VMBase
    {
        private string _kidsSsn;
        private bool _parkWithBenificialRate;

        [RequiredWhenParkingWithBenificialRate()]
        public string KidsSsn
        {
            get { return _kidsSsn; }
            set
            {
                _kidsSsn = value;
                RaisePropertyChanged(() => KidsSsn);
            }
        }

        public bool ParkWithBenificialRate
        {
            get { return _parkWithBenificialRate; }
            set
            {
                _parkWithBenificialRate = value;
                RaisePropertyChanged(() => ParkWithBenificialRate);
                RaisePropertyChanged(() => KidsSsn);
            }
        }

        public bool ChildIsUnderFiveYearsOld(string ssn)
        {
            // Retrieve some value from a service
            return ssn.Equals("1234567");
        }

        public RelayCommand Pay
        {
            get
            {
                return new RelayCommand(() =>
                                            {
                                                // Empty his bank account
                                            });
            }
        }
    }

We would then need to have a validation attribute that can do 2 things:
  • Check if the user is trying to park with a beneficial rate
  • Check if the social security number is of a child younger than 5 years old

    public class RequiredWhenParkingWithBenificialRateAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Get the parent type
            var vm = validationContext.ObjectInstance as ParkingViewModel;
           
            if(vm != null && vm.ParkWithBenificialRate)
            {
                if(!vm.ChildIsUnderFiveYearsOld(value.ToString()))
                {
                    return new ValidationResult("Child must be under five years old!!!",
                                                new [] {validationContext.MemberName});
                }
            }

            return ValidationResult.Success;
        }
    }
 
That’s it!