MVVM Light - PCL+WPF - Getting exception of type Microsoft.Practices.ServiceLocation.ActivationException
Asked Answered
H

2

7

Background

Hi all SO viewers. I am normally an Android developer, but now I'm developing a cross platform application targeting WPF and Android. That being said, there's practically no info on how to directly do what I want. So, I ended up finding a 3-part blog series that goes in depth on how to develop a Windows-based cross platform MVVM project. As long as I set the PCL to be compatible with Xamarin.Android, any code that doesn't throw an error SHOULD work once I get to the Android side of things. Here are the links to the blog posts: Blog 1, Blog 2, Blog 3. Again, I do Android, so I am new to doing coding for a WPF Application.

Issue

So my issue today is only dealing with the PCL-WPF side which relates to the above-linked blog post. I followed every single step laid out in the posts as best as I could. The blog uses WinRT and WinPhone as the two target platforms, so I HAD to try figuring out things on my own to make things work on the WPF. Two of the things I had to do was use IsolatedStorage and basically use the WinPhone App.Xaml to make the WPF side build.

I have finished the blog all the way to the end and build succeeds. I even can see my example Debug lines like it talks about at the end of the third blog post. However, when I go to run it, I get the following:

ActivationException was unhandled by user code

An exception of type 'Microsoft.Practices.ServiceLocation.ActivationException' occurred in GalaSoft.MvvmLight.Extras.dll but was not handled in user code

$exception {Microsoft.Practices.ServiceLocation.ActivationException: Type not found in cache: StackOverF.Services.IStorageService. at GalaSoft.MvvmLight.Ioc.SimpleIoc.DoGetService(Type serviceType, String key, Boolean cache) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 537 at GalaSoft.MvvmLight.Ioc.SimpleIoc.GetService(Type serviceType) in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 789 at GalaSoft.MvvmLight.Ioc.SimpleIoc.MakeInstanceTClass in c:\MvvmLight\Source\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (PCL)\Ioc\SimpleIoc.cs:line 729} System.Exception {Microsoft.Practices.ServiceLocation.ActivationException}

Is there anything that you guys can tell me that maybe the blog author skipped over that I need to do? Maybe if enough rocks are thrown at this "boulder," it'll crack open...

Clarification

There are basically currently only two projects in my Visual Studio Solution. One is the Portable Class Library. The other is the WPF Application. In the very near future, once I get things working on the WPF side of the equation, I'll use the PCL in Xamarin to reuse the code in an Android project. However, the Android side is not part of my problem here. I'm having the above issue when only dealing with the WPF project.

Code (Last edited Feb 18, 2016)

IMainViewModel.cs

using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackOverF.ViewModels {
    public interface IMainViewModel {
        ObservableCollection<Workload> Workload { get; }

        RelayCommand RefreshCommand { get; }
        RelayCommand AddCommand { get; }
    }
}

MainViewModel.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using StackOverF.Models;
using StackOverF.Services;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace StackOverF.ViewModels {
    public class MainViewModel : ViewModelBase,IMainViewModel {

        private IDataService dataService;

        public MainViewModel(IDataService dataService) {
            this.dataService = dataService;

            RefreshAsync();
        }

        private ObservableCollection<Workload> workload = new ObservableCollection<Workload>();
        public ObservableCollection<Workload> Workload {
            get {
                return workload;
            }
        }

        #region Commands

        #region Refresh
        private RelayCommand refreshCommand;
        public RelayCommand RefreshCommand {
            get {
                return refreshCommand ?? (refreshCommand = new RelayCommand(async () => { await RefreshAsync();}));
            }
        }

        private async Task RefreshAsync() {
            workload.Clear();
            foreach (Workload listing in await dataService.GetWorkloadAsync()) {
                workload.Add(listing);
            }
        }
        #endregion

        #region Add
        private RelayCommand addCommand;
        public RelayCommand AddCommand {
            get {
                return addCommand ?? 
                    (addCommand = new RelayCommand(async () => { 
                        Workload listing = new Workload() { Id = 3, Serial = "relay12" };
                        await dataService.AddWorkloadAsync(listing);
                        workload.Add(listing);
                    }));
            }
        }
        #endregion

        #endregion
    }
}

LocatorService.cs (DeviceLocatorService, located in WPF Project)

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackOverF.Services {
    public class DeviceLocatorService {
        static DeviceLocatorService() {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            if (ViewModelBase.IsInDesignModeStatic) {
            }
            else {
            }

            if (!SimpleIoc.Default.IsRegistered<IStorageService>()) 
                SimpleIoc.Default.Register<IStorageService, StorageService>();
        }

        public static void Cleanup() {
        }
    }
}

LocatorService.cs (LocatorService, located in PCL Project)

