I have recently puchased a very good book on MVVM - MVVM Survival Guide For Enterprise Architectures in Silverlight and WPF
Unfortunatly, one of the sections relating to IoC has a lot of code samples for StructureMap which is not available for Silverlight
Can anyone point me to a link that would help me translate Structure Map code to Autofac which is the injection tool I am looking at using
The code uses the factory mehod of creating classes and a bootstrapper
using Northwind.ViewModel;
using StructureMap;
namespace Northwind.UI.WPF
{
public class BootStrapper
{
public MainWindowViewModel MainWindowViewModel
{
get
{
return ObjectFactory
.GetInstance<MainWindowViewModel>();
}
}
public BootStrapper()
{
ObjectFactory.Initialize(
o => o.Scan(
a =>
{
a.WithDefaultConventions();
a.AssembliesFromApplicationBaseDirectory(
d => d.FullName
.StartsWith("Northwind"));
a.LookForRegistries();
}));
}
}
using StructureMap;
namespace Northwind.ViewModel
{
public class CustomerDetailsViewModelFactory
: ICustomerDetailsViewModelFactory
{
private readonly IContainer _container;
public CustomerDetailsViewModelFactory(
IContainer container)
{
_container = container;
}
public CustomerDetailsViewModel CreateInstance(
string customerID)
{
return _container
.With("customerID")
.EqualTo(customerID)
.GetInstance<CustomerDetailsViewModel>();
}
}
}
Paul
Autofac and StructureMap work differently, so you can't "translate" it one to one.
However, this is what it should look like to accomplish the same.
I've made some assumptions as not everything is there to test out your code.
public class BootStrapper
{
private readonly ILifetimeScope _container;
public BootStrapper()
{
var builder = new ContainerBuilder();
Assembly[] assemblies =
GetAssembliesFromApplicationBaseDirectory(
x => x.FullName.StartsWith("Northwind"));
builder.RegisterAssemblyTypes(assemblies)
.AsImplementedInterfaces();
// Module in Autofac = Registry in StructureMap
builder.RegisterAssemblyModules(assemblies);
Assembly viewModelAssembly =
typeof(MainWindowViewModel).Assembly;
builder.RegisterAssemblyTypes(viewModelAssembly);
_container = builder.Build();
}
private static Assembly[] GetAssembliesFromApplicationBaseDirectory(Func<AssemblyName, bool> condition)
{
string baseDirectoryPath =
AppDomain.CurrentDomain.BaseDirectory;
Func<string, bool> isAssembly =
file => string.Equals(
Path.GetExtension(file), ".dll", StringComparison.OrdinalIgnoreCase);
return Directory.GetFiles(baseDirectoryPath)
.Where(isAssembly)
.Where(f => condition(new AssemblyName(f)))
.Select(Assembly.LoadFrom)
.ToArray();
}
public MainWindowViewModel MainWindowViewModel
{
get
{
return _container.Resolve<MainWindowViewModel>();
}
}
}
public class CustomerDetailsViewModelFactory : ICustomerDetailsViewModelFactory
{
private readonly ILifetimeScope _container;
public CustomerDetailsViewModelFactory(ILifetimeScope container)
{
_container = container;
}
public CustomerDetailsViewModel CreateInstance(string customerID)
{
return _container.Resolve<CustomerDetailsViewModel>(
new NamedParameter("customerID", customerID));
}
}
Related
Hello I'm trying to setup an architecture where only one module gets booted when the app is launched. Then I'd like to lazy load other modules based on the user's actions.
To achieve this in my app.xaml.cs I have one module loaded at bootstrap time (MainModule), and an other has InitializationMode = InitializationMode.OnDemand
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
Type BlipModuleType = typeof(BlipModule);
moduleCatalog.AddModule(new ModuleInfo()
{
ModuleName = BlipModuleType.Name,
ModuleType = BlipModuleType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand
});
moduleCatalog.AddModule<MainModule>();
}
then my main module, which displays the view correctly, has a single view registered to the only region available:
public class MainModule : IModule
{
private readonly IRegionManager _regionManager;
public MainModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewA));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
The lazy loaded module has the same structure, registering a different view (which works properly if i decide to use it as my main module)
public class BlipModule : IModule
{
private readonly IRegionManager _regionManager;
public BlipModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewB));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
finally I have a Command in the viewmodel of my MainModule ViewA, that is supposed to load the new module and navigate to it.
public class ViewAViewModel : BindableBase
{
const string BlipModuleName = "BlipModule";
public ReactiveCommand ChangeRoute { get; set; } = new ReactiveCommand();
public ViewAViewModel(IRegionManager regionManager, IModuleManager moduleManager)
{
ChangeRoute.Subscribe(res =>
{
moduleManager.LoadModule(BlipModuleName);
});
moduleManager.LoadModuleCompleted += (s, e) =>
{
if (e.ModuleInfo.ModuleName == BlipModuleName)
{
regionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(BlipModuleName, UriKind.Relative));
}
};
}
}
The viewB of the BlipModule is actually loaded (I get a hit if I set a breakpoint in the view's constructor), but instead of the view I get a white page with "System.Object" inside of it.
Any idea? thanks!
You want to RegisterForNavigation instead of RegisterViewWithRegion.
WE have a ViewModelLocater class in our Silverlight App. It consist of a basic constructor and a public property to return the ViewModel for a class. The code is something like this
public class ViewModelLocator
{
private Dictionary<string, ViewModel> _viewModels =
new Dictionary<string, ViewModel>();
public ViewModelLocator()
{
_viewModels.Add("Home", HomeViewModel());
_viewModels.Add("Setup", new SetupViewModel());
_viewModels.Add("TasksActivities", new TasksActivitiesViewModel());
_viewModels.Add("Timesheet", new TimesheetViewModel());
}
public ViewModel this[string viewName]
{
get { return _viewModels[viewName]; }
}
}
and in each of the XAML pages we set the ViewModel for that page using
DataContext="{Binding [Setup], Source={StaticResource ViewModelLocator}}"
Setup is the key in the above dictionary.
The Silverlight App is really big and we have only recently started looking into any memory leaks(There are many...) I am using Windbg to track these leaks and I have noticed a lot of memory leaks leading back to the ViewModelLocater class. Every time the app loads the ViewModelLocator constructor creates ViewModels for all the Views. So I am wondering if there is a better way of implementing the ViewModelLocator class.
We use ViewModelLoader/ViewModelLocator to provide both DesignTime as well as Runtime DataContexts.
ViewModelLocator Class
public static class ViewModelLocator
{
public static readonly DependencyProperty FactoryProperty = DependencyProperty.RegisterAttached("Factory",
typeof (IViewModelFactory), typeof (ViewModelLocator),
new FrameworkPropertyMetadata(null, PropertyChangedCallback));
public static void SetFactory(DependencyObject dependencyObject, IViewModelFactory value)
{
dependencyObject.SetValue(FactoryProperty, value);
}
public static IViewModelFactory GetFactory(DependencyObject dependencyObject)
{
return (IViewModelFactory) dependencyObject.GetValue(FactoryProperty);
}
private static void PropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var fe = dependencyObject as FrameworkElement;
if (fe != null)
{
fe.DataContext = GetFactory(dependencyObject).Create();
}
}
}
IViewModelFactory
public interface IViewModelFactory
{
object Create();
}
ViewModelFactory
public class MainViewModelFactory : ViewModelFactoryBase
{
protected override object CreateDesignTimeViewModel()
{
return new MainViewModel(new DesignTimeEventAggregator(), new DesignTimeLogger(), new ViewModelViewRepository());
}
protected override object CreateViewModel()
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
ViewModelFactoryBase Class
public abstract class ViewModelFactoryBase : IViewModelFactory
{
protected abstract object CreateDesignTimeViewModel();
protected abstract object CreateViewModel();
public object Create()
{
return Designer.IsInDesignTime() ? CreateDesignTimeViewModel() : CreateViewModel();
}
}
And in XAML, this is how I hookup ViewModel Locator to View:
<viewModelLocation:ViewModelLocator.Factory>
<viewModelFactories:MainViewModelFactory />
</viewModelLocation:ViewModelLocator.Factory>
Our application implements MVVM-WPF-Unity and I adding a feature that will add plugins in our application thru MEF. Thus I have a Plugin Manager class and plugin class.
In the Plugin class module, I have this.
namespace project.plugin
{
[Export("Module",typeof(IModule))]
public class Plugin : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
}
[ImportingConstructor]
public Module(IRegionManager regionManager, IUnityContainer container)
{
this.regionManager = regionManager;
this.container = container;
}
public void Initialize()
{
RegisterViewsAndServices();
}
}
Inside my Plugin Manager I did this:
[ImportMany]
public IEnumerable<Lazy<IModule>> ImportedModules { get; set; }
AggregateCatalog moduleCatalog = new AggregateCatalog(
new AssemblyCatalog(typeof(IModule).Assembly),
new DirectoryCatalog("Plugins"));
CompositionContainer container = new CompositionContainer(moduleCatalog);
container.ComposeParts(this);
foreach (IModule iModule in ImportedModules)
{
iModule.Initialize();
}
The problem here is that ImportedModules seems to be empty although as I looked in my container catalog I can see the plugin class has been exported. Help please please please
models from shell-view-model with abstract factory pattern. I need inject in view-models classes from external assembly. If I use abstract factory pattern on creation view-models. Problem is imported classes in view-models are null.
Shell-view-models look like this:
public interface IViewModelFactory
{
ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel);
IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel);
}
[Export(typeof(IViewModelFactory))]
public class DefaulFactoryViewModel:IViewModelFactory
{
#region Implementation of IViewModelFactory
public ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel)
{
return new LogOnViewModel(shellViewModel);
}
public IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel)
{
return new MessengerViewModel(shellViewModel);
}
#endregion
}
public interface IShellViewModel
{
void ShowLogOnView();
void ShowMessengerView();
}
[Export(typeof(IShellViewModel))]
public class ShellViewModel : Conductor<IScreen>, IShellViewModel
{
private readonly IViewModelFactory _factory;
[ImportingConstructor]
public ShellViewModel(IViewModelFactory factory)
{
_factory = factory;
ShowLogOnView();
}
public void ShowLogOnView()
{
var model = _factory.CreateLogOnViewModel(this);
// var model = IoC.Get<LogOnViewModel>();
ActivateItem(model);
}
public void ShowMessengerView()
{
var model = _factory.CreateMessengerViewModel(this);
ActivateItem(model);
}
}
Some view-model.:
public class LogOnViewModel : Screen,ILogOnViewModel
{
[Import]//inject class from external assembly
private IPokecConnection _pokecConn;
private readonly IShellViewModel _shellViewModel=null;
private User _user=null;
public LogOnViewModel(IShellViewModel shellViewModel)
{
_shellViewModel = shellViewModel;
_user = new User();
}
}
variable _pokecConn are null becasuse I use abstract factory on creation new view-models.
if I use in shell-view model this:
var model = IoC.Get<LogOnViewModel>();
instead this:
var model = _factory.CreateLogOnViewModel(this);
and add Export attribute on view-models classes it works good, but I would like use abstract factory, and inject in view-model only classes from extrenal assembly.
It exist solution on this problem, or I must create view-models from IoC and export all class? Thanl for advance.
EDITED :
MEF BOOTSTRAPER CLASS:
public class MefBootStrapper : Bootstrapper<IShellViewModel>
{
#region Fields
private CompositionContainer _container;
#endregion
#region Overrides
protected override void Configure()
{ // configure container
#if SILVERLIGHT
_container = CompositionHost.Initialize(
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
#else
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
//add external DLL
catalog.Catalogs.Add(
new AssemblyCatalog(string.Format(
CultureInfo.InvariantCulture, "{0}{1}", System.IO.Directory.GetCurrentDirectory(), #"\Pokec_Toolkit.dll")));
_container = new CompositionContainer(catalog);
#endif
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(_container);
_container.Compose(batch);
_container.SatisfyImportsOnce(this);
}
protected override object GetInstance(Type serviceType, string key)
{
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);
if (exports.Count() > 0)
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
#endregion
}
Did you forget the attribute ImportingConstructor for the LogOnViewModel constructor?
EDIT: Import property always null (MEF import issue)
I am just learning MVVM with MEF and already see the benefits but I am a little confused about some implementation details. The app I am building has several Models that do the same with with different entities (WCF RIA Services exposing a Entity framework object) and I would like to avoid implementing a similar interface/model for each view I need and the following is what I have come up with though it currently doesn't work.
The common interface has a new completed event for each model that implements the base model, this was the easiest way I could implement a common class as the compiler did not like casting from a child to the base type.
The code as it currently sits compiles and runs but the is a null IModel being passed into the [ImportingConstructor] for the FaqViewModel class.
I have a common interface (simplified for posting) defined as follows, this should look familiar to those who have seen Shawn Wildermuth's RIAXboxGames sample.
public interface IModel
{
void GetItemsAsync();
event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}
A base method that implements the interface
public class ModelBase : IModel
{
public virtual void GetItemsAsync() { }
public virtual event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
protected void PerformQuery<T>(EntityQuery<T> qry, EventHandler<EntityResultsArgs<T>> evt) where T : Entity
{
Context.Load(qry, r =>
{
if (evt == null) return;
try
{
if (r.HasError)
{
evt(this, new EntityResultsArgs<T>(r.Error));
}
else if (r.Entities.Count() > 0)
{
evt(this, new EntityResultsArgs<T>(r.Entities));
}
}
catch (Exception ex)
{
evt(this, new EntityResultsArgs<T>(ex));
}
}, null);
}
private DomainContext _domainContext;
protected DomainContext Context
{
get
{
if (_domainContext == null)
{
_domainContext = new DomainContext();
_domainContext.PropertyChanged += DomainContext_PropertyChanged;
}
return _domainContext;
}
}
void DomainContext_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsLoading":
AppMessages.IsBusyMessage.Send(_domainContext.IsLoading);
break;
case "IsSubmitting":
AppMessages.IsBusyMessage.Send(_domainContext.IsSubmitting);
break;
}
}
}
A model that implements the base model
[Export(ViewModelTypes.FaqViewModel, typeof(IModel))]
public class FaqModel : ModelBase
{
public override void GetItemsAsync()
{
PerformQuery(Context.GetFaqsQuery(), GetFaqsComplete);
}
public override event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}
A view model
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.FaqViewModel)]
public class FaqViewModel : MyViewModelBase
{
private readonly IModel _model;
[ImportingConstructor]
public FaqViewModel(IModel model)
{
_model = model;
_model.GetFaqsComplete += Model_GetFaqsComplete;
_model.GetItemsAsync(); // Load FAQS on creation
}
private IEnumerable<faq> _faqs;
public IEnumerable<faq> Faqs
{
get { return _faqs; }
private set
{
if (value == _faqs) return;
_faqs = value;
RaisePropertyChanged("Faqs");
}
}
private faq _currentFaq;
public faq CurrentFaq
{
get { return _currentFaq; }
set
{
if (value == _currentFaq) return;
_currentFaq = value;
RaisePropertyChanged("CurrentFaq");
}
}
public void GetFaqsAsync()
{
_model.GetItemsAsync();
}
void Model_GetFaqsComplete(object sender, EntityResultsArgs<faq> e)
{
if (e.Error != null)
{
ErrorMessage = e.Error.Message;
}
else
{
Faqs = e.Results;
}
}
}
And then finally the Silverlight view itself
public partial class FrequentlyAskedQuestions
{
public FrequentlyAskedQuestions()
{
InitializeComponent();
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
}
[Import(ViewModelTypes.FaqViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
}
It seems as though I am going down the wrong path with trying to refactor into multiple models. As seen here, http://msdn.microsoft.com/en-us/magazine/dd458800.aspx#id0090019 if would appear the best thing to do would be to think about the model as an instance of the EDMX class referenced via RIA Services. As such, the model should contain all methods and event handlers needed to access the DomainContext.
If anybody has other thoughts I would be open to them.
As a fellow noob, I am just starting to play with MEF, and I think I have identified a possible problem with your code. From your question, it sounds like your main problem is the null IModel reference.
Try changing this:
private readonly IModel _model;
to this:
[Import]
public IModel _model { get; set; }
I haven't yet played with how MEF likes private and readonly properties, so try setting to public and then verify that _model isn't null when you first try to use it.