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");
}
}
Related
I am working on an asp.net mvc core 2.0 project and entityframework.
I am working with viewmodel concept.
This mean i do not pass entities instances directly to my view, i pass a viewmodel instance which only contains what the view needs.
in one case, i need to pass the entire entity fields plus other informations.
What i've done is a derivated my ViewModel class from my Entity Class. and i add the extra fields:
public MyViewModel: Person
{
// I will automaticly have Person fields in MyViewModel
public bool IsSelected {get;set;}
public String otherinformation {get;set;}
...
}
I am fed up with assigning each fields of my viewmodel from my entity instance.
myviewmodelinstance.field1 = myentity.field1;
myviewmodelinstance.field2 = myentity.field2;
myviewmodelinstance.field3 = myentity.field3;
myviewmodelinstance.IsSelected = false;
...
And i need to do the inverse operation in the postback.
Is there a way to "copy" or "clone" every fields, like this:
myentity.CopyTo(myviewmodelinstance);
myviewmodelinstance.IsSelected = false;
Thanks
You could use the AutoMapper to achieve it. It is the ideal way to perform it. Another option is that you can serialize and deseriliaze the object as json.
var myviewmodelinstance = JsonConvert.DeserializeObject<MyViewModel>(JsonConvert.SerializeObject(myentity));
Note : The code I provided required Json.Net
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.
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.
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.
So lets say i have an OrderModel and an OrderViewModel. I have the Supplier, Order Date, etc properties on both the ViewModel and the Model and they are linked up. Seen examples on this and seems straighforward enough, although somewhat duplicated in terms of writing setters/getters.
Now what do I do with the OrderDetails? In my model I would have a List.
Do I have an OrderDetailViewModel for the OrderDetail? If so then how does the OrderViewModel provide that? As an ObservableCollection? And if so how do you keep that in sync with the original List?
This is where I haven't seen a decent example. If there is one out there someone could point me to, I'd appreciate it. I liek the concept of the MVVM but I am starting to thing its a hell of a lot of overhead. Why not just have the ViewModel handle the model part as well. In day to day LOB apps is there really that much difference between the two to warrant all the code that true MVVM seems to required?
It looks like this is what you need: http://jonas.follesoe.no/SpeakingAtMSDNLiveNextMonth.aspx
A translation on google gives this as the abstract for the talk:
Silverlight 2 was released this autumn, and lays a good foundation for developers who want to create rich Internet applications (RIA) based on. NET. In this session we in Depth in Silverlight 2 that development and the benefits of choosing Silverlight 2 as a platform for data-centric business applications. The session will cover among other things, data access via secured WCF services, how to structure the code using the Model-View-View Model pattern (MVVM), how to write code, designers can work with, and easy-Blend tips for developers. The session will be built around a dive log application where the code will be available after the presentation.
However in the mean time Jonas has already talked about MVVM here:
http://jonas.follesoe.no/YouCardRevisitedImplementingTheViewModelPattern.aspx
You can use something like this to keep your ObservableCollections synchronised between the model and view model:
/// <summary>
/// Keeps one collection synchronised with another.
/// </summary>
/// <typeparam name="Source">The type of the source items.</typeparam>
/// <typeparam name="Destination">The type of the destination items.</typeparam>
public class CollectionSync<Source, Destination>
{
private readonly Func<Source, Destination> _destItemFactory;
private readonly Action<Destination> _destItemRemover;
private readonly IList<Destination> _destList;
private readonly IList<Source> _sourceList;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionSync<Source, Destination>"/> class.
/// </summary>
/// <param name="sourceList">The source list.</param>
/// <param name="destList">The destination list.</param>
/// <param name="destItemFactory">Factory method which creates a Destination for a given Source.</param>
/// <param name="destItemRemover">Method called when a Destination is removed.</param>
public CollectionSync(IList<Source> sourceList,
IList<Destination> destList,
Func<Source, Destination> destItemFactory,
Action<Destination> destItemRemover)
{
_destItemFactory = destItemFactory;
_destItemRemover = destItemRemover;
_sourceList = sourceList;
_destList = destList;
((INotifyCollectionChanged) _sourceList).CollectionChanged += SourceCollection_CollectionChanged;
PopulateWithAllItems();
}
private void PopulateWithAllItems()
{
foreach (Source sourceItem in _sourceList)
_destList.Add(_destItemFactory(sourceItem));
}
private void SourceCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
OnItemsAdded(args.NewStartingIndex, args.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
OnItemsRemoved(args.OldStartingIndex, args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
OnItemsReset();
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
throw new NotImplementedException();
}
}
private void OnItemsReset()
{
_destList.Clear();
PopulateWithAllItems();
}
private void OnItemsRemoved(int index, ICollection items)
{
int itemCount = items.Count;
for (int i = 0; i < itemCount; i++)
{
Destination removed = _destList[index];
_destList.RemoveAt(index);
if (_destItemRemover != null)
_destItemRemover(removed);
}
}
private void OnItemsAdded(int index, IList items)
{
int itemIndex = index;
foreach (Source item in items)
{
// Add to Items collection
_destList.Insert(itemIndex, _destItemFactory(item));
itemIndex++;
}
}
}
Taking your Order/OrderDetails example, in your Order view model you would hook up the two ObservableCollections like this:
_modelToViewModelSync = new CollectionSync<IOrderDetail, OrderDetailViewModel>(
orderDetailModels, // the list of your order details models
OrderDetails, // the list of order details view models exposed by the Order view model
x => new OrderDetailViewModel(x), // factory method to create a view model
null); // do something here if you care when your view models are removed
When it comes to the question "Do I need another view model", my answer is this: If all your view is doing is showing the model data, there is no harm in binding directly to the order directly. Creating a ViewModel for this would be very much redundant. The time when the ViewModel needs to be created is when you have logic or state in the "order detail" screen that needs to be represented. Instead of adding that to the model, you create a ViewModel at that time.
As far as keeping those items in sync, Similar to GraemeF, I have created a Binder class that is uses reflection to bind two values together. It keeps my model and viewmodel properties in sync, and it can be used to keep other things in sync, like this particular collection. There is some overhead in creating a binder like this, but once it is done, you can specify data correlations in a functional way, which is really nice.