I have a silverlight 4 project with mvvmlight and ninject 3.x.
I've setup the container like this
kernel = new StandardKernel(new ViewModelModule(), new ClientSessionModule());
And the modules like this
public class ViewModelModule : NinjectModule
{
public override void Load()
{
((StandardKernel)Kernel)
.Bind(scanner => scanner.FromThisAssembly()
.Select(x => x.IsSubclassOf(typeof(ViewModelBase)))
.BindDefaultInterface());
}
}
and
public class ClientSessionModule : NinjectModule
{
public override void Load()
{
Kernel.Bind<IClientContext>().To<ClientContext>().InSingletonScope();
}
}
Now the problem is the latter one. At one point in the application this dependency is resolved explictly and updated like this:
var context = App.Ioc.Get<IClientContext>();
context.Username = "just a sample name";
Now when a ViewModel is loaded it get the IClientContext injected like
public MainViewModel(IClientContext clientContext)
The problem is that the instance does not have the name set earlier.
But if I inside the same ViewModel resolves explictly again I get the expected instance with the name set.
What am I missing since the sigleton does not get injected as I expected it to be automatically?
Just found a duplicate kernel in my solution. And that explains why I was not able so resolve as expected as I had 2 kernels running
Related
I am using a subclass of ConfigurableBootstrapper to test my Nancy modules. This is a workaround to get the ConfigurableBootstrapper to pick up the custom view location convention I use in the 'real' bootstrapper:
public class ConfigurableBootstrapperWithCustomConvention : ConfigurableBootstrapper
{
public ConfigurableBootstrapperWithCustomConvention(Action<ConfigurableBootstrapperConfigurator> configuration)
: base(configuration)
{
}
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
nancyConventions.ViewLocationConventions.Add((viewName, model, context) =>
string.Concat(context.ModuleName, "/Views/", viewName));
base.ConfigureConventions(nancyConventions);
}
}
In my tests I first set the root path to that of my nancy project using the FakeRootPathProvider. This contains the necessary folder structure ("/Home/Views/Index.cshtml") for the view used in HomeModule to be located using this custom convention. The (simplified) test code:
[Test]
public void when_the_default_page_is_loaded_it_should_show_links_to_submit_form()
{
FakeRootPathProvider.RootPath = "../../../MyApp.Web";
var bootstrapper = new ConfigurableBootstrapperWithCustomConvention(with =>
{
with.RootPathProvider(new FakeRootPathProvider());
with.ViewEngine<RazorViewEngine>();
with.Module<HomeModule>();
});
var browser = new Browser(bootstrapper);
var response = browser.Get("/");
Assert.That(response.Body.AsString(), Is.StringContaining("<a href=\"SubmitSelf\">"));
}
When I run this test I get the following exception (at the call to response.Body):
System.ArgumentNullException : Value cannot be null.
Parameter name: httpContext
at System.Web.HttpContextWrapper..ctor(HttpContext httpContext)
at System.Web.Optimization.Styles.RenderFormat(String tagFormat, String[] paths)
at RazorOutput.RazorView.Execute()
at Nancy.ViewEngines.Razor.NancyRazorViewBase`1.ExecuteView(String body, IDictionary`2 sectionContents)
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3<T0,T1,T2>(CallSite site, T0 arg0, T1 arg1, T2 arg2)
at Nancy.ViewEngines.Razor.RazorViewEngine.<>c__DisplayClass27.<RenderView>b__26(Stream stream)
at Nancy.Testing.BrowserResponse.get_Body()
at MyApp.Tests.Integration.Web.Home.HomeModuleTests.when_the_default_page_is_loaded_it_should_show_links_to_submit_form() in HomeModuleTests.cs: line 44
If I set the Fake root path to be "../../../MyApp.Web/Home/Views" instead, the test runs OK. (It appears that the custom view location convention is being ignored when testing using Nancy's "Browser" class).
Is there some issue with setting a NancyConvention in this way?
If so is there a different way to set a view location convention with ConfigurableBootstrapper?
In Prism's IModule Initialize() method, the RegisterViewWithRegion() method is called to map views and regions.
What is the difference between those two lines of code? Why use a ServiceLocator?
_regionManager.RegisterViewWithRegion("Region1", () => _serviceLocator.GetInstance<View1>());
_regionManager.RegisterViewWithRegion("Region1", typeof(View1));
Here is the whole ModuleInit.cs class for context:
namespace MyModule
{
[ModuleExport("MyModule.ModuleInit", typeof(MyModule.ModuleInit))]
public class ModuleInit : IModule
{
private readonly IRegionManager _regionManager;
public IServiceLocator _serviceLocator;
[ImportingConstructor]
public ModuleInit(IRegionManager regionManager, IServiceLocator serviceLocator)
{
_regionManager = regionManager;
_serviceLocator = serviceLocator;
}
#region IModule Members
public void Initialize()
{
_regionManager.RegisterViewWithRegion("Region1", () => _serviceLocator.GetInstance<View1>());
}
#endregion
}
}
[Edit]
The RegisterViewWithRegion Method MSDN site describes what the two different versions do:
RegisterViewWithRegion(IRegionManager, String, Func<Object>)
Associate a view with a region, using a delegate to resolve a
concreate instance of the view. When the region get's displayed, this
delelgate will be called and the result will be added to the views
collection of the region.
RegisterViewWithRegion(IRegionManager, String, Type)
Associate a view with a region, by registering a type. When the region
get's displayed this type will be resolved using the ServiceLocator
into a concrete instance. The instance will be added to the Views
collection of the region
So it seems to me that the only difference would be to use a ServiceLocator to resolve the type into an instance either immediately, or later when the region gets displayed?
[Edit2]
Found the answer elsewhere on Stackoverflow
That's because the main App is not supposed to know about modules.
When a module is loaded, it registers with the ServiceLocator and it has access to the RegionManager.
It can then, without the main app knowing anything about the newly loaded module, inject a view from the module into the main app (a new tab for example).
The ServiceLocator will ask MEF for dependencies, so you can always call _serviceLocator.GetInstance<View1>() parameterless, and MEF will go resolve whatever needs to be imported, no matter what you change in View1's constructor signature.
i have the following problem. i have ResourceDictionaries located in different assemblies. if i create UserControls and use Styles and Resourcen from this ResourceDictionaries all works fine on runtime, but at designtime i got errors in vs2010 like - Resource with name "InvertConverter" could not be located.
CoreResource.dll
OtherResource.dll
UserControl.dll (reference the both above)
OtherWpf.dll (reference all above and use the usercontrols)
Now i checked a lot of post and blogs these days related to this problem. one solution was to add the ResourceDictionaries to every UserControl - this would work but create a lot of overhead at runtime. all other solutions i find did not work for me.
i will post what i have done at leat as an answer because it works for me. but i'd like to see other/better soltutions.
here is what i did now.
i simply use a static method to add my resourcedictionaries just at designtime.
public class DesignTimeResourceLoader
{
public static void LoadResources4DesignTime(UserControl ctrl)
{
//do this just in DesignMode
if (Convert.ToBoolean(DesignerProperties.IsInDesignModeProperty.GetMetadata(ctrl).DefaultValue))
{
var uricore = new Uri("/CoreResource;component/ResourceDictionary.xaml", UriKind.Relative);
var core = (ResourceDictionary)Application.LoadComponent(uricore);
ctrl.Resources.MergedDictionaries.Add(core);
var uriother = new Uri("/OtherResource;component/OtherResourceDictionary.xaml", UriKind.Relative);
var other = (ResourceDictionary)Application.LoadComponent(uriother);
ctrl.Resources.MergedDictionaries.Add(other);
//if you have(need more just add here
}
}
}
i create and use this class in my UserControl.dll and for every Usercontrol i call the method in the constructor.
public partial class MyControl : UserControl
{
public MyControl ()
{
DesignTimeResourceLoader.LoadResources4DesignTime(this);
InitializeComponent();
}
}
this works for me atm. bu maybe there are some drawback i did not see now.
I 'm fairly new to Prism and I 'm currently re-writing one of our existing applications using Prism as a proof of concept project.
The application uses MVVM with a ViewModel first approach: our ViewModel is resolved by the container, and an IViewResolver service figures out what view it should be wired up to (using name conventions amongst other things).
The code (to add a view to a tab control) at the moment looks something like this:
var vm = (get ViewModel from somewhere)
IRegion reg = _regionManager.Regions["MainRegion"];
var vw = _viewResolver.FromViewModel(vm); // Spins up a view and sets its DataContext
reg.Add(vw);
reg.Activate(vw);
This all works fine, however I 'd really like to use the Prism navigation framework to do all this stuff for me so that I can do something like this:
_regionManager.RequestNavigate(
"MainRegion",
new Uri("NameOfMyViewModel", UriKind.Relative)
);
and have Prism spin up the ViewModel + View, set up the DataContext and insert the view into the region.
I 've had some success by creating DataTemplates referencing the ViewModel types, e.g.:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Module01">
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestView />
</DataTemplate>
</ResourceDictionary>
...and have the module add the relevant resource dictionary into the applications resources when the module is initialized, but that seems a bit rubbish.
Is there a way to effectively take over view creation from Prism, so that when RequestNavigate is called I can look at the supplied Uri and spin up the view / viewmodel based on that? There’s an overload of RegionManager.RegisterViewWithRegion that takes a delegate that allows you to supply a view yourself, and I guess I’m after something like that.
I think I might need to supply my own IRegionBehaviorFactory, but am unsure what's involved (or even if I am on the right path!).
Any help appreciated!
--
note: Originally posted over at the prism codeplex site
Sure you can do that. I 've found that Prism v4 is really extensible, if only you know where to plug in.
In this case, you want your own custom implementation of IRegionNavigationContentLoader.
Here's how to set things up in your bootstrapper (the example is from a subclass of UnityBootstrapper from one of my own projects):
protected override void ConfigureContainer()
{
// IMPORTANT: Due to the inner workings of UnityBootstrapper, accessing
// ServiceLocator.Current here will throw an exception!
// If you want access to IServiceLocator, resolve it from the container directly.
base.ConfigureContainer();
// Set up our own content loader, passing it a reference to the service locator
// (it will need this to resolve ViewModels from the container automatically)
this.Container.RegisterInstance<IRegionNavigationContentLoader>(
new ViewModelContentLoader(this.Container.Resolve<IServiceLocator>()));
}
The ViewModelContentLoader itself derives from RegionNavigationContentLoader to reuse code, and will look something like this:
public class ViewModelContentLoader : RegionNavigationContentLoader
{
private readonly IServiceLocator serviceLocator;
public ViewModelContentLoader(IServiceLocator serviceLocator)
: base(serviceLocator)
{
this.serviceLocator = serviceLocator;
}
// THIS IS CALLED WHEN A NEW VIEW NEEDS TO BE CREATED
// TO SATISFY A NAVIGATION REQUEST
protected override object CreateNewRegionItem(string candidateTargetContract)
{
// candidateTargetContract is e.g. "NameOfMyViewModel"
// Just a suggestion, plug in your own resolution code as you see fit
var viewModelType = this.GetTypeFromName(candidateTargetContract);
var viewModel = this.serviceLocator.GetInstance(viewModelType);
// get ref to viewResolver somehow -- perhaps from the container?
var view = _viewResolver.FromViewModel(vm);
return view;
}
// THIS IS CALLED TO DETERMINE IF THERE IS ANY EXISTING VIEW
// THAT CAN SATISFY A NAVIGATION REQUEST
protected override IEnumerable<object>
GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
{
if (region == null) {
throw new ArgumentNullException("region");
}
// Just a suggestion, plug in your own resolution code as you see fit
var viewModelType = this.GetTypeFromName(candidateNavigationContract);
return region.Views.Where(v =>
ViewHasDataContract((FrameworkElement)v, viewModelType) ||
string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
}
// USED IN MY IMPLEMENTATION OF GetCandidatesFromRegion
private static bool
ViewHasDataContract(FrameworkElement view, Type viewModelType)
{
var dataContextType = view.DataContext.GetType();
return viewModelType.IsInterface
? dataContextType.Implements(viewModelType)
: dataContextType == viewModelType
|| dataContextType.GetAncestors().Any(t => t == viewModelType);
}
// USED TO MAP STRINGS OF VIEWMODEL TYPE NAMES TO ACTUAL TYPES
private Type GetTypeFromName(string typeName)
{
// here you need to map the string type to a Type object, e.g.
// "NameOfMyViewModel" => typeof(NameOfMyViewModel)
return typeof(NameOfMyViewModel); // hardcoded for simplicity
}
}
To stop some confusion about "ViewModel first approach":
You use more a "controller approach", but no "ViewModel first approach". A "ViewModel first approach" is, when you inject your View in your ViewModel, but you wire up both, your ViewModel and View, through a third party component (a controller), what by the way is the (I dont want to say "best", but) most loosely coupled approach.
But to answer your Question:
A possible solution is to write an Extension for the Prism RegionManager that does exactly what you have described above:
public static class RegionManagerExtensions
{
public static void AddToRegion<TViewModel>(
this IRegionManager regionManager, string region)
{
var viewModel = ServiceLocator.Current.GetInstance<TViewModel>();
FrameworkElement view;
// Get View depending on your conventions
if (view == null) throw new NullReferenceException("View not found.");
view.DataContext = viewModel;
regionManager.AddToRegion(region, view);
regionManager.Regions[region].Activate(view);
}
}
then you can call this method like this:
regionManager.AddToRegion<IMyViewModel>("MyRegion");
I have a Silverlight application which has two different XAPs - an InitialXAP which is loaded statically by the HTML page and a DynamicXAP which is loaded from code within the initial XAP. The DynamicXAP is loaded with code similar to this:
var asm = LoadAssemblyFromXap(stream, "DLLName");
// LoadAssemblyFromXAP will load the DynamicXAP as a file stream,
// unpack it and load DLLName as a dll.
var controllerType = asm.GetType("ClassNameToInstantiate_InsideAsm");
var constructor = controllerType.GetConstructor(Type.EmptyTypes);
return constructor.Invoke(null);
I have a class which uses reflection (specifically FieldInfo.GetValue) to do data binding. This class is defined in the InitialXAP. If I try to use this class in the DynamicXAP, I get an error:
Message: Unhandled Error in Silverlight Application System.FieldAccessException: Class.In.DynamicXAP.Which.Uses.The.Reflection.Class.In.InitialXAP
at System.Reflection.RtFieldInfo.PerformVisibilityCheckOnField(IntPtr field, Object target, IntPtr declaringType, FieldAttributes attr, UInt32 invocationFlags)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck, Boolean doCheckConsistency)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck)
at System.Reflection.RtFieldInfo.GetValue(Object obj)
I can get around this error by creating a subclass of the class using reflection and overriding the method using reflection like so:
public class InitialXAP.ClassUsingReflection {
public virtual object GetValue()
{
return fieldInfo.GetValue(parent);
}
}
public class ClassUsingReflection : InitialXAP.ClassUsingReflection {
public override object GetValue()
{
return fieldInfo.GetValue(parent);
}
}
But I would prefer to avoid this duplication by allowing reflection from the InitialXAP in the DynamicXAP. Any ideas on what I can do?
Although there is a learning curve, I would look at Silverlight MEF or Prism (both are together at last in the latest Prism 4 Beta). They both support dynamic loading of modules and enforce good patterns for reuse and separate/team development.
InitialXAP.ClassUsingReflection...
Note the duplicate isn't part of the inital xap namespace (ClassUsingReflection), and may be imported.
Notice GetVisible - as in not visible to Dynamic xap...
Just leave the duplicate (take away base class obviously) and try.