how to parametrize an import in a View? - mef

I am looking for some help and I hope that some good soul out there will be able to give me a hint :)
I am building a new application by using MVVM Light. In this application, when a View is created, it instantiates the corresponding ViewModel by using the MEF import.
Here is some code:
public partial class ContractEditorView : Window
{
public ContractEditorView ()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
}
And here is the export for the ViewModel:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.ContractEditorViewModel)]
public class ContractEditorViewModel: ViewModelBase
{
public ContractEditorViewModel()
{
_contract = new Models.Contract();
}
}
Now, this works if I want to open a new window in order to create a new contract... or in other words, it is perfect if I don't need to pass the ID of an existing contract.
However let's suppose I want to use the same View in order to edit an existing contract. In this case I would add a new constructor to the same View, which accepts either a model ID or a model object.
"Unfortunately" the ViewModel is created always in the same way:
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
As far as I know, this invokes the standard/no-parameters constructor of the corresponding ViewModel at composition-time.
So what I would like to know is how to differentiate this behavior? How can I call a specific constructor during composition time? Or how can I pass some parameters during the Import?
I really apologize if this question sounds silly, but I have only recently started to use MEF!
Thanks in advance,
Cheers,
Gianluca.

You CAN do this. Check out the Messenger implementation in MVVM-Light. You can pass a NotificationMessage(Of Integer) to send the right ID to the view model. The view model has to register for that type of message, and load it when a message is sent.
MEF Imports by default only have a parameterless constructor.

Related

freshmvvm access PageModel from Page code behind

Im using xamarin forms with freshmvvm framework.
I would like to know how I can skip using xaml, and just access binding data from code behind in c#.
Are there any code samples that could help?
Although this goes against the principles of MVVM there is of course a way to do it.
Without a MVVM framework you would just create a ViewModel by hand and set the BindingContext (documentation) yourself. The 'only' thing (in regard to this) a MVVM framework does for you is set that binding up automatically so you're not bothered with writing the same code over and over again.
So, imagine you have this ViewModel, note I user PageModel to match the FreshMvvm naming:
// SamplePageModel.cs
public class SamplePageModel
{
public string Foo { get; set; } = "Bar";
}
Now in my Page, I set the BindingContext like this:
// SamplePage.cs
// ... Skipped code, just constructor here:
public SamplePage()
{
InitializeComponent();
BindingContext = new SamplePageModel();
}
Now you can bind to any property of SamplePageModel.
FreshMvvm does this part automagically.
If, for whatever reason, you would like to access the ViewModel/PageModel directly, just do the reverse. Somewhere in your Page or code-behind you can do:
// ... Some code here
var pageModel = BindingContext as SamplePageModel;
// ... More code here
Now if pageModel isn't null there you have your data-bound and filled PageModel!
I found Gerald's answer helpful, but I found that you need to override this event in your page vs doing the as in the constructor:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var pageModel = BindingContext as YourFreshMVVMPageModel;
// Modify the page based on the pageModel
}
The PageModel construction seems to take place after the page Constructor, and this Event seems to fire at the right time and still make the page do what you want.

Finding a View with IViewFor<InterfaceClass> from a class VM that implements InterfaceClass?

