WPF + Castle Windsor + MVVM: Locator-DataContext - wpf

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.

Related

Return a async list from SQL Server database and use in a datagrid WPF app

I'm currently facing a pretty strange behaviour I'm uncertain whether I do something wrong or not but here is the problem.
I try to return a list asynchronously from a repository and load it into a datagrid in a WPF application using Prism. When I return the list synchronously, it loads just fine, but as soon as I use it async the program run, but the interface will not display and no error is triggered.
If you need more info don't hesitate - thanks in advance.
Here is the code - shortened for simplicity:
The view
<UserControl>
<Grid>
<DataGrid ItemsSource="{Binding Products}" />
</Grid>
</UserControl>
The view model:
public class ProductViewModel : BindableBase, INavigationAware
{
private readonly IProductRepo _repo = new ProductRepo();
public ObservableCollection<Data.Models.Product> Products { get; set; }
public ProductViewModel()
{
Products = new ObservableCollection<Data.Models.Product>(this._repo.GetAll().Result);
}
}
The DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
}
The IProductRepo interface:
public interface IProductRepo
{
Task<List<Product>> GetAll();
}
The ProductRepo class that implements IProductRepo interface:
public class ProductRepo : IProductRepo
{
private readonly ApplicationDbContext _context;
public ProductRepo()
{
this._context = new ApplicationDbContext();
}
public async Task<List<Product>> GetAll()
{
return await this._context.Products.ToListAsync();
}
}
I tried to create an async private method inside the view model to load the data, then call the function in the constructor, but this doesn't work.
public void LoadData()
{
List<Data.Models.Product> ProductFromDB = this._repo.GetAll().Result;
foreach (var p in ProductFromDB)
{
Products.Add(p);
}
}
If you really believe you need to load all data on initialization, then a simple solution would be to load the data from the view.
The point is that calling Task.Result on an asynchronous method will very likely cause a deadlock.
Additionally, a long-running constructor can interfere with or even break the initialization routine of the controls (UI).
Furthermore, not awaiting an async method will lead to undefined behavior as the caller's context continues to execute while the the async method is still running and pending to return.
For example, the caller can now leave the method scope prematurely and the async method can never return.
It's more than best practice to ensure that constructors return fast and are light weight. They shouldn't allocate expensive resources and should not start background threads, which is generally a surprise for the caller.
A constructor is not meant to execute any kind of operations except to initialize its instance members to a reasonable default. Even advanced instance configuration should take place outside the constructor.
Also be aware that ToList or ToListAsync will force the query to be evaluated client-side. This can hurt significantly the application performance especially when the table is big. On the other hand, the database is highly optimized to execute queries efficiently, which makes server-side query evaluation favorable.
Since you are only fetching the complete table, it doesn't really matter in your scenario. But in case of advanced queries it can make the difference.
The following example shows how to execute the asynchronous initialization from the view. Depending on the exact scenario other solution can be applied.
The example assumes that the ProductViewModel is created and exposed by the main view model class. But the caller doesn't matter. Just let the control that creates the instance call the long-running initialization routine:
IInitializable.cs
Introducing as dedicated interface even allows to anonymously call all async and long-running initialization routines on application composition, for example in a dependency injection context.
This generally works in any composition context, for example when using composition to manage the hierarchy of classes.
In case of this example, the composition root of all view models would be the main view model class.
public interface IInitializable
{
Task<bool> InitializeAsync();
bool IsInitialized { get; }
}
ProductViewModel.cs
public class ProductViewModel : BindableBase, INavigationAware, IInitializable
{
private readonly IProductRepo _repo = new ProductRepo();
public ObservableCollection<Data.Models.Product> Products { get; }
public bool IsInitialized { get; private set; }
public ProductViewModel()
{
// Only initialize to a reasonable default.
// Expensive initialization is deferred.
this.Products = new ObservableCollection<Data.Models.Product>();
}
// Allow to defer long-running initialization routines.
// Such routines can be both, synchronous or asynchronous.
public async Task<bool> InitializeAsync()
{
if (this.IsInitialized)
{
return;
}
// You should rename 'GetAll' to 'GetAllAsync'
List<Product> allProducts = await this._repo.GetAll();
allProducts.ForEach(this.Products.Add);
this.IsInitialized = true;
return this.IsInitialized;
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : BindableBase, INavigationAware, IInitializable
{
public ProductViewModel ProductViewModel { get; }
public bool IsInitialized { get; private set; }
private IList<IInitializable> InitializableDependencies { get; }
public MainWindowViewModel()
{
// The container for polymorphic and anonymous iteration
this.InitializableDependencies = new List<IInitializable>();
// Create the composition tree.
// Because expensive initialization of dependencies is deferred,
// the type construction is cheap and guaranteed to return fast.
this.ProductViewModel = new ProductViewModel();
// Store potential IInitialize implementation for later initialization.
// This allows to use polymorphism to improve the code.
// Additionally, we avoid explicit knowledge whether the instantiated type implements this interface or not.
// This enhances extensibility significantly.
TryAddInitializable(this.ProductViewModel);
}
// Store IInitializable implementations anonymously for later iteration.
private bool TryAddInitializable(object instance)
{
if (instance is IInitializable initializableInstance)
{
this.InitializableDependencies.Add(initializableInstance);
return true;
}
return false;
}
// Allow to defer long-running initialization routines.
// Such routines can be both, synchronous or asynchronous.
public async Task<bool> InitializeAsync()
{
if (this.IsInitialized)
{
return;
}
// Cascade IInitializeAsync calls. In context of dependency injection
// this wouldn't be necessary as the IoC container would allow to filter
// all types that implement IInitializable to call InitializeAsync on each type explicitly.
// Because we only need to know the IINitializable interface, the calls are anonymous, allowing easy extension of the code.
foreach (IInitializable initializableInstance in this.InitializableDependencies)
{
// Trigger the cascade of InitializeAsync calls,
// given that the ProductViewModel is the composition root of other view model classes
// or classes that implement IInitializable.
bool isSuccesfullyInitialized = await initializableInstance.InitializeAsync();
}
this.IsInitialized = true;
return this.IsInitialized;
}
}
MainWindow.xaml.cs
The idea is to start the deferred initialization from the FrameworkElement.Loaded event of a reasonable element (in this case this is the MainWindow).
If ProductViewModel is created by another view model class, then you would have to let this view model implement IInitializable too and cascade the InitializeAsync method calls.
partial class MainWindow : Window
{
private MainWindowViewModel ViewModel { get; }
public MainWindow()
{
InitializeComponent();
this.ViewModel = new MainWindowViewModel();
this.DataContext = this.ViewModel;
this.Loaded += OnLoaded;
}
// Use the Loaded event to execute long-running or resource intensive routines
// outside the constructor.
// Define handler as 'async void' to await the async method calls.
private async void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.ViewModel is IInitializable initializableViewModel)
{
// Trigger the cascade of InitializeAsync calls,
// given that the MainWindowViewModel is the composition root of all view model classes
// or classes that implement IInitializable in general.
_ = await initializableViewModel.InitializeAsync();
}
}
}
MainWindow.xaml
<Window>
<ProductView DataGridItemsSource="{Binding ProductViewModel.Products}" />
</Window>

