Hi try port my Winform app to WPF with MVVM. I use Caliburn Micro and MEF as IoC. I have service in external dll -> Pokec_Toolki.dll and this services use other external dll such as JSON.NET, HtmlAgilityPack..etc (third party libraries).
My service look like this:
public interface IPokecConnection
{
PokecAccount LogOn(string nick, string password);
bool LogOff(PokecAccount account);
}
[Export(typeof(IPokecConnection))]
public class PokecConnection : IPokecConnection
{
}
For my Service I create consumer for class PokecConnection, it is in the same assembly as Service:
[Export]
public class PokecConnectionConsumer
{
private readonly IPokecConnection _pokecConn;
[ImportingConstructor]
public PokecConnectionConsumer(IPokecConnection pokecConn)
{
_pokecConn = pokecConn;
}
public PokecAccount MethodWrapperForLogOn(string name, string password)
{
return _pokecConn.LogOn(name, password);
}
}
I have simple view with 2 textbox and 1 button. I bind event click of button with caliburn convention on view-model.
View:
<Button Micro:Message.Attach="[Event Click]=[Action LogOn(tbNick.Text,tbPassword.Text)]"
Content="Prihlás ma"
Width="100"
Grid.Row="2"
Height="25" Margin="4,4,4,4" />
I need consume service method in my view model. So in view-model I have this:
[Export(typeof(IShellViewModel))]
public class ShellViewModel : Screen, IShellViewModel, IDataErrorInfo
{
private PokecConnectionConsumer _connConsumer;
protected override void OnInitialize()
{
base.OnInitialize();
var mycatalog = new AssemblyCatalog(System.Reflection.Assembly.LoadFrom("Pokec_Toolkit.dll"));
var container = new CompositionContainer(mycatalog);
container.ComposeParts(this);
_connConsumer = container.GetExportedValue<PokecConnectionConsumer>();
}
//this method is bind on event click of button in view
public void LogOn(string nick, string password)
{
//call method LogOn from class PokecConnection in external dll through PokecConnectionConsumer
var accout = _connConsumer.MethodWrapperForLogOn(nick, password);
// in this place if account is not null, I need close this view and create new view MainView
//a send to the MainViewModel constructor as argument variable account
//some test
MessageBox.Show(accout.SessionId);
}
}
I have these question:
First, It is good create consumer
for service class in external dll?
In my case PokecConnectionConsumer
**and this class is in same assembly
as service class PokecConnection?
It is correct way to load external
assembly in view-model in method
OnInitialize?
It is correct call service method in
view-model?
It exist more suitable way how bind
method from external dll on view
control ? I need simple load external dll in wpf app with MVVM
and bind service method on controls
in views. What is the best way?
How can I close actual view and create new view, also activate this view. I need send as argument variable from view-model to another vie-model constructor.**
MY SOLUTION:
I create interface assembly and refer this assembly in external service dll and also in wpf app.
In bootstraper I load this assembly with reflection:
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
catalog.Catalogs.Add(
new AssemblyCatalog(string.Format(
CultureInfo.InvariantCulture, "{0}{1}", System.IO.Directory.GetCurrentDirectory(), #"\Pokec_Toolkit.dll")));
_container = new CompositionContainer(catalog);
Than I create conductor class:
public interface IShellViewModel
{
void ShowLogOnView();
void ShowMessengerView();
}
[Export(typeof(IShellViewModel))]
public class ShellViewModel : Conductor<IScreen>, IShellViewModel
{
public ShellViewModel()
{
ShowLogOnView();
}
public void ShowLogOnView()
{
ActivateItem(IoC.Get<LogOnViewModel>());
}
public void ShowMessengerView()
{
ActivateItem(IoC.Get<MessengerViewModel>());
}
}
And in view-model I have this:
[Export]
public class LogOnViewModel : Screen, IDataErrorInfo, ILogOnViewModel
{
[Import]
private IShellViewModel _shellViewModel;
[Import]
private IPokecConnection _pokecConn;
//this method is bind on event click of button
public void LogOn(string nick, string password)
{
//SHOW NEW WIEW
_shellViewModel.ShowMessengerView();
}
}
1) Yes, so that if the external dependency changes, you do not have to recompile your UI. Also it must be outside View/VM logic.
2) Not I am afraid. Create a separate service class for it.
3) Same, Create a separate service class for it and use within VM.
4) Well, you have to load a DLL anyway. So load it in the service class.
Related
I have lots of repositories like this:
public class PersonRepository : IPersonRepository
{
private readonly IUnitOfWork _unitOfWork;
public PersonRepository(IUnitOfWork instance)
{
_unitOfWork = instance;
}
//Remove, Get methods...
public void Add(Person p)
{
_unitOfWork.Context.People.Add(p);
}
}
and Unit of work class like this:
public class UnitOfWork : IUnitOfWork, IDisposable
{
public UnitOfWork(){ }
private readonly HezarehContext _context = new HezarehContext();
public HezarehContext Context
{
get
{
return _context;
}
}
public int Save()
{
return _context.SaveChanges();
}
public void Initialize()
{
Context.Database.Initialize(false);
}
#region IDisposable Members
public void Dispose()
{
_context.Dispose();
}
#endregion
}
Now i want each time my ViewModels gets resolved, a new IUnitOfWork instantiated. Most of my ViewModels are like this:
public class PeopleMainViewModel : NotificationObject
{
// both of repositories must have same instance of IUnitOfWork
private readonly IPersonRepository _personRepository = ServiceLocator.Current.GetService<IPersonRepository>();
private readonly ICategoryRepository _categoryRepository = ServiceLocator.Current.GetService<ICategoryRepository>();
public PeopleMainViewModel()
{
InitializeView();
}
// add, edit, remove commands ...
}
ViewModels always gets resolved using Unity Container like this:
Container.RegisterType<IPersonRepository, PersonRepository>();
// resolve in InjectionProperty...
Container.RegisterType<Object, PeopleMainView>("PeopleMainView", new InjectionProperty(PeopleMainView.DataContextProperty.Name, Container.Resolve<PeopleMainViewModel>();
And my question is, How and Where i Register my ViewModels and IUnitOfWork to have IUnitOfWork instance for each of them?
If I understand your question, just register your IUnitOfWork the same way (and same place) you register the repository in your above example. You don't need to register your ViewModels based on your current design since you aren't using an Interface.
Container.RegisterType<IUnitOfWork, UnitOfWork>();
And continue to have your repositories accept the IUnitOfWork in the constructor. This will allow Unity to use constructor injection to provide a new instance of IUnitOfWork each time it resolves a repository. By default, you'll get a new instance of the IUnitOfWork each time. If you'd like to have a singleton IUnitOfWork, you would have to say so when you register the IUnitOfWork like this:
Container.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager());
If you want to read up on Lifetime Managers, you can do so here.
I would also recommend changing your ViewModels to take the repositories in as Constructor Parameters, like this if you are going to Resolve them (so Unity will do the work without you referencing the ServiceLocator directly)
public PeopleMainViewModel(IPersonRepository personRepo, ICategoryRepository categoryRepo)
{
...
}
Update:
There is another solution here in unity.codeplex discussions.
I finally found a solution.
There is a feature in Unity container that let you pass parameters while resolving a Type. by changing constructor of ViewModels to this:
public class PeopleMainViewModel : NotificationObject
{
private readonly IPersonRepository _personRepository = null;
private readonly ICategoryRepository _categoryRepository = null;
public PeopleMainViewModel(IUnityContainer container, IUnitOfWork unitOfWork)
{
// now both of repositories have same instance of IUnitOfWork
_personRepository = container.Resolve<IPersonRepository>(new ParameterOverride("unitOfWork", unitOfWork));
_categoryRepository = container.Resolve<ICategoryRepository>(new ParameterOverride("unitOfWork", unitOfWork));
InitializeView();
}
// add, edit, remove commands ...
}
problem solved. now _personReposiotry and _categoryRepository have reference to same instance of unitOfWork.
I use Castle Windsor bootstrapper in my WPF app. I tried register view model classes to windsor container.
I have all view models classes in namespace sample.viewmodels
So I tried this:
_windsorContainer.Register(AllTypes.FromAssembly(GetType().Assembly)
.Where(x => x.Namespace.EndsWith("ViewModels"))
.Configure(x => x.LifeStyle.Is(LifestyleType.Singleton)));
Or this way for register view model classes to container:
_windsorContainer.Register(AllTypes.FromAssembly(GetType().Assembly)
.Where(x => x.Namespace.Equals("Sample.ViewModels"))
.Configure(x => x.LifeStyle.Is(LifestyleType.Singleton)));
This way doesn't work. I don’t why. MainViewModel / ChildViewModel property is null.
If I use this style it works good.
////Shell
_windsorContainer.Register(
Component.For<IShellViewModel>()
.ImplementedBy<ShellViewModel>()
.LifeStyle.Is(LifestyleType.Singleton));
//Main screen
_windsorContainer.Register(
Component.For<IMainViewModel>()
.ImplementedBy<MainViewModel>()
.LifeStyle.Is(LifestyleType.Singleton));
//Child screen
_windsorContainer.Register(
Component.For<IChildViewModel>()
.ImplementedBy<ChildViewModel>()
.LifeStyle.Is(LifestyleType.Singleton));
What is bad in first case?
Here I have view models:
namespace Sample.ViewModels
{
public interface IShellViewModel
{}
public class ShellViewModel : Conductor<IScreen>.Collection.AllActive,
IShellViewModel
{
public IMainViewModel MainViewModel { get; set; }
public IChildViewModel ChildViewModel { get; set; }
protected override void OnInitialize()
{
DisplayName = "Castle Windsor Boostraper";
base.OnInitialize();
}
}
public interface IMainViewModel : IScreen
{
void SayHello();
}
public class MainViewModel : Screen, IMainViewModel
{
public void SayHello()
{
MessageBox.Show("Hello from MainViewModel");
}
}
public interface IChildViewModel : IScreen
{
void SayHello();
}
public class ChildViewModel : Screen, IChildViewModel
{
public void SayHello()
{
MessageBox.Show("Hello from ChildViewModel");
}
}
}
EDITED:
I little update registration part:
_windsorContainer.Register(AllTypes.FromAssembly(GetType().Assembly)
.Pick()
.WithService.DefaultInterface()
.Configure(x => x.LifeStyle.Is(LifestyleType.Singleton)));
It work but I am not sure if this is best way.
I think you want to do something like this to register your viewmodels in Castle Windosr
_windsorContainer.Register(AllTypes.FromThisAssembly()
.Where(x => x.Namespace.EndsWith("ViewModels"))
.WithService.AllInterfaces()
.WithService.Self()
.If(s => !s.IsAbstract)
.Configure(x => x.LifeStyle.Is(LifestyleType.Singleton)));
But I'm wondering why you register your viewmodels as singleton? I would register them as transient, so you could instanciate multiple different viewmodels for the same view in the application. I also use a marker-interface for my viewmodels, so I don't have to rely on the viewmodels on being in a namespace that ends with "ViewModels".
_windsorContainer.Register(Classes
.FromThisAssembly()
.BasedOn<IViewModelBase>()
.WithServiceAllInterfaces()
.WithServiceSelf()
.LifestyleTransient()
in the first example you are only registering the concrete types to be singletons, the second example you associate the relevant interfaces with the implementations. since your properties expose the models as interfaces, the first code part will not be able to satisfy those properties.
I'm not sure the best way to get this accomplished. Here's my view:
public partial class MyPage : Page
{
[Import]
public MyVM ViewModel
{
get { return DataContext as MyVM ; }
set { DataContext = value; }
}
public String EventName { get; set; }
public MyPage()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{ }
}
And my VM:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ()
{
}
}
This works great. However, I need to get data from either the viewmodel that has my string, or the URL. Either way, I'm not sure the best way to get the string to MyVW using MEF.
I thought ok I'll use Messaging from MVVMLight, but the MyVM class isn't instantiated yet to receive the broadcast from the other ViewModel. So then I thought well, I'll try this:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ([Import("hello")]string hello)
{
}
}
and then put this in the view:
[Export("hello")]
public String MyHello { get; set; }
but that gave me an error. Cannot call SatisfyImports on a object of type 'Form A' because it is marked with one or more ExportAttributes.
So what's the best way to accomplish this?
To share data between views I usually inject a SharedData object into my ViewModels.
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public ISharedData SharedData { get; set; }
I'm also using the Caliburn Micro framework so I'm not passing data around via the URL querystring. By convention CM will parse out URL parameters and inject them into properties on your VM but I'm not sure if this functionality only applies to Windows Phone development.
from here
Examine the Page’s QueryString. Look
for properties on the VM that match
the QueryString parameters and inject
them, performing the necessary type
coercion.
When you say you want to possibly pass data from the view to the vm, that should happen through databinding.
Edit:
I have found one method to do this but I'm not sure if it is the best way.
In WindsorContainer initialization, first I register viewmodel:
container.Register(Component.For<CentrosViewModel>().LifeStyle.Transient);
and later I register the View
container.Register(Component.For<CentrosAdminView>().LifeStyle.Transient.DependsOn(Property.ForKey("DataContext")
.Eq(ViewModelLocator.Centrosviewmodel)));
And definition of property ViewModelLocator.Centrosviewmodel is:
public static CentrosModel Centrosviewmodel
{
get
{
return App.container.Resolve<CentrosViewModel>();
}
}
End Edit
I'm trying to make an Wpf application using Castle Windsor and Mvvm Toolkit (galasoft) but I thing my problem will be the same with any MVVM toolkit.
With MVVM you must set the DataContext of the View to your ViewModel. Normally this is done by something like this in declaration of the view
DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}}
Resource Locator is defined in App.xaml like this:
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator" />
</Application.Resources>
If I establish StartupURI in App.xaml to my view all is right.
But if I leave StartupUri blank and I try to get an instance of my view through castle using following syntax:
container.Resolve<CentrosAdminView>().Show();
I get exception: "Cannot Find Resource with Name '{Locator}'
I supose that Initial DataContext is different when running directly than when running through Castle Windsor and this is the reason why it can't find resource.
My two questions are:
Is It necessary have a ViewModelLocator when using Castle Windsor? In
case of Yes: How can I setup DataContext of Views correctly with
Windsor? In case of No: How would be the right way?
I leave down my Castle Configuration. Any help would be really appreciated.
My Windsor configuration look like this:
<castle>
<properties>
<!-- SQL Server settings -->
<connectionString>Server=192.168.69.69;Database=GIOFACTMVVM;user id=sa;password=22336655</connectionString>
<nhibernateDriver>NHibernate.Driver.SqlClientDriver</nhibernateDriver>
<nhibernateDialect>NHibernate.Dialect.MsSql2005Dialect</nhibernateDialect>
</properties>
<facilities>
<facility id="nhibernatefacility"
type="Repository.Infrastructure.ContextualNHibernateFacility, Repository">
<factory id="sessionFactory1">
<settings>
<item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="connection.driver_class">#{nhibernateDriver}</item>
<item key="connection.connection_string">#{connectionString}</item>
<item key="dialect">#{nhibernateDialect}</item>
<item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
</settings>
<assemblies>
<assembly>Domain</assembly>
<assembly>ObservableCollections</assembly>
</assemblies>
</factory>
</facility>
</facilities>
</castle>
and by code:
public static IWindsorContainer Start()
{
var container = new WindsorContainer(new XmlInterpreter());
container.AddFacility<TransactionFacility>();
container.Register(
Component.For<HistoriasAdminView>().LifeStyle.Transient,
Component.For<HistoriasModel>().LifeStyle.Transient,
Component.For<CentrosModel>().LifeStyle.Transient,
Component.For<CentrosAdminView>().LifeStyle.Transient,
Component.For<MainViewModel>().LifeStyle.Transient,
Component.For<MainWindow>().LifeStyle.Transient,
Component.For<IRepository<Historias>>().ImplementedBy<Repository<Historias>>().LifeStyle.Transient,
Component.For<IRepository<Centros>>().ImplementedBy<Repository<Centros>>().LifeStyle.Transient,
Component.For<IUnitOfWork>().ImplementedBy<NHUnitOfWork>().LifeStyle.Transient
);
return container;
}
You are using the Service Locator pattern, where you register services, pass around a reference to the container, and explicitly call resolve all over you code. If you take a quick tour through the Castle Windsor wiki, they discourage this use of the container.
Generally, you should register all your types (via installers), resolve only one root object (maybe your main view, maybe some sort of startup/MVC controller style code), and let the rest be resolved by the container. The next time you will call the container will almost always be container.Dispose, when your application exits.
See the Windsor wiki page regarding the Three Calls Pattern.
If you find cases where you need to pull from the container during runtime to create specific instances (where you have to pass specific parameters to create that instance), use the Typed Factory Facility instead of directly resolving dependencies.
MVVM with Windsor:
In my first MVVM app I wrote with Windsor (just finished the first version), I simply registered my main view and view model without specifying the lifestyle. This defaulted them to be singletons.
The view took a view model instance as a required dependency (in the constructor), and used it to set the data context. I did this in code-behind because it was non-intrusive and painless.
// In my program I used interfaces for everything. You don't actually have to...
public interface IMainView
{
void Show();
}
public class MainView : Window, IMainView
{
public MainView(IMainViewModel viewModel)
{
Initialize();
this.DataContext = viewModel;
}
}
public interface IMainViewModel
{
int SomeProperty { get; set; }
ICommand ShowSubViewCommand { get; }
// ...
}
public class MainViewModel : IMainViewModel
{
public MainViewModel(SomeOtherSubComponent subComponent)
{
this.subComponent = subComponent;
// ...
}
// ...
}
Where I had sub-views that I wanted to create multiple instances of, I registered them with a transient life-cycle. Then I created a view factory and view model factory, and used them to get instances of the sub-view and sub-viewmodel from the parent view. I registered an event handler for the view's close event, and called a Release method on the factory classes.
public interface ISubView
{
void Show();
event Action OnDismissed;
}
public class SubView : Window, ISubView
{
public SubView(ISubViewModel viewModel)
{
Initialize();
this.DataContext = viewModel;
// Would do this in the view model,
// but it is a pain to get Window.Close to call an ICommand, ala MVVM
this.OnClose += (s, a) => RaiseDismissed();
}
public event Action OnDismissed;
private void RaiseDismissed()
{
if(OnDismissed != null)
OnDismissed();
}
}
public interface ISubViewModel
{
string SomeProperty { get; }
// ...
}
// Need to create instances on the fly, so using Typed Factory facility.
// The facility implements them, so we don't have to :)
public interface IViewFactory
{
ISubView GetSubView(ISubViewModel viewModel);
void Release(ISubView view);
}
public interface IViewModelFactory
{
ISubViewModel GetSubViewModel();
void Release(ISubViewModel viewModel);
}
// Editing the earlier class for an example...
public class MainViewModel : IMainViewModel
{
public MainViewModel(IViewFactory viewFactory, IViewModelFactory viewModelFactory)
{
this.viewFactory = viewFactory;
this.viewModelFactory = viewModelFactory;
// Todo: Wire up ShowSubViewCommand to call CreateSubView here in ctor
}
public ICommand ShowSubViewCommand { get; private set; }
private void CreateSubView()
{
var viewModel = viewModelFactory.GetSubViewModel();
var view = viewFactory.GetSubView(viewModel);
view.OnDismissed += () =>
{
viewModelFactory.Release(viewModel);
viewFactory.Release(view);
};
view.Show();
}
// Other code, private state, etc...
}
At the end of all this, the only calls to the container are:
void App_Startup()
{
this.container = new WindsorContainer();
container.Install(Configuration.FromAppConfig());
var mainView = container.Resolve<IMainView>();
mainView.Show();
}
public override OnExit()
{
container.Dispose();
}
The benefit to all this rigmarole is that it is independent of the container (and can be used without a container), it is obvious which dependencies each of my components take, and most of my code doesn't ever have to ask for its dependencies. The dependencies are just handed to each component as it needs it.
I have a WPF windows application that uses the ms ribbon control for the menu. In my infrastructure project I want to have a shared service that will be referenced in all modules. Each module will then use that service to define what menu items should be displayed for the module.
I read this Prism+MEF: delayed a service export from prism-module but can't get my other modules to recognize the service.
The service
namespace Infrastructure
{
[ModuleExport("InfModule", typeof(InfModule), InitializationMode = InitializationMode.WhenAvailable)]
[PartCreationPolicy(CreationPolicy.Shared)]
public class InfModule : IModule
{
[Export(typeof(IMenuService))]
public IMenuService MenuService { get; private set; }
public void Initialize()
{
MenuService = new MenuService();
MenuService.AddItem("test");
}
}
}
The module
namespace Classic
{
[ModuleExport("Classic", typeof(Classic), InitializationMode = InitializationMode.WhenAvailable)]
[ModuleDependency("InfModule")]
public class Classic : IModule
{
private IRegionManager _regionManager;
[Import(typeof(IMenuService))]
private IMenuService menuService { get; set; }
[ImportingConstructor]
public Classic(IRegionManager regionManager)
{
this._regionManager = regionManager;
// This shows as true
Debug.WriteLine(menuService == null);
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion("RibbonRegion", typeof(Views.RibbonTabMenu));
// This shows as true
Debug.WriteLine(menuService == null);
}
}
}
I would have expected one of the debug lines to output as false since its imported. Any idea's what I'm missing?
Property imports will never be set while running the constructor, since you can't set properties on an object until it's constructed.
The other problem is that in InfModule, you are setting the exported value too late. MEF only looks at the value for an export once, after that it caches the value and doesn't call the getter again. In this case it is getting the export before Initialize() is called. The logic to set the export needs to either run from the constructor or from code in the property getter.