Shell Sample have different libraries of View & ViewModel

Dec 21, 2012 at 6:24 PM

Hi Andy,

Great Work.... Your Framework is nice. I am trying my first win 8 App with it....

But i stumbled upon something...... In implementing the Shell sample in the ShellViewModel

you created the instance of SHellpage.

My App will be having different libraries for view and viewModel.

How to implement the same in such a senario....

Thanks for such a nide framework...looking formawad for some more stuff in it... :)

Coordinator
Dec 22, 2012 at 5:09 PM

Hi,

For the standard pages of an Okra application keeping the views and view-models in separate assemblies should be fairly straightforward. Since MEF is used to compose everything be default all that is required is to point MEF both assemblies and it will handle the rest. You configure MEF by overriding the OkraBootstrapper GetContainerConfiguration() method in your AppBootstrapper. Note that you only need to provide one view and one view-model as these are simply used to find the correct assemblies.

protected override ContainerConfiguration GetContainerConfiguration()
{
    return GetOkraContainerConfiguration()
        .WithAssembly(typeof(MyView).GetTypeInfo().Assembly)
        .WithAssembly(typeof(MyViewModel).GetTypeInfo().Assembly);
}

However, using a shell is slightly more complex as the sample code I posted does not use MEF for composing the view/view-model combination. You should be able to modify this to use MEF however. The first step would be to use the existing ShellViewModel code and inject the ShellPage via a MEF importing constructor rather than creating this in the NavigateTo method. In other words something like,

[Export(typeof(INavigationTarget))]
[Shared]
public class ShellViewModel : NotifyPropertyChangedBase, INavigationTarget
{
    // *** Constructor ***

    [ImportingConstructor]
    public ShellViewModel(IShellPage shellPage)
    {
        this.shellPage = shellPage;
    }

    ...

    public void NavigateTo(object page)
    {
        if (shellPage.DataContext == null)
        {
            shellPage.DataContext = this;
        }

        this.Content = page;
        Window.Current.Content = shellPage;
    }
}

Note that I have used a shared IShellPage interface so that the view-model assembly does not need any reference to the view assembly. In the ShellPage implementation you should add [Shared] and [Export(typeof(IShellPage))] so that MEF can bind the two together. Rather than a shared interface you should also be able to change the IShellPage to Page and use a named export (i.e. [Export("ShellPage")] and [Import("ShellPage")]) that I think would be neater, however I'm not at my dev PC now to test this.

Hope this helps,

    Andy

Dec 24, 2012 at 4:55 AM

Hi Andy,

Sorry for a late reply... I tried what you said but i am getting an exception. Here is what i did.

IShellPage :

 

namespace TestLib.Views
{
    public interface IShellPage
    {
        object DataContext { get; set; }
        void SetDataContext(object dataContext);
        void SetCurrentWindowContext();
    }
}

 

ShellViewModel :

 

namespace TestLib.ViewModel
{
    [Export(typeof(INavigationTarget))]
    [Shared]
    public class ShellViewModel : NotifyPropertyChangedBase , INavigationTarget
    {
        private IShellPage _shellPage;
        private object _content;

        [ImportingConstructor]
        public ShellViewModel(IShellPage shellPage)
        {
            _shellPage = shellPage;
        }

        public object Content
        {
            get { return _content; }
            set
            {
                _content = value;
                OnPropertyChanged("Content");
            }
        }

        public void NavigateTo(object page)
        {
            if (_shellPage.DataContext == null)
            {
                _shellPage.DataContext = this;
            }

            

            this.Content = page;
 
            // Set the shell view as the window content
 
            shellPage.SetCurrentWindowContext();
            
        }
    }
}

ShellPage :

 

[Export(typeof(IShellPage))]
    [Shared]
    public sealed partial class SplitPage : TestApp.Common.LayoutAwarePage , IShellPage
    {
        public SplitPage()
        {
            this.InitializeComponent();
        }

        public void SetDataContext(object dataContext)
        {
            this.DataContext = dataContext;
        }

        public void SetCurrentWindowContext()
        {
            Window.Current.Content = this;
        }
    }

CallStack :
>	TestApp.exe!TestApp.Views.SplitPage.InitializeComponent()	
 	TestApp.exe!TestApp.Views.SplitPage..ctor()	
 	[External Code]	
 	TestApp.exe!TestApp.App..ctor()	
 	TestApp.exe!TestApp.Program.AnonymousMethod(Windows.UI.Xaml.ApplicationInitializationCallbackParams p = {unknown})	

 

Sorry for all the verbose writing but i still cna find the reason why the exception is coming on

SplitPage.InitializeComponent() method.

I have attached a sample solution of what i was doing.....
https://www.dropbox.com/s/4g2qktrgc08q83r/ShellSample.zip

You can have a look at it when you get time... :)

Thanks for all the help.

Merry X-Mass and a Happy New Year
Coordinator
Dec 26, 2012 at 4:25 PM

Hi,

You seem to have got the right approach here.

Regarding the exception you are getting, if it is in the InitializeComponent() method then I suspect it is an error when parsing the XAML. Maybe try commenting out some of the XAML elements and see if the exception goes away - and double checking that the StaticResource references are all available. Does the exception error message give anything useful? I'll be away from my dev PC until into the New Year so won't be able to look into the code too much until then.

Regards,

    Andy

Coordinator
Jan 6, 2013 at 3:59 PM

Hi,

I've had a chance to look at your solution. The exception you are getting is because the app is unable to find the "StandardStyles.xaml" resource dictionary for some reason. Probably because the shell page is being created via composition in the application constructor before all the resources are initialized. The easiest solution is to explicitly reference the resources at the start of SplitPage.xaml,

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>

            <!-- 
                Styles that define common aspects of the platform look and feel
                Required by Visual Studio project and item templates
                -->
            <ResourceDictionary Source="../Common/StandardStyles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <!-- Collection of items displayed by this page -->
        <CollectionViewSource
        x:Name="itemsViewSource"
        Source="{Binding Items}"
        />
    </ResourceDictionary>
</Page.Resources>

I also changed the ShellViewModel.NavigateTo(...) method to,

public void NavigateTo(object page)
{
    _shellPage.DataContext = this;
    this.Content = page;
    _shellPage.SetCurrentWindowContext();
}

Other fixes I made were,

  1. Removed the extra DataContext definition in the root grid element of SplitPage.xaml (I.e. removed the 'DataContext="{Binding Group}"').
  2. Added some static context to BasicPage1.xaml (since it binds to properties that are not present in the test view-model.

I've posted the updated sample at,

http://okra.codeplex.com/releases/view/99961

Regards,

    Andy