Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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>
like image 767
Steven_BDawg Avatar asked Feb 15 '16 21:02

Steven_BDawg


1 Answers

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.

like image 181
Steven_BDawg Avatar answered Oct 19 '22 23:10

Steven_BDawg