I have the following class which is my view model (this is very simple right now, but it will contain a chunk more logic eventually):
public class IndicoTalk : ITalk
{
private Talk _talk;
public IndicoTalk(Talk t)
{
this._talk = t;
}
public string Title
{
get { return _talk.Title; }
}
}
Now, I have a reactive ui view for this guy:
public sealed partial class TalkView : UserControl, IViewFor<ITalk>
{
public TalkView()
{
this.InitializeComponent();
this.Bind(ViewModel, x => x.Title, y => y.TalkTitle.Text);
}
Note that the IViewFor is for ITalk, not IndicoTalk. This is because I can have other types of talk, and they will all fit into the same view.
And I register this ViewModel in my App start up:
Locator.CurrentMutable.Register(() => new TalkView(),
typeof(IViewFor<IWalker.DataModel.Inidco.IndicoMeetingRef.IndicoTalk>));
Finally, in another viewmodel I have a ReactiveList which contains a bunch of these IndicoTalks's. Of course, when I bind this to a ListBox, ReactiveUI fails to find the view. If I switch to IViewFor then everything works just fine.
What is the proper way to gently redirect the view resolution in this case?
A half-way solution: leave all code above the same, but put in the IViewFor ITalk instead of IndicoTalk. This works, but means I will have to register with Splat (the CurrentMutable call above) every ViewModel that inherrits from ITalk. I'd love to avoid that if possible!
Many thanks!
So, why not just do:
Locator.CurrentMutable.Register(() => new TalkView(), typeof(IViewFor<ITalk>));

wicket :how to combine CompoundPropertyModel and LoadableDetachableModel

I want to achieve two goals:
I want my model to be loaded every time from the DB when it's in a life-cycle (for every request there will be just one request to the DB)
I want my model to be attached dynamically to the page and that wicket will do all this oreable binding for me
In order to achieve these two goals I came to a conclusion that I need to use both CompoundPropertyModel and LoadableDetachableModel.
Does anyone know if this is a good approach?
Should I do new CompoundPropertyModel(myLoadableDetachableModel)?
Yes, you are right, it is possible to use
new CompoundPropertyModel<T>(new LoadableDetachableModel<T> { ... })
or use static creation (it does the same):
CompoundPropertyModel.of(new LoadableDetachableModel<T> { ... })
that has both features of compound model and lazy detachable model. Also detaching works correctly, when it CompoudPropertyModel is detached it also proxies detaching to inner model that is used as the model object in this case.
I use it in many cases and it works fine.
EXPLANATION:
See how looks CompoundPropertyModel class (I'm speaking about Wicket 1.6 right now):
public class CompoundPropertyModel<T> extends ChainingModel<T>
This mean, CompoundPropertyModel adds the property expression behavior to the ChainingModel.
ChainingModel has the following field 'target' and the constructor to set it.
private Object target;
public ChainingModel(final Object modelObject)
{
...
target = modelObject;
}
This take the 'target' reference to tho object or model.
When you call getObject() it checks the target and proxies the functionality if the target is a subclass of IModel:
public T getObject()
{
if (target instanceof IModel)
{
return ((IModel<T>)target).getObject();
}
return (T)target;
}
The similar functionality is implemented for setObject(T), that also sets the target or proxies it if the target is a subclass of IModel
public void setObject(T object)
{
if (target instanceof IModel)
{
((IModel<T>)target).setObject(object);
}
else
{
target = object;
}
}
The same way is used to detach object, however it check if the target (model object) is detachable, in other words if the target is a subclass if IDetachable, that any of IModel really is.
public void detach()
{
// Detach nested object if it's a detachable
if (target instanceof IDetachable)
{
((IDetachable)target).detach();
}
}

MEF and IObservables

I have a singleton IObservable that returns the results of a Linq query. I have another class that listens to the IObservable to structure a message. That class is Exported through MEF, and I can import it and get asynchronous results from the Linq query.
My problem is that after initial composition takes place, I don't get any renotification on changes when the data supplied to the Linq query changes. I implemented INotifyPropertyChanged on the singleton, thinking it word make the exported class requery for a new IObservable, but this doesn't happen.
Maybe I'm not understanding something about the lifetime of MEF containers, or about property notification. I'd appreciate any help.
Below are the singleton and the exported class. I've left out some pieces of code that can be inferred, like the PropertyChanged event handlers and such. Suffice to say, that does work when the underlying Session data changes. The singleton raises a change event for UsersInCurrentSystem, but there is never any request for a new IObservable from the UsersInCurrentSystem property.
public class SingletonObserver: INotifyPropertyChanged
{
private static readonly SingletonObserver _instance = new SingletonObserver();
static SingletonObserver() { }
private SingletonObserver()
{
Session.ObserveProperty(xx => xx.CurrentSystem, true)
.Subscribe(x =>
{
this.RaisePropertyChanged(() => this.UsersInCurrentSystem);
});
}
public static SingletonObserverInstance { get { return _instance; } }
public IObservable<User> UsersInCurrentSystem
{
get
{
var x = from user in Session.CurrentSystem.Users
select user;
return x.ToObservable();
}
}
}
[Export]
public class UserStatus : INotifyPropertyChanged
{
private string _data = string.Empty;
public UserStatus
{
SingletonObserver.Instance.UsersInCurrentSystem.Subscribe(sender =>
{
//set _data according to information in sender
//raise PropertyChanged for Data
}
}
public string Data
{
get { return _data; } }
}
}
My problem is that after initial composition takes place, I don't get any renotification on changes when the data supplied to the Linq query changes.
By default MEF will only compose parts once. When a part has been composed, the same instance will be supplied to all imports. The part will not be recreated unless you explicitly do so.
In your case, if the data of a part change, even if it implements INotifyPropertyChanged, MEF will not create a new one, and you don't need to anyway.
I implemented INotifyPropertyChanged on the singleton, thinking it word make the exported class requery for a new IObservable
No.
Maybe I'm not understanding something about the lifetime of MEF containers, or about property notification.
Property notification allows you to react to a change in the property and has no direct effect on MEF. As for the container's lifetime, it will remain active until it is disposed. While it is still active, the container will keep references to it's compose parts. It's actually a little more complex than that, as parts can have different CreationPolicy that affects how MEF holds the part, I refer you to the following page: Parts Lifetime for more information.
MEF does allow for something called Recomposition. You can set it likewise:
[Import(AllowRecomposition=true)]
What this does tough is allow MEF to recompose parts when new parts are available or existing parts aren't available anymore. From what I understand it isn't what you are referring to in your question.

Adding views does not call MEF Import statements

I have a view controlled by a view model (using MEF) that allows a user to selected items from a drop down list. Each item that the user selects populates a tab control that is defined as a region. The view model instantiates a view, assigns it a view model, then adds it to the region:
ProjectDetailView view = new ProjectDetailView();
ProjectDetailViewModel viewModel = new ProjectDetailViewModel();
viewModel.CurrentProject = project;
view.DataContext = viewModel;
RegionManager.Regions["SelectedItemsRegion"].Add(view);
This all works fine from the UI perspective. The project detail view model, however, has [Import] statements on it to receive an EventAggregator for publishing events.
[Import]
public IEventAggregator EventAggregator { get; set; }
Because I'm only adding views to a region and not doing a request navigate to a specific URI, the composition never occurs (or at least it doesn't appear to) so EventAggregator is always null. How do I get these dynamically added views to go through the process of importing the requested classes? Is there a compose method I can call on a specific view so things get resolved?
I would suggest that you create a factory class to instantiate EventAggregator, like so:
public EventAggregatorFactory
{
[Export(typeof(IEventAggregator))]
public IEventAggregator Instance
{
get
{
return new EventAggregator();
}
}
}
Obviously, move the Export declaration into the factory class. This should allow proper instantiation of the Import of EventAggregator when the viewmodel is invoked.