using Microsoft.Practices.ServiceLocation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using StackOverF.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackOverF.Services {
    public class LocatorService {
        static LocatorService() {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);


            // Services
            if (ViewModelBase.IsInDesignModeStatic) {
                SimpleIoc.Default.Register<IDataService, Design.DataService>();
            }
            else {
                SimpleIoc.Default.Register<IDataService, Services.DataService>();
            }

            // View Models
            SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
        }

        public IMainViewModel MainViewModel {
            get {
                return ServiceLocator.Current.GetInstance<IMainViewModel>();
            }
        }

        public static void Cleanup() {
        }
    }
}

It errors (WHILE DEBUGGING ONLY) on the return ServiceLocator.Current.GetInstance<IMainViewModel>(); line.

App.xaml

<Application x:Class="StackOverF.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"   
             xmlns:services="clr-namespace:StackOverF.Services;assembly=StackOverF.PCL"
             xmlns:deviceServices="clr-namespace:StackOverF.Services"
             StartupUri="Views/MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <deviceServices:DeviceLocatorService x:Key="Locator.WPF" d:IsDataSource="True" />
            <services:LocatorService x:Key="Locator" d:IsDataSource="True" />
        </ResourceDictionary>
    </Application.Resources>
</Application>
Horseback answered 15/2, 2016 at 21:33 Comment(7)
WPF for Windows? I don't really understand what platform are you targeting.Inapposite
It's cross platform. The TWO platforms I'm developing for is WPF for Windows and Android for... Android lol. You have "universal" code in the PCL which can be shared across the two platforms, while having platform specific code in their own projects.Horseback
@Horseback Can you show your DeviceLocatorService? As I consider IStorageService is not registered in IoC container.Novercal
@Novercal Added! Again, this is per the guide I was following via blog 1, blog 2, blog 3.Horseback
@Horseback Can you please put a breakpoints one at DeviceLocatorService ctor another at LocatorService ctor? I think it call LocatorService's ctor before DeviceLocatorService's ctor. On my machine code works fine.Novercal
@Horseback Please, can you provide your app.xaml code?Novercal
@Novercal So I just realized the code works fine if I start without debugging.... or if I Start with debugging and just keep stepping over. It's still an error though, no matter if it breaks the program or not. Going to edit and add app.xaml now.Horseback
H
1

The solution to my problem is simpler than one might expect. WPF applications have no problem using MVVM toolkits/frameworks, but they do seem to have a problem with sharing. Since WPF wasn't intended to be a cross-platform-friendly language, the "correct" way to program something like this won't work for it.

The issue comes around trying to include both LocatorService classes in the App.xaml and expecting WPF to run both classes like WinRT or WinPhone might. WPF seems to only refer to a class if the data is needed. Just like in the blog's example, I had in the Main.xaml data bound to the LocatorService class. Since the WPF application only ran that class's code, it'd throw the error.

The solution was to combine the LocatorService files into one file, on the WPF project side. Why on the WPF side you ask? A Portable Class Library should only contain universal code, shareable cross platform.

Horseback answered 15/3, 2016 at 16:21 Comment(0)
N
0

As I consider, IoC container is trying to resolve MainWindow and in order to make it container resolves IDataService. But IDataService has dependency IStorageService which is not registered in IoC container.

In my point of view, IStorageService is not registered because DeviceLocatorService constructor is never called.

I reckon you have a problem with app.xaml

<Application
    x:Class="SampleApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:ignore="http://www.ignore.com"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
    mc:Ignorable="d ignore"    
    xmlns:services="using:SampleApp.Services"   
    xmlns:local="using:SampleApp">

    <Application.Resources>
        <ResourceDictionary>

            <services:LocatorService x:Key="Locator" d:IsDataSource="True" />
            <services:DeviceLocatorService x:Key="Locator.W8" d:IsDataSource="True" />
        </ResourceDictionary>
    </Application.Resources>
</Application>
Novercal answered 18/2, 2016 at 21:37 Comment(10)
As you see now that I've added it, DeviceLocatorService is in the App.xaml.Horseback
@Horseback Can you try to use one namespace in App.xaml ?Novercal
I am using one namespace.Horseback
@Horseback You are using 2 xmlns:services="clr-namespace:StackOverF.Services;assembly=StackOverF.PCL" xmlns:deviceServices="clr-namespace:StackOverF.Services"Novercal
@Horseback Does it call DeviceLocatorService constructor?Novercal
Okay I see what you're talking about. You were talking about the xml namespace while I was talking about the Solution's. The problem about doing the one xml namespace is they are in two different locations. the Locator service is in the Portable Class Library while the DeviceLocator is in the WPF (see blog 2). Unlike in the blog, Visual Studio won't let me get away with the one namespace, which I guess is because it's WPF. I don't know for sure though. If I do it like what you copy/pasted from the blog, i get a not found error.Horseback
@Horseback In a author's project, files are in different project too, but in xaml author use one namespace, without assemblyNovercal
Let us continue this discussion in chat.Novercal
For the WinRT project, yes, but not for the Windows Phone.Horseback
This is not the actual answer but since the bounty ended, I should award it to Valentin. In chat we found the actual line that was causing the error, but not the solution yet.Horseback

© 2022 - 2024 — McMap. All rights reserved.