I'm learning prism V4 using MEF to load my modules. Loading modules does work, but in one module I want to load a View/ViewModel (MVVM) and don't really know how I get MEF to resolve all this stuff for me.
First: how do I need to mark the ViewModel (I follow the StockTraderRI example) so it is not loaded on startup but instead can be loaded during runtime into a region?
Second: how do I load the ViewModel using MEF so it gets connected to the corresponding interfaces?
MEF does this very nicely for things on startup which are marked as [Export], but I got no idea how to achieve this during runtime.
You can use what is known as a Lazy Export so that the interface is not resolved until you explicitly use it.
If you need to create multiple instances, MEF doesn't support this particularly well. You can either do your own discovery and instantiation, or you can define the Export like this:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(ISomething)]
public class Something : ISomething { }
The downside is that now wherever you need to create the instance, you need to have a reference to the actual Container instance. Then you can do:
var something = _container.GetExportedObject<ISomething>();
EDIT: Okay, I think I understand better what you're after. Here is how I've typically resolved this issue:
I implement my View objects as UserControl instances and don't set a DataContext anywhere in their code or XAML.
I create a DataTemplate that binds from the Type of the ViewModel to the UserControl.
On my MainViewModel (or whatever corresponds to the View hosting the regions), I expose a general RegionX Object (possibly typed to an interface if all of my ViewModels will share some common functionality, but Object works fine).
I create a ContentPresenter with Content bound to the RegionX property.
Now my MainViewModel can import different ViewModel instances corresponding to the types of ViewModels that might be hosted by the RegionX. When I want to switch the 'active' View in the region, I simply set RegionX to the corresponding ViewModel.
Related
I am developing a modular WPF application with Prism in .Net Core 5.0 (using MVVM, DryIoc) and I would like to have a module that is not a WPF module, i.e., a module with functionality that can be used by any other module. I don't want any project reference, because I want to keep the loosely coupled idea of the modules.
My first question is: is it conceptually correct? Or is it mandatory that a module has a screen? I guess it should be ok.
The second and more important (for me) is, what would be the best way to create the instance?
This is the project (I know I should review the names in this project):
HotfixSearcher is the main class, the one I need to get instantiated. In this class, for example, I subscribe to some events.
And this is the class that implements the IModule interface (the module class):
namespace SearchHotfix.Library
{
public class HotfixSearcherModule : IModule
{
public HotfixSearcherModule()
{
}
public void OnInitialized(IContainerProvider containerProvider)
{
//Create Searcher instance
var searcher = containerProvider.Resolve<IHotfixSearcher>();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IHotfixSearcher, HotfixSearcher>();
}
}
}
That is the only way I found to get the class instantiated, but I am not a hundred per cent comfortable with creating an instance that is not used, I think it does not make much sense.
For modules that have screens, the instances get created when navigating to them using the RequestNavigate method:
_regionManager.RequestNavigate(RegionNames.ContentRegion, "ContentView");
But since this is only a library with no screens, I can't find any other way to get this instantiated.
According to Prism documentation, subscribing to an event shoud be enough but I tried doing that from within my main class HotfixSearcher but it does not work (breakpoints on constructor or on the event handler of the event to which I subscribe are never hit).
When I do this way, instead, the instance is created, I hit the constructor breakpoint, and obviously the instance is subscribed to the event since it is done in the constructor.
To sum up, is there a way to get rid of that var searcher = containerProvider.Resolve<IHotfixSearcher>(); and a better way to achieve this?
Thanks in advance!
Or is it mandatory that a module has a screen?
No, of course not, modules have nothing to do with views or view models. They are just a set of registrations with the container.
what would be the best way to create the instance?
Let the container do the work. Normally, you have (at least) one assembly that only contains public interfaces (and the associated enums), but no modules. You reference that from the module and register the module's implementations of the relevant interfaces withing the module's Initialize method. Some other module (or the main app) can then have classes that get the interfaces as constructor parameters, and the container will resolve (i.e. create) the concrete types registered in the module, although they are internal or even private and completely unknown outside the module.
This is as loose a coupling as it gets if you don't want to sacrifice strong typing.
is there a way to get rid of that var searcher = containerProvider.Resolve<IHotfixSearcher>(); and a better way to achieve this?
You can skip the var searcher = part :-) But if the HotfixSearcher is never injected anywhere, it won't be created unless you do it yourself. OnInitialized is the perfect spot for this, because it runs after all modules had their chance to RegisterTypes so all dependencies should be registered.
If HotfixSearcher is not meant to be injected, you can also drop IHotfixSearcher and resolve HotfixSearcher directly:
public void OnInitialized(IContainerProvider containerProvider)
{
containerProvider.Resolve<HotfixSearcher>();
}
I am not a hundred per cent comfortable with creating an instance that is not used, I think it does not make much sense.
It is used, I suppose, although not through calling one of its methods. It's used by sending it an event. That's just fine. Think of it like Task.Run - it's fine for the task to exist in seeming isolation, too.
I've edited the question to make what I want to obtain clearer.
Here's the original question:
I'm working on a class that inherits from Control which will be used in my View and includes some Dependency Properties.
One of these DPs is an IEnumerable(Of RfidTag) and will be bound to an ObservableCollection(Of RfidTag) inside the VM.
The class RfidTag is defined as public class in the same file where the VM's class resides.
The questions are:
1. is it a good practice to expose a VM-related class to a Control class?
2. is the VM source file a good place for the RfidTag class?
UPDATE 1
In my application logic (I think I could say in my Model) there is an event published throught an Eventaggregator. The event's payload is a List of ModelRfidTag (that is the model class).
My VM subscribes to this event and I made the RfidTag class to wrap my Model class and provides some additional properties related only to the VM.
When the event handler inside the VM is executed, it makes an ObservableCollection(Of RfidTag) bindable from the view.
Then in the View I've a bounch of my control instances like that
<c:RfidTagPresenter
TagPosition="1"
Collection="{Binding RfidTagList, Mode=OneWay}" />
Then in my RfidTagPresenter (the class that inherits from Control) I've a DP of type RfidTag (called RfidTagResult) that returns the object in the OC which has the Position property (property available inside the RfidTag class) equal to the value set by the TagPosition DP.
In this way, the ControlTemplate of the RfidTagPresenter can bind its elements to the desired object.
This is the simplification of what I want to make. In the actual application there are some other DPs on which the RfidTagResult selection is performed
UPDATE 2
After a bit of research, seem that I can solve one problem with an indexed property that return (in the get method) the object from the collection I want to bind.
However a problem still exist: My control need to have a DP of type of RfidTag so that the relative ControlTemplate can bind to the property declared in the RfidTag class.
So: Is it possible (read: a good practice) to have a DP of a type that is a VM related class?
In other words: Can a custom control know about the class type used by the VM?
I will try to go all over your question (if I miss something let me know) but first you should explain the purpose of binding a Collection in a control as a DP.
is it a good practice to expose a VM-related class to a Control class?
RfidTag, I suppose, is a Model. What you are really doing here is binding a Model in your control which go against the MVVM pattern. You should think about the next question... Do you really need all your RfidTag to be shown in the View ? If you need to show a name, an ID... you could just create an IEnumerable<string> as DP (which is correct) and then in your VM instead of an ObservableCollection<RfidTag> you would have an ObservableCollection<string>.
Some simple theory. In MVVM, VM adapts the Model to the View. So your VM should have everything that will be shown in your View.
is the VM source file a good place for the RfidTag class?
RfidTag is a Model so there's no better place for it :)
I'm trying to port the core of an application across to Portable Class Libraries and don't appear to have binding support.
I'm trying to bind a property on my ViewModel to my Model, which consists of an ObservableDictionary (INotifyPropertyChanged, INotifyCollectionChanged, IDictionary<string, string>). I do this usually (with WP7) by using the following code when initialising the view model:
SetBinding(MyProperty, new Binding(string.Format("MyDictionary[{0}]", "thekey")) { Source = MyModel });
How would I approach this when using Portable Class Libraries, where it seems like the Binding class is unavailable?
I've implemented this by having the base class for the ViewModels wire up to the PropertyChanged event of the ViewModel and the NotifyCollectionChanged event of the ObservableDictionary. I then have a method (with a set of overloads for additionally supplying an implementation of an IPclValueConverter which is a copy of the IValueConverter) which adds to a collection of PclBinding objects which is a set of PropertyInfo, dictionary key, IPclValueConverter and a converter parameter.
Within the PropertyChanged/NotifyCollectionChanged I check to see if the binding should be updated, and if so perform the update passing the value through a converter if present.
This means that from my original example, I now write the following inside my ViewModel which creates the binding as required:
SetBinding(() => MyProperty, "theKey");
If anyone is actually interested in this code I'd be happy to post it up. :)
I would like to make a global Zend_Log object that I can reach from my Controllers and my Models.
What should I add to my Bootstrap? (My bootstrap extends Zend_Application_Bootstrap)
How then can I reach the logger object from my controller actions and from my model?
As you do with any other class - assign it to the Zend_Registy. I'd suggest setting like this:
Zend_Registry::set('Zend_Log',$logInstance);
This is a common way, which is used also for translate (set translate instance to 'Zend_Translate' and classes like forms and validators will find it automatically).
You can use Zend_Registry::get('Zend_Log')->log(...) to log anywhere you want. It's not very good from the point of architecture (you should not use normally), but for log - which can appear practically anywhere in the app from view helpers to controllers and models it's a good thing.
I'm just starting to dig into the MVVM pattern for WPF but because I'm not a professional developer, I sometimes feel a little lost.
The main problem I have is deciding who should create whom: Let's say I have a database storing dogs and cats. I would then have a DatabaseManager which communicates with the database (Unit of Work!?), a DogRepository / CatRepository and the Dogs / Cats objects.
On the other side I have my MainWindow with which the user can open/close the database and which contains a Listbox for cats and one for dogs, so I need a MainWindowViewModel, CatsViewModel and DogsViewModel.
But how do I create these objects? In my application I create the MainWindow obviously. And then? Do I create a MainWindowViewModel and pass it to the MainWindow or does the MainWindow create its own MainWindowViewModel? How is the DatabaseManager created? By the MainWindowViewModel?
What about the CatsViewModel and the corresponding CatRepository? Does the MainWindowViewModel create the CatsViewModel which in turn creates a CatRepository? I'm really confused.
You are confused because all of these options are valid. If you aren't using dependency injection and the ViewModels have all of the information they need on their own, there's no reason not to have the view create the viewmodel (usually done by creating one via a StaticResource for the View:
<Window.Resources>
<local:CatViewModel x:Key="catVM" />
</Window.Resources>
<Grid DataContext="{StaticResource catVM}">
...
</Grid>
If you are using dependency injection, it's still perfectly valid to declare your VM as a dependency of your view in its constructor:
public CatView(CatViewModel vm) : this()
{
this.DataContext = vm;
}
The last option is the concept of a third party that creates both the view and the viewmodel (however is appropriate) and marries them from outside of both. Usually this is called a presenter.
In any case, all of these are valid options. You should pick the one that is most appropriate for you. I'm sure someone will come along and claim blasphemy, but it's really up to your needs and your project.
The View-Model-ViewModel (MVVM) pattern doesn't define who is responsible to create the different classes. Therefore, you find a lot different approaches in the MVVM community.
I like to use an Application Controller or use-case controllers that handle the work-flow of the application and so they are responsible to create the ViewModel and Repository classes.
A concrete example how this works is shown in the ViewModel sample of the project:
WPF Application Framework (WAF)
http://waf.codeplex.com