Adding views does not call MEF Import statements - mvvm

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.

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.

Proper MVVM way to pass info from parent to child view models

I am starting a new MVVM project with WPF, both of which I am unfamiliar with and wanted to make sure that I was using a proper MVVM approach.
I have the following:
I have a master user control that contains its own ViewModel and a TabControl
Each Tab contains a separate user control with their own ViewModels
When the MasterViewModel is created, one of its property gets set (let's call it MasterId)
I need to propagate that MasterId property change from the master view model to the child view models
I am wondering what is the simplest / best / proper MVVM approach for implementing this simple scenario?
When it comes to TabControls and the like I tend to have the child view models within a collection of the MasterViewModel
public ObservableCollection<ViewModelBase> Tabs { get; private set; }
When your ID gets set or your child view models get created then you can set properties on them
public int MasterId
{
get { return _masterId; }
set { _masterId = value;
foreach(var vm in Tabs)
vm.MasterId = value;
NotifyPropertyChanged("MasterId");
}
}

MVVM and Repository Question

Let's say that I have two views in my app, MemberListView and MemberEditView. They are associated with their perspective viewModels, MemberListViewModel and MemberEditViewModel. The models speak to a repository class, MemberRepository, which has the CRUD methods for the member class.
In the MemberEditView form, I have several dropdowns that display thinkgs like Status (Active/Inactive/Pending), the members trade code etc. They are ObservableCollection objects in my viewModel and are bound to ComboBoxes on the view. Should the MemberRepository handle the gets for retrieving the lists of each to be displayed?
What if on the MemberEditView I have a grid that displays all the jobs that the member has had over the years. If the user doubleclicks one of the jobs, it calls a JobHistoryEditView to display the job Information and it has a JobHistoryViewModel. Should the MemberRepository take care of the JobHistory CRUD methods or should I have a separate JobHistory Repository?
Most MVVM applications would have this architecture:
View -> ViewModel -> Model -> Repository
I have recently been espousing a variant:
View -> ViewModel <- Presenter -> Model -> Repository
(Where A -> B means "A knows about B", but B doesn't know about A.)
Notice that in both cases, the only thing that knows about the repository is the Model, not the ViewModel. Your model isn't just the domain entities, it also has to house the business logic. Obviously one of the user stories your business logic has to support is something I'll call a MemberEditTask:
public class MemberEditTask
{
private readonly Member _member;
public MemberEditTask(Member member, IRepository repository)
{
this._member = member;
this.StatusChoices = repository.GetPossibleMemberStatuses(member);
}
public ReadOnlyCollection<MemberStatus> StatusChoices { get; private set; }
public MemberStatus Status
{
get { return this._member.Status; }
set
{
if(!this.StatusChoices.Contains(value))
{
throw new ArgumentOutOfRangeException();
}
this._member.Status = value;
}
}
}
All of this logic belongs in your Model because the list of possible choices (and validating that one of those was actually chosen) is defined by business logic. You could also imagine some other thing consuming the MemberEditTask, like an automated process running on the server that edits a member in response to a file uploaded on an FTP server, or a background process (setting the status to Inactive after a certain amount of time). All of those things need to execute the same business rules, so it all has to be common (not in the ViewModel).
So given that class, the ViewModel class looks like this:
public class MemberEditViewModel : ViewModelBase
{
private readonly MemberEditTask _task;
public MemberEditViewModel(MemberEditTask task)
{
this._task = task;
}
public IEnumerable<MemberStatus> StatusChoices
{ get { return this._task.StatusChoices; }
public MemberStatus Status
{
get { return this._task.Status; }
set
{
this._task.Status = value;
NotifyAllPropertiesChanged();
}
}
}
In this case, as a very simple convenience, just believe that NotifyAllPropertiesChanged is a protected method of ViewModelBase that uses reflection to raise a PropertyChanged event on all public properties of the ViewModel. :) That's overkill of course, but it drives at a more important point...
This is almost a silly example because in this case, MemberEditViewModel is unnecessary. If the View is the only one setting Status then there's really no need to raise the property changed event either! Of course in the real world, you will have more properties and there will be interactions. The reason for the ViewModel to exist is to notify consumers when its view-related properties change, which is something the Model doesn't do (and shouldn't in my opinion). (The ViewModel also has extra View-specific logic to support animations, etc.)
So back to your question... whether or not the MemberRepository is responsible for executing the gets of the statuses is irrelevant from the point of view of the ViewModel because the repository is a service used by the Model. The Model is a service used by the ViewModel. Make your Model of the task/workflow/process/whatever expose the list of status options.
Sorry if that was long-winded.

MVVM Toolkit light Messenger chained Messages

this might be complicated to explain but I give it a try...
I would like to use the Messenger to navigate to a new Page and also create a new Object (or pass one). How is this possible or am I on the wrong path?
Basically:
Click on "add new person" button which should bring up the PersonView and also should hold a new instance of a person object.
Click on "add person" button which should bring up the same PersonView page and should receives the object which is selected.
Message 1 = open Uri / Message 2 send exisiting or new object.
So far I have MainPageViewModel which sends
Messenger.Default.Send<Uri>(...)...
And MainPage.cs which Registers Messenger.Default.Register<Uri>(...)and executes
Frame.Navigate(...targetUri)....
I tryed to Send a message to the PersonViewModel right after Frame.Navigate... but this runs out of sync... so the page was not loaded to receive the PersonMessage,...
So any tips, tricks, licks, approaches would be greate...
Thanks...
Hope this helps, basicaly it is a simple Singleton class that gets the navigation frame the page that contains, after that you are able to use it in your viewmodel and navigate, and get notified when the page changes, so with this you control in a better way the navigation
and send messages, and get aware about your page status.
public class NavigationFrameController {
private static NavigationFrameController _instance;
private MainPage _root;
public Frame NavFrame { get; set;}
private static object keyLock = new Object();
NavigationFrameController() {
_root = (MainPage)Application.Current.RootVisual;
NavFrame = _root.ContentFrame;
NavFrame.Navigated += new NavigatedEventHandler(ContentFrame_Navigated);
NavFrame.NavigationFailed += new NavigationFailedEventHandler(ContentFrame_NavigationFailed);
}
public static NavigationFrameController Instance {
get {
if (_instance == null)
lock (keyLock) {
_instance = new NavigationFrameController();
}
return _instance;
}
}
public void NavigateTo(Uri uri) {
NavFrame.Source = uri;
}
private void ContentFrame_Navigated(object sender, NavigationEventArgs e) {
//send your message
// get attached to this event and get notified
}
private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) {
}
I had the same problem - essentially where you receive the message to open a new window also create the viewmodel and add it to the view as datacontext. When you instantiate your viewmodel pass in the existing object or null etc... then in your viewmodel you can handle whether its a new or existing object.
If you are using dependency injection then call a resolve from the codebehind where you process the 'add person' messsage etc..
I think what you should do is keep the first message for navigation, and add to it the information about the object (person) you want to send. you can add a parameter to the query string, say "add=true" and then you can create the object normally in the view model, or the ID of the object to edit, and in this case, the viewmodel can retrieve the object itself and edit it.
To achieve this, in the code behind of the PersonView (associated with the PersonViewModel) has to send a message upon navigation (OnNavigatedTo) to its ViewModel containing the received info from the query string.

how to parametrize an import in a View?

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.