MVVMLight ViewModelLocator registering dataservice

This question might look naive, but I couldn't understand this code in the ViewModelLocator.cs file:
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
I see that we use a DataService to get data (from WCF service for example) and assigning it to the MainViewModel. But what if I'm registering more than one ViewModel? like this:
static ViewModelLocator()
{
....
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<Page2ViewModel>();
}
and let's say I have another DataService (DataService2 for example), but this one I'll use with the Page2ViewModel. how can I do that?
Also, if someone can help me (or even give me a link to read) about the above code. I have no clue what it means.
You are not assigning any IDataService to the MainViewModel here. You are registering a type mapping, so your container will be aware that it should return a DataService whenever an IDataService required.
This is related to dependency injection http://en.wikipedia.org/wiki/Dependency_injection
The DI container auto-wires the dependencies, so when you need a specific type, you can call
ServiceLocator.Current.GetInstance<IDataService>()
or
ServiceLocator.Current.GetInstance<MainViewModel>()
etc. If it can build it (so you registered your types), it will resolve the full dependency graph for you.
For example, if your MainViewModel has a constructor dependency on IDataService, and you are not in design mode, a DataService will be injected to the MainViewModel constructor. Don't be afraid from the buzzword injected, it is just a call to the MainViewModel constructor with the appropriate parameters :).
So, MainViewModel will not interference with Page2ViewModel here.
I made a simple sample for you to demonstrate what happens (I used Unity, http://unity.codeplex.com/ , but the syntax is almost the same):
class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IService, Service1>();
container.RegisterType<IService, Service2>("MySpecificService");
container.RegisterType<IRepository, Repository>();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
var viewModel = ServiceLocator.Current.GetInstance<MainViewModel>();
viewModel.Foo();
}
}
interface IService
{
}
interface IRepository
{
}
class Service1 : IService
{
public Service1(IRepository repository)
{
Console.WriteLine("Service1 created");
}
}
class Service2 : IService
{
public Service2()
{
Console.WriteLine("Service2 created");
}
}
class Repository : IRepository
{
public Repository()
{
Console.WriteLine("Repository created");
}
}
class MainViewModel
{
public MainViewModel(IService service)
{
Console.WriteLine("MainViewModel created");
}
public void Foo()
{
var specificService = ServiceLocator.Current.GetInstance<IService>("MySpecificService");
}
}
the output is:
Repository created
Service1 created
MainViewModel created
Service2 created
Because you need a MainViewModel (maybe in SimpleIoC you need to register MainViewModel too, in Unity, it can resolve concrete classes without mapping), the container trying to create one, but it realizes that MainViewModel needs an IService, and it finds the default one from the mapping, which is Service1, but it realizes that Service1 needs an IRepository, and it finds the default one, so it can pass a Repository to the Service1 constructor, then the Service1 instance to the MainViewModel constructor. All the dependencies resolved.
The Foo call is an example how you can register more than one type to the same interface. Dependency injection is a much more bigger topic, but auto-wiring is an important part of it.

Caliburn Micro and Castle Windsor Bootstrapper - Register View Model classes in bootstrapper

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.

MEF Importing ViewModel that needs data, to View for Silverlight

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.

Consume Service method in View-Model [WPF-MVVM]

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.

Resources