I am trying to integrate the Event Aggregator into my app and following the online documentation, I haven't quite been able to get it to work and also am unable to find a complete example to look at. The app I am trying to build is a simple search engine for our internal knowledge base, so all it has is 3 views - the main search page (the root page), the results page and a details page.
Below I've just added what I think is the relevant code so far for debugging this error I am getting on build. If you need any more snippets, then more than happy to provide!
Admittedly, I really dont know how the bootstrapper works - have just been following tutorials and documentation thus far.
My code is as follows:
Bootstrapper.cs
namespace CleverBot
{
class Bootstrapper : BootstrapperBase
{
private readonly SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
_container.RegisterPerRequest(typeof(DetailedDocumentViewModel), null, typeof(DetailedDocumentViewModel));
_container.RegisterPerRequest(typeof(SearchPageViewModel), null, typeof(SearchPageViewModel));
_container.RegisterPerRequest(typeof(SearchResultsViewModel), null, typeof(SearchResultsViewModel));
_container.RegisterPerRequest(typeof(ShellViewModel), null, typeof(ShellViewModel));
}
protected override object GetInstance(Type serviceType, string key)
{
return _container.GetInstance(serviceType, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
}
And my ShellViewModel.cs looks like this:
namespace CleverBot.ViewModels
{
public class ShellViewModel : Conductor<object>
{
private readonly IEventAggregator _eventAggregator;
public ShellViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
Startup.Init<SolrModel>("http://localhost:8983/solr/brain_drive");
ShowSearchPage();
}
public void ShowSearchPage()
{
ActivateItem(new SearchPageViewModel(_eventAggregator));
}
}
}
And finally, my SearchPageViewModel.cs looks like:
namespace CleverBot.ViewModels
{
public class SearchPageViewModel : PropertyChangedBase
{
private string _searchTerm;
private readonly IEventAggregator _eventAggregator;
public SearchPageViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public string SearchTerm
{
get { return _searchTerm; }
set
{
_searchTerm = value;
NotifyOfPropertyChange(() => SearchTerm);
}
}
public void BasicSearchButton()
{
// Build the new query object
var queryResult = new SolrQuery(_searchTerm);
// Execute the query, while also applying all the options
var Result = ExecuteQuery(queryResult);
// Publish the result, to be picked up by SearchResults VM
_eventAggregator.PublishOnUIThread(Result);
}
private SolrQueryResults<SolrModel> ExecuteQuery(SolrQuery query)
{
var solr = CommonServiceLocator.ServiceLocator.Current.GetInstance<ISolrOperations<SolrModel>>();
QueryOptions options = getQueryOptions();
var docs = solr.Query(query, options);
return docs;
}
private QueryOptions getQueryOptions()
{
var facetPivotQuery = new SolrFacetPivotQuery()
{
Fields = new[] { new PivotFields("created_year", "created_month") },
MinCount = 1
};
QueryOptions options = new QueryOptions
{
Facet = new FacetParameters
{
Queries = new ISolrFacetQuery[]
{
new SolrFacetFieldQuery("type"),
new SolrFacetFieldQuery("source"),
facetPivotQuery
}
},
Highlight = new HighlightingParameters
{
Fields = new[] { "text", "id", "author" },
Fragsize = 150,
Snippets = 200,
MaxAnalyzedChars = -1
}
};
return options;
}
}
}
Is anyone able to shed any light on why I am getting the System.NullReferenceException error? I know what it means but I don't see how I am getting it as am just starting out with this framework.
Thanks in advance!
It looks like I had to register the following:
_container.Singleton<IWindowManager, WindowManager>();
in my bootstrapper.cs, so now it looks like:
namespace CleverBot
{
class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
// THE FOLLOWING WAS ADDED
_container.Singleton<IWindowManager, WindowManager>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override object GetInstance(Type serviceType, string key)
{
return _container.GetInstance(serviceType, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
}
Related
container.RegisterType<IDataContextFactory<MyDataContext>, DefaultDataContextFactory<MyDataContext>>(new PerRequestLifetimeManager());
Created a PerRequestLifetimeManager using OperationContext but it does not seem call setValue function at all, it always trys to go to GetValue() function which always retruns null since nothing has been set.
My goal is to create a lifetimeManager for dbconetxt that will give me a new dbContext per method call. transient is not an option since it won;t work for join query.
public class WcfOperationContext : IExtension<OperationContext>
{
private readonly IDictionary<string, object> items;
private WcfOperationContext()
{
items = new Dictionary<string, object>();
}
public IDictionary<string, object> Items
{
get { return items; }
}
public static WcfOperationContext Current
{
get
{
WcfOperationContext context = OperationContext.Current.Extensions.Find<WcfOperationContext>();
if (context == null)
{
context = new WcfOperationContext();
OperationContext.Current.Extensions.Add(context);
}
return context;
}
}
public void Attach(OperationContext owner) { }
public void Detach(OperationContext owner) { }
}
public class PerRequestLifetimeManager : LifetimeManager
{
private string key;
public PerRequestLifetimeManager()
{
key = Guid.NewGuid().ToString();
}
public override object GetValue()
{
if (WcfOperationContext.Current == null)
{
return null;
}
else
{
return WcfOperationContext.Current.Items[key];
}
}
public override void RemoveValue()
{
if (WcfOperationContext.Current != null)
{
WcfOperationContext.Current.Items.Remove(key);
}
}
public override void SetValue(object newValue)
{
if (WcfOperationContext.Current != null)
{
WcfOperationContext.Current.Items.Add(key, newValue);
}
}
}
My solution for this was to use this nuget package: UnityWCF
The Service should be instantiated by Unity and new instance per call.
For this use this settings on the service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ...
Inject DbContext where you need. And register in Unity like this:
container.RegisterType<DbContext, YourDbContext>(new HierarchicalLifetimeManager(), ...);
I have been building a application with PRISM, this application is going to be groing alot over the coming years so it is set up modular and with IoC in mind.
In this application I am currently implementing Nlog for the logging mechanism.
Because I do not want to add a reference to NLog throughout my whole application I have created an interface for the logger
public interface ILog
{
void Log(LogLevel loglevel, string format);
void Log(LogLevel loglevel, string format, params object[] args);
void Log(LogLevel loglevel, Exception exc, string format, params object[] args);
}
I have also created an interface for the factory
public interface ILogFactory
{
ILog Initialize(Type type);
}
The factory takes a Type parameter that can be used to created a logger specific for that class. Now comes the challenge where I am going to use the ILog I would like to have a instance using the target types class.
public class ClassA
{
private ILog Logger { get; set;}
public ClassA(Ilog logger)
{
Logger = logger;
}
}
How can I wire my ILogFactory into PRISM so on a resolve of a class that uses Ilog that is uses the ILogFactory.Initialize(Type type) with in this case typeof(ClassA).
So after searching more of the web I finally found what I was looking for at
this blog
I had to modify the code a bid to make it not depend on log4net this resulted in the following code
A BuildTrackingExtension which we need to know where the logger we are going the create is needed.
public class BuildTrackingExtension : UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.AddNew<BuildTrackingStrategy>(UnityBuildStage.TypeMapping);
}
public static IBuildTrackingPolicy GetPolicy(IBuilderContext context)
{
return context.Policies.Get<IBuildTrackingPolicy>(context.BuildKey, true);
}
public static IBuildTrackingPolicy SetPolicy(IBuilderContext context)
{
IBuildTrackingPolicy policy = new BuildTrackingPolicy();
context.Policies.SetDefault(policy);
return policy;
}
}
public class BuildTrackingStrategy : BuilderStrategy
{
public override void PreBuildUp(IBuilderContext context)
{
var policy = BuildTrackingExtension.GetPolicy(context)
?? BuildTrackingExtension.SetPolicy(context);
policy.BuildKeys.Push(context.BuildKey);
}
public override void PostBuildUp(IBuilderContext context)
{
IBuildTrackingPolicy policy = BuildTrackingExtension.GetPolicy(context);
if ((policy != null) && (policy.BuildKeys.Count > 0))
{
policy.BuildKeys.Pop();
}
}
}
public interface IBuildTrackingPolicy : IBuilderPolicy
{
Stack<object> BuildKeys { get; }
}
public class BuildTrackingPolicy : IBuildTrackingPolicy
{
public BuildTrackingPolicy()
{
BuildKeys = new Stack<object>();
}
public Stack<object> BuildKeys { get; private set; }
}
Then a LogCreationExtension to create the logger from my ILogFactory
public class LogCreationExtension : UnityContainerExtension
{
private ILogFactory LogFactory;
private LogCreationStrategy strategy;
public LogCreationExtension(ILogFactory logFactory)
{
LogFactory = logFactory;
}
protected override void Initialize()
{
strategy = new LogCreationStrategy(LogFactory);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
}
public class LogCreationStrategy : BuilderStrategy
{
public bool IsPolicySet { get; private set; }
private ILogFactory LogFactory;
public LogCreationStrategy(ILogFactory logFactory)
{
LogFactory = logFactory;
}
public override void PreBuildUp(IBuilderContext context)
{
Type typeToBuild = context.BuildKey.Type;
if (typeof(ILog).Equals(typeToBuild))
{
if (context.Policies.Get<IBuildPlanPolicy>(context.BuildKey) == null)
{
Type typeForLog = LogCreationStrategy.GetLogType(context);
IBuildPlanPolicy policy = new LogBuildPlanPolicy(typeForLog, LogFactory);
context.Policies.Set<IBuildPlanPolicy>(policy, context.BuildKey);
IsPolicySet = true;
}
}
}
public override void PostBuildUp(IBuilderContext context)
{
if (IsPolicySet)
{
context.Policies.Clear<IBuildPlanPolicy>(context.BuildKey);
IsPolicySet = false;
}
}
private static Type GetLogType(IBuilderContext context)
{
Type logType = null;
IBuildTrackingPolicy buildTrackingPolicy = BuildTrackingExtension.GetPolicy(context);
if ((buildTrackingPolicy != null) && (buildTrackingPolicy.BuildKeys.Count >= 2))
{
logType = ((NamedTypeBuildKey)buildTrackingPolicy.BuildKeys.ElementAt(1)).Type;
}
else
{
StackTrace stackTrace = new StackTrace();
//first two are in the log creation strategy, can skip over them
for (int i = 2; i < stackTrace.FrameCount; i++)
{
StackFrame frame = stackTrace.GetFrame(i);
logType = frame.GetMethod().DeclaringType;
if (!logType.FullName.StartsWith("Microsoft.Practices"))
{
break;
}
}
}
return logType;
}
}
public class LogBuildPlanPolicy : IBuildPlanPolicy
{
private ILogFactory LogFactory;
public LogBuildPlanPolicy(Type logType, ILogFactory logFactory)
{
LogType = logType;
LogFactory = logFactory;
}
public Type LogType { get; private set; }
public void BuildUp(IBuilderContext context)
{
if (context.Existing == null)
{
ILog log = LogFactory.Initialize(LogType);
context.Existing = log;
}
}
}
Wiring it up using unity
//Container.RegisterType<ILogFactory, NLogLogFactory>();
Container.RegisterInstance<ILogFactory>(_LogFactory);
Container.AddNewExtension<BuildTrackingExtension>();
Container.AddNewExtension<LogCreationExtension>();
I am using RegisterInstance because I am using the logfactory before InitializeShell is called
We have some legacy app assume that we can not change that SiteSettings class because complete project coding thousands of line will disturb. so we want to solve problem using DI. I created POC app here you can see in global asax there is comment //HOW CAN I PASS TenantId HERE so it will be same for this complete httprequest life.
LegacyCode:
public class OrderController
{
public static string CompleteOrder()
{
return SiteSettings.Instance.DefaultTimeZone();
}
}
public class SiteSettings
{
public ITenantSettings TenantSettings { get; set; }
private static SiteSettings _instance;
private SiteSettings() { }
public static SiteSettings Instance => _instance ?? (_instance = new SiteSettings());
public string DefaultTimeZone()
{
return TenantSettings.DefaultTimeZone();
}
}
New Classes for Injection
public interface ITenantSettings
{
string DefaultTimeZone();
}
public class TenantSettings : ITenantSettings
{
private readonly int _tenantId;
public TenantSettings(int tenantId)
{
_tenantId = tenantId;
}
public string DefaultTimeZone()
{
return "USA Time For Tenant ID " + _tenantId.ToString();
}
}
Global ASAX
public class Global : HttpApplication, IContainerProviderAccessor
{
// Provider that holds the application container.
static IContainerProvider _containerProvider;
// Instance property that will be used by Autofac HttpModules
// to resolve and inject dependencies.
public IContainerProvider ContainerProvider => _containerProvider;
protected void Application_Start(object sender, EventArgs e)
{
// Build up your application container and register your dependencies.
var builder = new ContainerBuilder();
builder.RegisterType<TenantSettings>().As<ITenantSettings>().InstancePerRequest();
_containerProvider = new ContainerProvider(builder.Build());
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
int id = 0;
int.TryParse(HttpContext.Current.Request.QueryString["id"], out id);
var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
var cp = cpa.ContainerProvider;
cp.RequestLifetime.InjectProperties(SiteSettings.Instance);
//HOW CAN I PASS TENANTID HERE so it will be same for this complete httprequest life.
}
}
Default ASPX
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(OrderController.CompleteOrder());
}
}
Error :
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'CoreLibrary.Tenants.TenantSettings' can be invoked with the available services and parameters:
Cannot resolve parameter 'Int32 tenantId' of constructor 'Void .ctor(Int32)'.
You can use WithParameter, in this instance I would suggest the ResolvedParameter:
builder.RegisterType<TenantSettings>()
.As<ITenantSettings>()
.InstancePerRequest()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "tenantId",
(pi, ctx) => int.Parse(HttpContext.Current.Request.QueryString["id"])));
In reality you will need something a little more resilient than int.Parse(HttpContext.Current.Request.QueryString["id"]) but this gives you a flavour of a solution
Update
We need to remove the line _instance ?? (_instance = new SiteSettings()) if we are to inject the dependencies. In my example SiteSettings now has a static Initialise method, and this method is the used to construct the value of SiteSettings.Instance.
Currently we are only interested in injecting ITenantSettings and as we want ITenantSettings to have a lesser lifetime scope (per request) than the scope of SiteSettings (singleton) we should inject a delegate (Func<ITenantSettings>).
public class SiteSettings
{
private static SiteSettings _instance;
private Func<ITenantSettings> _tenantSettingsFactory;
private SiteSettings(Func<ITenantSettings> tenantSettingsFactory)
{
_tenantSettingsFactory = tenantSettingsFactory;
}
public static void Initialise(Func<ITenantSettings> tenantSettingsFactory)
{
_instance = new SiteSettings(tenantSettingsFactory);
}
public ITenantSettings TenantSettings { get { return _tenantSettingsFactory(); } }
public static SiteSettings Instance
{
get {
if (_instance == null) throw new InvalidOperationException();
return _instance;
}
}
public string DefaultTimeZone()
{
return TenantSettings.DefaultTimeZone();
}
}
Here's a test that demonstrates what you are asking:
[Fact]
public void Demonstrate_TenantSettingsFactory_AlwaysResolvesCurrentTenantId()
{
int tenantId = 0;
var builder = new ContainerBuilder();
builder.RegisterType<TenantSettings>()
.As<ITenantSettings>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "tenantId",
(pi, ctx) => tenantId));
var container = builder.Build();
SiteSettings.Initialise(container.Resolve<ITenantSettings>);
tenantId = 1;
Assert.Equal("USA Time For Tenant ID 1", SiteSettings.Instance.DefaultTimeZone());
tenantId = 2;
Assert.Equal("USA Time For Tenant ID 2", SiteSettings.Instance.DefaultTimeZone());
}
Note I removed InstancePerRequest and HttpContext.Current as I am using a unit test project.
I am using Structuremap as my Dependency Resolver. I am trying to implement Container Per Request Pattern on my Global.asax.cs file.
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
public void Application_BeginRequest()
{
Container = ObjectFactory.Container.GetNestedContainer();
}
As the ObjectFactory will not be supported in the future versions of Structuremap I would like get access to the container from the DependencyResolver. How is possible?
Thanks in Advance.
Noufal
Having ran into this question myself, this was the best guide I could find for registering StructureMap with ASP.NET MVC's Dependency Resolver (via the CommonServiceLocator package).
I've copied and pasted the aforementioned article's solution, but I would recommend going through the benefits of this solution in the original article.
public class StructureMapDependencyResolver : ServiceLocatorImplBase
{
private const string StructuremapNestedContainerKey = "Structuremap.Nested.Container";
public IContainer Container { get; set; }
private HttpContextBase HttpContext
{
get
{
var ctx = Container.TryGetInstance<HttpContextBase>();
return ctx ?? new HttpContextWrapper(System.Web.HttpContext.Current);
}
}
public IContainer CurrentNestedContainer
{
get { return (IContainer)HttpContext.Items[StructuremapNestedContainerKey]; }
set { HttpContext.Items[StructuremapNestedContainerKey] = value; }
}
public StructureMapDependencyResolver(IContainer container)
{
Container = container;
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
return (CurrentNestedContainer ?? Container).GetAllInstances(serviceType).Cast<object>();
}
protected override object DoGetInstance(Type serviceType, string key)
{
var container = (CurrentNestedContainer ?? Container);
if (string.IsNullOrEmpty(key))
{
return serviceType.IsAbstract || serviceType.IsInterface
? container.TryGetInstance(serviceType)
: container.GetInstance(serviceType);
}
return container.GetInstance(serviceType, key);
}
public void Dispose()
{
if (CurrentNestedContainer != null)
{
CurrentNestedContainer.Dispose();
}
Container.Dispose();
}
public IEnumerable<object> GetServices(Type serviceType)
{
return DoGetAllInstances(serviceType);
}
public void DisposeNestedContainer()
{
if (CurrentNestedContainer != null)
CurrentNestedContainer.Dispose();
}
public void CreateNestedContainer()
{
if (CurrentNestedContainer != null) return;
CurrentNestedContainer = Container.GetNestedContainer();
}
}
You can then set the resolver like so:
public class MvcApplication : System.Web.HttpApplication
{
public static StructureMapDependencyResolver StructureMapResolver { get; set; }
protected void Application_Start()
{
...
// Setup your Container before
var container = IoC.Initialize();
StructureMapResolver = new StructureMapDependencyResolver(container);
DependencyResolver.SetResolver(StructureMapResolver);
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
StructureMapResolver.CreateNestedContainer();
}
protected void Application_EndRequest(object sender, EventArgs e)
{
StructureMapResolver.DisposeNestedContainer();
}
}
The great result of this type of configuration is you receive a new child container per request, with the container being disposed of at the end of each request.
I just tried this and its working, please let me if it is not the best way.
StructuremapMvc.StructureMapDependencyScope.Container
There are two dependency resolvers one for ASP.NET MVC and other for ASP.NET Web Api
Web Api: Use WebApiContrib.IoC.StructureMap.StructureMapResolver
MVC: Use StructureMapDependencyResolver
public class StructureMapDependencyResolver : StructureMapDependencyScope, IDependencyResolver
{
public StructureMapDependencyResolver(IContainer container)
: base(container)
{
}
public IDependencyScope BeginScope()
{
var child = Container.GetNestedContainer();
return new StructureMapDependencyResolver(child);
}
}
public class StructureMapDependencyScope : ServiceLocatorImplBase, IDependencyScope
{
protected readonly IContainer Container;
public StructureMapDependencyScope(IContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
Container = container;
}
public void Dispose()
{
Container.Dispose();
}
public override object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
return serviceType.IsAbstract || serviceType.IsInterface
? Container.TryGetInstance(serviceType)
: Container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return Container.GetAllInstances(serviceType).Cast<object>();
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
return Container.GetAllInstances(serviceType).Cast<object>();
}
protected override object DoGetInstance(Type serviceType, string key)
{
if (string.IsNullOrEmpty(key))
{
return serviceType.IsAbstract || serviceType.IsInterface
? Container.TryGetInstance(serviceType)
: Container.GetInstance(serviceType);
}
return Container.GetInstance(serviceType, key);
}
}
Usage is as follows...
public static class Ioc
{
public static void Config()
{
var container = InitializeContainer();
var webApiDependencyResolver = new StructureMapResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiDependencyResolver;
var mvcDependencyResolver = new StructureMapDependencyResolver(container);
DependencyResolver.SetResolver(mvcDependencyResolver);
}
}
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Ioc.Config();
...
}
}
i am trying to use Caliburn Micro in my windows phone 7 project.
But i got a nullreferenceexception when navigate the page.
namespace Caliburn.Micro.HelloWP7 {
public class MainPageViewModel {
readonly INavigationService navigationService;
public MainPageViewModel(INavigationService navigationService) {
this.navigationService = navigationService;
}
public void GotoPageTwo() {
/*navigationService.UriFor<PivotPageViewModel>()
.WithParam(x => x.NumberOfTabs, 5)
.Navigate();*/
navigationService.UriFor<Page1ViewModel>().Navigate();
}
}
}
namespace Caliburn.Micro.HelloWP7
{
public class Page1ViewModel
{
readonly INavigationService navigationService;
public Page1ViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
}
}
}
can anyone tell me what's the problem of my code? thanks in advance.
here is bootstrapper:
public class ScheduleBootstrapper : PhoneBootstrapper
{
PhoneContainer container;
protected override void Configure()
{
container = new PhoneContainer(RootFrame);
container.RegisterPhoneServices();
container.PerRequest<MainPageViewModel>();
container.PerRequest<MainContentViewModel>();
container.PerRequest<Page1ViewModel>();
AddCustomConventions();
}
static void AddCustomConventions()
{
ConventionManager.AddElementConvention<Pivot>(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Pivot.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
ConventionManager.AddElementConvention<Panorama>(Panorama.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
}
protected override object GetInstance(Type service, string key)
{
return container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
}
I had this too, and tracked it down as follows:
As you know, Caliburn.Micro uses convention-over-configuration to locate Views for ViewModels, and vice-versa, which means we need to follow the conventions. My mistake was to have the namespace's inconsistent for the View and ViewModel
In my case, I had
MyWP7App.DetailsViewModel, and
MyWP7App.Views.DetailsView
--> I renamed the VM's namespace to be MyWP7App.ViewModels.DetailsViewModel, and it worked out fine. I think I could have moved the view into MyWP7App.DetailsView for a good result, too...
Under the covers
the call to Navigate() invokes DeterminePageName() which, in turn, invokes ViewLocator.LocateTypeForModelType
This, like the rest of CM is overridable, but the default implementation looks like this:
public static Func<Type, DependencyObject, object, Type> LocateTypeForModelType = (modelType, displayLocation, context) => {
var viewTypeName = modelType.FullName.Substring(
0,
modelType.FullName.IndexOf("`") < 0
? modelType.FullName.Length
: modelType.FullName.IndexOf("`")
);
Func<string, string> getReplaceString;
if (context == null) {
getReplaceString = r => { return r; };
}
else {
getReplaceString = r => {
return Regex.Replace(r, Regex.IsMatch(r, "Page$") ? "Page$" : "View$", ContextSeparator + context);
};
}
var viewTypeList = NameTransformer.Transform(viewTypeName, getReplaceString);
var viewType = (from assembly in AssemblySource.Instance
from type in assembly.GetExportedTypes()
where viewTypeList.Contains(type.FullName)
select type).FirstOrDefault();
return viewType;
};
If you follow the debugger through, you end up with a collection viewTypeList that contains MyWP7App.DetailsView, and a type whose full name is MyWP7App.Views.DetailsView, and the viewType returned is therefore null... this is the cause of the NullReferenceException.
I'm 99% sure the NameTransformer.Transform call will perform a pattern-match and transform the ViewModels in the namespace of the VM to Views in the namespace of the View it's trying to locate...