Pour continuer sur la lancée du post précédent, je me suis dit qu'il fallait que j'ajoute le téléchargement à la volée des xaps. Ainsi on peut découper son application et le client ne télécharge que ce qui lui est nécessaire!
Pour cela, il "suffit" de trafiquer le UnityContentLoader du post précédent pour qu'à la demande d'une page, si le type n'est pas encore chargé, il y'ait chargement. De plus, comme il y'a un pattern async, c'est plutôt simple à mettre en place: tout se fait dans le BeginLoad (et aussi un peu dans le CanLoad en fait).
public class UnityContentLoader : INavigationContentLoader
{
private static Type ResolveType(String typeName)
{
var containerRegistration = UnityContainer.Registrations.FirstOrDefault(cr => cr.RegisteredType.FullName == typeName);
if (containerRegistration == null)
return null;
return containerRegistration.RegisteredType;
}
private static IUnityContainer UnityContainer
{
get
{
return ServiceLocator.Current.GetInstance<IUnityContainer>();
}
}
private static IModuleCatalog ModuleCatalog
{
get
{
return ServiceLocator.Current.GetInstance<IModuleCatalog>();
}
}
private static IModuleManager ModuleManager
{
get
{
return ServiceLocator.Current.GetInstance<IModuleManager>();
}
}
#region INavigationContentLoader Members
public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, AsyncCallback userCallback, object asyncState)
{
var splittedName = targetUri.OriginalString.Split(',').Select(s => s.Trim()).ToArray();
var asyncResult = new UnityContentLoaderAsyncResult(splittedName[0], asyncState);
Action<AsyncCallback, UnityContentLoaderAsyncResult> action = (callback, result) =>
{
if (SynchronizationContext.Current != null)
SynchronizationContext.Current.Post(obj => BeginLoadOnUiThread(callback, result), null);
else
Deployment.Current.Dispatcher.BeginInvoke(() => BeginLoadOnUiThread(callback, result));
};
//Module information given, checking whether we need to load the module or not
if (splittedName.Count() > 1)
{
var module = ModuleCatalog.Modules.First(m => m.ModuleName == splittedName[1]);
if (module == null)
asyncResult.Exception = new ModuleNotFoundException();
else
{
if (module.State == ModuleState.NotStarted)
{
ModuleManager.LoadModuleCompleted += (sender, args) =>
{
if (args.ModuleInfo.ModuleName == module.ModuleName)
{
action(userCallback, asyncResult);
}
};
ModuleManager.LoadModule(module.ModuleName);
return asyncResult;
}
}
action(userCallback, asyncResult);
return asyncResult;
}
else
{
//No module information given, assuming assemblies are already loaded
action(userCallback, asyncResult);
return asyncResult;
}
}
public bool CanLoad(Uri targetUri, Uri currentUri)
{
var splittedName = targetUri.OriginalString.Split(',').Select(s => s.Trim()).ToArray();
if (splittedName.Count() > 1)
{
var module = ModuleCatalog.Modules.First(m => m.ModuleName == splittedName[1]);
if (module == null)
return false;
if (module.State == ModuleState.NotStarted)
return true;
}
return ResolveType(splittedName[0]) != null;
}
public void CancelLoad(IAsyncResult asyncResult)
{
}
public LoadResult EndLoad(IAsyncResult asyncResult)
{
var result = asyncResult as UnityContentLoaderAsyncResult;
if (result.Exception != null)
throw result.Exception;
return new LoadResult(result.Content);
}
#endregion
private static void BeginLoadOnUiThread(AsyncCallback userCallback, UnityContentLoaderAsyncResult asyncResult)
{
if (asyncResult.Exception != null)
{
asyncResult.IsCompleted = true;
if (userCallback != null)
userCallback(asyncResult);
return;
}
try
{
var type = ResolveType(asyncResult.TypeName);
if (type == null)
throw new ArgumentException("Unable to find type requested");
asyncResult.Content = UnityContainer.Resolve(type);
}
catch (Exception e)
{
asyncResult.Exception = e;
}
finally
{
asyncResult.IsCompleted = true;
if (userCallback != null)
userCallback(asyncResult);
}
}
private class UnityContentLoaderAsyncResult : IAsyncResult
{
private readonly String _typeName;
private readonly object _asyncState;
internal UnityContentLoaderAsyncResult(String typeName, object asyncState)
{
_typeName = typeName;
_asyncState = asyncState;
}
#region IAsyncResult Members
public object AsyncState { get { return _asyncState; } }
public WaitHandle AsyncWaitHandle { get { return null; } }
public bool CompletedSynchronously { get { return false; } }
public bool IsCompleted { get; internal set; }
#endregion
internal String TypeName { get { return _typeName; } }
internal Exception Exception { get; set; }
internal object Content { get; set; }
}
}
Tout se joue dans l'UriMapper : lorsqu'une uri contient uniquement un type, on présume que le module est chargé en même temps que l'appli. Lorsque dans l'uri il y'a le type ainsi que le nom du module alors on présume que le module n'est pas forcément chargé et on s'en assure auparavant. On reprend la marche normale une fois chargé.
Voici un exemple d'UriMapper qui va bien:
<sdk:UriMapper x:Key="UriMapper">
<sdk:UriMapping Uri="" MappedUri="ViewFirstDemo.Modules.ShellModule.Pages.Home.IHomeView" />
<sdk:UriMapping Uri="/Home" MappedUri="ViewFirstDemo.Modules.ShellModule.Pages.Home.IHomeView" />
<sdk:UriMapping Uri="/Dummy" MappedUri="ViewFirstDemo.Modules.DummyModule.Pages.Dummy, DummyModule" />
<sdk:UriMapping Uri="/About" MappedUri="ViewFirstDemo.Modules.ShellModule.Pages.About.AboutView" />
</sdk:UriMapper>
(On remarque bien sur /Dummy l'ajout à la fin du nom du module séparé par une virgule)
Avec le ModuleCatalog correspondant:
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<Modularity:ModuleInfo Ref="Shell.xap"
InitializationMode="WhenAvailable"
ModuleName="ShellModule"
ModuleType="ViewFirstDemo.Modules.ShellModule.ShellModule, ViewFirstDemo.Modules.ShellModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Modularity:ModuleInfo Ref="Dummy.xap"
InitializationMode="OnDemand"
ModuleName="DummyModule"
ModuleType="ViewFirstDemo.Modules.DummyModule.DummyModule, ViewFirstDemo.Modules.DummyModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</Modularity:ModuleCatalog>