Monday, 19 September 2011

MS Dynamics 2011 and MVVM

So, I've been out of the Microsoft Dynamics world for about a year now (and semi AWOL in the process!), but after changing jobs I find myself back in this world using 2011. First up, Silverlight! Now, I have started to notice quite a bit that a lot of samples or guides on CRM 2011 and Silverlight don't seem to use MVVM. Why on earth is that? First things first, this in my opinion sheds a very bad light on Dynamics development. The very first project I get to work on had already been started by a previous developer. I open up the project and to my dismay I see absolutely zero view models. Task 1 - Push TDD.

So, why would we not use MVVM in Dynamics 2011? Let's walk through a sample I found and my phase 1 of refactoring. I open up the code behind and find this:


    private DataItemCollection _collection;
    public System.String _serverUrl;
    private string _userId;
    public MainPage()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        GetPageInformation();
         LoadCompanies();
    }

    private void LoadCompanies()
    {
        _collection = new DataItemCollection();
        QueryExpression query = QueriesHelper.GetAllAccounts();
        SoapHelper.BeginExecuteRetrieveMultiple(query, new AsyncCallback(GetAccountsCompleted), null);
    }

    private void GetAccountsCompleted(IAsyncResult result)
    {
        OrganizationResponse response = ((IOrganizationService)result.AsyncState).EndExecute(result);
        if ((response != null) && (response.Results.Count > 0))
        {

...I SHALL PROTECT YOUR EYES FROM A MONSTROSITY OF A FUNCTION HERE...

        }

        if (!Deployment.Current.Dispatcher.CheckAccess())
        {
            Deployment.Current.Dispatcher.BeginInvoke(delegate()
            {
                this.TreeViewClients.DataContext = _collection;
            });
        }
        else
        {
            this.TreeViewClients.DataContext = _collection;
        } 
    }

    private void GetPageInformation()
    {
        ScriptObject xrm = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
        ScriptObject page = (ScriptObject)xrm.GetProperty("Page");
        ScriptObject pageContext = (ScriptObject)page.GetProperty("context");
        _userId = (string)pageContext.Invoke("getUserId");
        _serverUrl = (string)pageContext.Invoke("getServerUrl");
    }


So what do we have here? What hints do I get as to why we wouldn't implement MVVM? Ok, so it looks like either the developer hadn't done much Silverlight/WPF before... or didn't quite know how to MVVM this. Looking at the code I can see that there's 2 things that we need to be considerate of when considering MVVM:

1. Queries need to run Asynchronous.

2. Access to our dispatcher for when we need to run "stuff" on the UI thread.

So how do we move this across to a MVVM structure then? Step 1, let's create a ViewModel:


    public class MyDamnViewModel
    {
        private readonly Dispatcher _dispatcher;

        public MyDamnViewModel()
        {
            _dispatcher = Deployment.Current.Dispatcher;
        }
    }


One plan here will be to allow for testing, but for now just copy the dispatcher to a local variable.

So what else can we add to this? How about everything! Considering the old form set everything up in the "onload" let's go with this for now. Let's just copy ALL methods across to my viewmodel and add the 2 method calls to our ViewModel constructor (GetPageInformation and LoadCompanies)... This will cause 1 major problem...


    this.TreeViewClients.DataContext = _collection;


What do we do? I notice my DataItemCollection already inherits from ObservableCollection... let's work with that for now and mark for review... Refactor the variable _collection so that we expose as a public ObservableCollection property:


    private DataItemCollection _collection; 
    public ObservableCollection<DataItem> NowWeHaveACollection 
    { 
        get { return _collection; } 
        set 
        { 
            _collection= (DataItemCollection)value; 
            NotifyPropertyChanged("NowWeHaveACollection"); 
        } 
    } 


I won't go into NotifyPropertyChanged stuff into too much depth here... but to get us by I just inherited from INotifyPropertyChanged and added this code to the view model:


    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }


Let's worry about refactoring and tidying all that up in a phase 2. For now I just want to get a basic MVVM structure in place.

So what did this new property give us? We can just set the property instead of directly accessing the control:


    NowWeHaveACollection = _collection;


Nearly there yet boss? Not quite. Code should be there, but we don't have anything hooked up. Let's set up our datacontext in the XAML:


    <usercontrol.datacontext> 
        <MyDamnViewModel /> 
    </usercontrol.datacontext>


And set our bindings up in the XAML instead of in the code. In my case it was a telerik tree view so we have converters and all sorts of rubbish in the code... I'll save that for another post. Let's pretend for now it's a bog standard list or something. Set this on your object in the XAML:


    ItemsSource="{Binding NowWeHaveACollection}"


And now our main form looks like this:


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


Ignoring the fact that it's called "MainPage" and some "phase 2" refactoring we're pretty much done.

So, to answer the question - No reason not to use MVVM in Dynamics 2011. I'd like to see less samples using direct code behind please!!

No comments:

Post a comment