Binding to ViewModel in XAML rather than in the View

Feb 19, 2013 at 11:39 AM
Edited Feb 19, 2013 at 11:39 AM
Hello,

I am quite new to the Okra app framework (I've actually just moved from using MVVM Light) and I am finding it to be a very a nice framework and light to work with. I do however have quite a few preferences from MVVM Light where I would like to use for Okra. One of them is the ability to bind to the ViewModel in the XAML of the View. With MVVM Light in this case, you would set up the ViewModels in the Locator and then bind directly to the ViewModel instances inside the XAML of the View corr

Example:
DataContext="{Binding Main, Source={StaticResource Locator}}"
I am just wonder if something like that is possible? I've look through some really nice examples on here and I am only seeing the View's datacontext being binded in the View class.

I.e.
            if (_shellPage == null)
                _shellPage = new ShellPage {DataContext = this};
Looking forward to a helpful response.

Thanks
Feb 19, 2013 at 2:29 PM
Actually, after some digging around I can understand now that binding is done automatically, correct?
Coordinator
Feb 19, 2013 at 8:21 PM
Hi,

I'm pleased you have found the Okra App Framework for your Windows Store development. I realise there is a bit of a learning curve at the moment and hope to write some more complete documentation in the future.

Regarding your query, you are correct that the DataContext of the view is automatically set by the framework whenever you navigate. All you need to do is to add the [PageExport(...)] and [ViewModelExport(...)] attributes to the view and view-model respectively. Whenever you navigate to a new page, the framework will locate the correct view and view-model based upon the specified page name, set the DataContext of the view to the view-model, and display the page. As long as you add the attributes then everything else happens for you.

By using this mechanism I haven't found a need for a view-model locator in my own apps. Does this sound like it would work for you, or do you have a particular scenario in mind where a view-model locator would be more suitable?

PS. I posted a blog entry introducing navigation here which might be of help.

Hope this helps and if you have any other questions/features you would like to see then let me know,
Andy
Feb 19, 2013 at 11:34 PM
Edited Feb 20, 2013 at 12:16 AM
Hello Andy

Thank you for your quick and awesome reply!

I am slowly getting around to understanding your framework and I am sort of getting somewhere with porting my app from MVVM Light to Okra Framework. I do have another question though with something I have been struggling with, that if you don't mind asking for your help.

My app is set up as a shell app (using the Shell sample you have posted up on here). I have wired the views up to a single instance of a DataSource by setting the Shared and Export attributes in the DataSource class. Currently it works fine for the LoginViewModel and LoginPage which are launched using a bootscrapper before the actual ShellPage and HomePage are launched. I am attempting to wire up the same instance of the DataSource to the HomeViewModel but for some reason I keep getting a "Object reference not set to an instance of an object." Exception on the DataSource object. Currently I have the HomeViewModel set up like this:
    [Export]
    [ViewModelExport(SpecialPageNames.Home)]
    public class HomeViewModel : NotifyPropertyChangedBase
    {
        [Import]
        public DataSource DataSource { get; set; }

       //Code omitted for this post
        
        public async void InitializePosts()
        {
            var posts = await DataSource.FetchPosts();
        }

        public HomeViewModel()
        {
            InitializePosts();
        }
    }
The LoginView which works fine:
    [Export]
    public class LoginViewModel 
    {
        [Import]
        public DataSource DataSource { get; set; }

        private TaskCompletionSource<object> _continueClicked;

        private DelegateCommand _authenticationCommand;
        
        public ICommand AuthenticationCommand
        {
            get
            {
                return _authenticationCommand ?? (_authenticationCommand = new DelegateCommand(async () =>
                    {
                        await ExecuteWebAuthenticationCommand();
                    }));
            }
        }

        public async Task GetResults()
        {
            _continueClicked = new TaskCompletionSource<object>();

            await _continueClicked.Task;
        }

        private async Task ExecuteWebAuthenticationCommand()
        {
            //Code omitted for this post
        }
    }
From my understanding is that it will be something to do with not correctly exporting/importing the HomeViewModel to the DataSource instance and vice versa (unsure?). Hopefully you can clear this up for me and I should be well on my way with this :)

Thanks
Coordinator
Feb 20, 2013 at 1:02 PM
Hi,

I think the problem you are having is due to how MEF handles the import of properties. In your HomeViewModel you have told MEF to inject the DataSource into a property so the order of events is,
  1. MEF creates a new instance of HomeViewModel, calling the constructor
  2. MEF injects the DataSource into the property
  3. MEF returns the resulting HomeViewModel back to the Okra navigation pipeline.
As you can see, MEF will call the constructor before the value of DataSource is injected, therefore when the InitializePosts() method is called the value has not been initialized (note that although this method is marked as 'async' it will run synchronously until it needs to await, after getting a reference to the DataSource). Thankfully it is easy to solve by importing the DataSource via the constructor rather than a property (note the addition of the [ImportingConstructor] attribute to tell MEF which constructor to use),
[Export]
[ViewModelExport(SpecialPageNames.Home)]
public class HomeViewModel : NotifyPropertyChangedBase
{
        public DataSource DataSource { get; private set; }

        ...

        [ImportingConstructor]
        public HomeViewModel(DataSource dataSource)
        {
            this.DataSource = dataSource;
            InitializePosts();
        }
    }
Hope this helps,
Andy
Feb 21, 2013 at 11:45 PM
Edited Feb 22, 2013 at 1:47 AM
Hi Andy

Wow, that was quite simple to solve. I appreciate your help so far.

I look forward to seeing future updates from your framework.

Edited

Actually, scratch that. I've just managed to walk in another issue. Rather than explaining the issue with a long wall of text I have attached a link to an edited version of your ShellApp sample showing the issue that I am facing.

To put briefly. I have attached a page navigation command to the ItemClick event in the GridView control (Page 1 with a list of Foo’s in the ShellApp sample). When a GridView item has been clicked, the home page will navigate to the next page (Page 2). However, on navigating back to the same page with GridView and clicking a different GridView item, the ItemClick event isn't firing the Delegate Command for some reason.

I believe it may be something to do with the Navigation Service handling caching because I have seen this issue before when NavigationCacheMode has been enabled on the page, however, I don’t have NavigationCacheMode enabled and I am unsure if your INavigationService is currently handling any caching by default.

Edited ShellApp sample source: http://sdrv.ms/X6ge7I

Cheers,

Dan
Feb 23, 2013 at 3:24 PM
I've figured it out. Seems the JulMar behaviours framework (http://mvvmhelpers.codeplex.com/) is very much broken. Use the Windows.UI.Interactivity (https://nuget.org/packages/Windows.UI.Interactivity) behaviours framework instead :)
Coordinator
Feb 24, 2013 at 10:31 AM
Hi Dan,

Sorry I didn't get back to you on your last query - turns out CodePlex doesn't send me notifications for edited posts! (probably best to add a new post in future)

Anyway, I have had issues with some of the behaviours frameworks in the past. I think it is because they disconnect from controls when they get the Unloaded event (such as when navigating away), but never reattach when the controls are re-loaded on navigating back. I'm not sure if this is an Okra specific problem, or appears with the standard Frame based navigation as well - something I will investigate at some point in the future.

It is good to know that you have found that the Windows.UI.Interactivity package works better though.

Andy