I think I'm on my way to finally grokking MVVM. I was just looking at a situation I found myself in and I'm wondering if I should combine two of my layers. I currently have a structure like this:
Domain models (in a separate .net project)
WPF-side "models" which wrap domain models and track dirty state
WPF ViewModels
Views
The question is whether I should combine 2 and 3. Right now #2 is the layer that gets passed around by my mediator to facilitate all the viewmodels knowing what document is currently opened. But I think my code for 2 and 3 is too similar and is unnecessary duplication at this point.
Example:
// in layer 2, class ProjectDocument
// Project is an instance of the domain model
public string Name
{
get { return Project.Name; }
set
{
if (Project.Name == value) return;
Project.Name = value;
Dirty = true;
}
}
// in layer 3, class ProjectSettingsViewModel
// _project is a ProjectDocument
public string Name
{
get { return _project.Name; }
set
{
_project.Name = value;
OnPropertyChanged("Name");
}
}
Seeing that struck me as unnecessary. So should I combine them? On one hand, it would remove a lot of redundant code and I'd have fewer places to update when I change things. On the other, I might be giving one class too much responsibility, and also it seems inappropriate for an actual ViewModel to be passed around to other VMs via my mediator. The ProjectSettingsViewModel wouldn't use even half of the fields on ProjectDocument if they were combined.
Maybe I'm looking at this the wrong way. Is there a better way to reduce the duplication while still keeping the responsibilities separate?
I have a master viewmodel that handles application states like which document/tab/window is open and I have an ISettingssService for other global needs. Passing one viewmodel to another is discouraged and you might want to think about creating am iSettings service that can be injected into the viewmodels, then the viewmodels can interact with "global" settings but the viewmodels are still decoupled because they do not depend on another viewmodel.
So, I would not combine them. Either I would have a dictionary of documents that can be open in the master viewmodel with a current open doc property or move this logic to a service/interface that gets used by each viewmodel.
Hope this helps.
Related
Using MVVM, one type of ViewModels include the Model they represnt as a Field.
So I do have a CompanyModel and a CompanyViewModel that has one instance of CompanyModel.
This CompanyModel has a collection of Divisions belonging to it. So CompanyModel has a List (or some collection class).
Now the CompanyViewModel would want to represent these Divisions as an ObservableCollection<DivisionViewModel>; and you you could add new Divisions in the CompanyViewModel.
What is the best way ensure that the ObservableCollection and the Models collection stay in sync? So when I add a new DivisionViewModel and save it, it automatically saves its model to the CompanyModel's List<Division>?
I have more classes like this Parent/child relations so I would love something I could reuse or implement perhaps in a AbstractViewModel class.
Note: My ViewModels implement IEditableObject
Probably the easiest way to do this is to create a new class that inherits from ObservableCollection, and which takes a source list and various initialization and mapping functions as parameters. Its signature might look something like this:
public class SynchronizedObservableCollection<TDest, TSource> : ObservableCollection
{
public SynchronizedObservableCollection(
IList<TSource> source,
Func<TSource, TDest> newDestFunc,
Func<TDest, TSource> newSourceFunc),
Func<TSource, TDest, bool> mapSourceToDestFunc
{
// Initialize the class here.
}
}
You'd then want handle the CollectionChanged event, creating new Source instances when a new Destination instance got added, deleting existing Source instances when an existing Destination instance got deleted, that sort of thing. You'd use the "new" functions above to create new instances of the various entities, and you'd use the "map" functions above in various Linq queries that would allow you to figure out, say, which instance of a viewmodel your ObservableCollection mapped to a model in your List.
You would use it in your example above like so, perhaps:
var divisionViewModels = new SynchronizedObservableCollection(
company.DivisionList,
division => new DivisionViewModel(division),
divisionVm => divisionVm.Model,
(division, divisionVm) => divisionVm.Model == division);
The exact implementation is left as an exercise to the reader :-). But I've used classes like this with some success in previous projects. Just make sure you work up some good unit tests around it, so that you know you can rely on it, and don't have to spend a lot of time hunting through event-handling callstacks.
I am wondering whether it is a good idea to make labels public so other classes can change them and get their value. Is this a good idea? If not, how should it be done then?
I wouldn't make the label public.
It would be better to add a public method that was specific to what the label was displaying, and have it update the label.
For example, if your label was a "System status" label, you might want to add (to your Form/UserControl):
public void SetStatusInformation(string currentStatus)
{
this.statusLabel.Text = currentStatus;
}
This allows you, later, to change how this information is displayed (in case you want to use a different control), and also simplifies your API, since the public methods are very clear to the user.
it's a bad idea. WinForms leaves many "what's the best way to do X?" questions open; and your best answer is to follow established patterns and practices (which aren't WinForms specific).
Read up on the MVP or MVC patterns. They are both high-level patterns which focus on seperating out your UI-specific code from your business-logic. Without this seperation your application can quickly become a maintenance nightmare, and things that should be simple get much more complicated.
For your specific scenario you would likely end up with a Model (your business-logic) which uses databinding to show it's data on the WinForms screen. When a change on the UI occurs it would be the Model that receives the change, and that change would propagate to the UI via databinding.
I would suggest wrapping in a setter property or method because it's very possible you'll have to do something like add logging or re-call on window's main thread if the caller is from another one. I found it easier to just always use code like the following when exposing functionality that lets clients update anything graphical.
public void SetStart()
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate()
{
this.SetStart();
});
}
else
{
progressBar1.Value = 0;
progressBar1.Visible = true;
}
}
I read this article today http://dotnetslackers.com/articles/silverlight/Silverlight-3-and-the-Data-Form-Control-part-I.aspx about the use of the MVVM pattern within a silverlight app where you have your domain entities and view spesific entities which basically is a subset of the real entity objects. Isn't this a clear violation of the DRY principle? and if so how can you deal with it in a nice way?
Personally, I don't like what Dino's doing there and I wouldn't approach the problem the same way. I usually think of a VM as a filtered, grouped and sorted collections of Model classes. A VM to me is a direct mapping to the View, so I might create a NewOrderViewModel class that has multiple CollectionViews used by the View (maybe one CV for Customers and another CV for Products, probably both filtered). Creating an entirely new VM class for every class in the Model does violate DRY in my opinion. I would rather use derivation or partial classes to augment the Model where necessary, adding in View specific (often calculated) properties. IMO .NET RIA Services is an excellent implementation of combining M and VM data with the added bonus that it's usable in on both the client and the server. Dino's a brilliant guy, but way to call him out on this one.
DRY is a principle, not a hard rule. You are a human and can differentiate.
E.g. If DRY really was a hard rule you would never assign the same value to two different variables. I guess in any non trivial program you would have more than one variable containing the value 0.
Generally speaking: DRY does usually not apply to data. Those view specific entities would probably only be data transfer objects without any noteworthy logic. Data may be duplicated for all kinds of reasons.
I think the answer really depends on what you feel should be in the ViewModel. For me the ViewModel represents the model of the screen currently being displayed.
So for something like a ViewCategoryViewModel, I don't have a duplication of the fields in Category. I expose a Category object as a property on the ViewModel (under say "SelectedCategory"), any other data the view needs to display and the Commands that screen can take.
There will always be some similarity between the domain model and the view model, but it all comes down to how you choose to create the ViewModel.
It's the same as with Data Transfer Objects (DTO).
The domain for those two object types is different, so it's not a violation of DRY.
Consider the following example:
class Customer
{
public int Age
}
And a corsponding view model:
class CustomerViewModel
{
public string Age;
// WPF validation code is going to be a bit more complicated:
public bool IsValid()
{
return string.IsNullOrEmpty(Age) == false;
}
}
Differnt domains - differnet property types - different objects.
I am getting my knickers in a twist recently about View Models (VM).
Just like this guy I have come to the conclusion that the collections I need to expose on my VM typically contain a different type to the collections exposed on my business objects.
Hence there must be a bi-directional mapping or transformation between these two types. (Just to complicate things, on my project this data is "Live" such that as soon as you change a property it gets transmitted to other computers)
I can just about cope with that concept, using a framework like Truss, although I suspect there will be a nasty surprise somewhere within.
Not only must objects be transformed but a synchronization between these two collections is required. (Just to complicate things I can think of cases where the VM collection might be a subset or union of business object collections, not simply a 1:1 synchronization).
I can see how to do a one-way "live" sync, using a replicating ObservableCollection or something like CLINQ.
The problem then becomes: What is the best way to create/delete items?
Bi-directinal sync does not seem to be on the cards - I have found no such examples, and the only class that supports anything remotely like that is the ListCollectionView. Would bi-directional sync even be a sensible way to add back into the business object collection?
All the samples I have seen never seem to tackle anything this "complex".
So my question is: How do you solve this? Is there some technique to update the model collections from the VM? What is the best general approach to this?
Personally, I use an ObservableCollection in my model and my viewmodel.
class Model
{
public ObservableCollection<Foo> Foos;
}
class ViewModel
{
public Model Model;
public ObservableCollection<FooView> Foos;
public ViewModel()
{
Model.Foos.CollectionChanged += OnModelFoosCollection_CollectionChanged;
}
void OnModelFoosCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Foo re;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
re = e.NewItems[0] as Foo;
if (re != null)
AddFoo(re); //For other logic that may need to be applied
break;
case NotifyCollectionChangedAction.Remove:
re = e.OldItems[0] as Foo;
if (re != null)
RemoveFoo(re);
break;
case NotifyCollectionChangedAction.Reset:
Foos.Clear();
/* I have an AddRange in an ObservableCollection-derived class
You could do Model.Foo.ForEach(ree => AddFoo(ree));
*/
var converter =
from ree in Model.Foo
select new FooView(ree);
Reports.AddRange(converter);
break;
default:
//exercise for the reader :)
s_ILog.Error("OnModelFoosCollection_CollectionChangedDid not deal with " + e.Action.ToString());
break;
}
}
void AddFoo(Foo f)
{
Foos.Add(new FooView(f));
}
void RemoveFoo(Foo f)
{
var match = from f in Foos
where f.Model == f //if you have a unique id, that might be a faster comparison
select f;
if(match.Any())
Foos.Remove(match.First());
}
}
Now, when you remove something from the Model's Foo collection, it will automatically remove the corresponding FooView. This corresponds with how I think about this sort of thing. If I want to delete something, the Model is where is really needs to be deleted.
It feels like a lot of code, but it isn't that much, really. I'm sure one could build a generic version of this, but IMO you always end up wanting custom logic dealing with addition/removal of elements.
I too am struggling with the bi-directional sync of two collections for use with WPF via MVVM. I blogged MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model? (Part 1) and MVVM: To Wrap or Not to Wrap? Should ViewModels wrap collections too? (Part 2) regarding the question, including some sample code that shows a two way sync. However, as noted in the posts, the implementation is not ideal. I would qualify it as a proof of concept.
I like the BLINQ, CLINQ, and Obtics frameworks that Alex_P posted about. These are a very nice way to get one side of the sync behvaior. Maybe the other side (from VM to Model) can be implemented via an alternate path? I just posted part 3 on my blog that discusses some of this.
From what I can see, bi-directional via BLINQ and CLINQ is not supported in cases where the LINQ statement projects the data to a new structure.
However, it does look like CLINQ may support Bi-Directional syncing in cases where the LINQ query returns the same datatype as the underlying collection. This is more of a filtering scenario, which doesn't match the use case of a ViewModel wrapping the data in the Model.
The only situation, when you might need the two-way synchronization, is when the control that you use to visualize your collection of VMs does not let you know of user's intention to create or remove an item. I.e. the control deals directly with your collection of VMs and the ONLY way you know the item has been added/removed, is by monitoring the collection of VMs. If this is not the case then you can implement one way sync and add/remove items directly on model's collection.
EDIT: Take for example WPF DataGrid
control bound to observable collection
of ItemViewModels. If its
CanUserAddRows property is set to true
and the user starts typing in the
empty row at the bottom, the DataGrid
will use default constructor of your
ItemViewModel to create a loose item
and then will add it to the
collection. There is no indication
from DG that it wants to add an item
the collection.c
I can't think of any
other control that is complicated
enough to be able to add items to
collection on its own.
The opposite
scenario is when you have ListView
bound to your collection and a command
which indicates user's intention to
add new item - then in command handler
you simply add new item to DataModel
and let the one-way sync do the rest of
the job. In this case ListView is not
able to add to the collection it
presents.
As to the sync process itself, look at Bindable LINQ project - it can minimize the amount code and improve readability. For example the code Tom posted will translate into something like this:
class ViewModel
{
public Model Model;
public ObservableCollection<FooView> Foos;
public ViewModel()
{
Foos = from foo in Model.Foos.AsBindable()
select new FooView(foo);
}
}
EDIT 2: After using B-LINQ for some time now I should say that you might have performance issues with it. I have used it to synchronize relatively big collections (hundreds of elements) collections with tens of elements being added and removed every second and I had to give it up and implement synchronization the way Tom had suggested.
I still use B-LINQ though in those parts of the project where collections are small and performance is not an issue.
I've written some helper classes for wrapping observable collections of business objects in their View Model counterparts here, maybe it should be extended to go the other way. always looking for contributions...
I have proposed a general Undo / Redo framework based in MVVM, that uses some techniques related to the problem you describe. It uses collections implementing this interface:
public interface MirrorCollectionConversor<V, D>
{
V GetViewItem(D modelItem, int index);
D GetModelItem(V viewItem, int index);
}
(V is for the ViewModel items, D for the model items)
Using this interface it automatically synchronizes the viewmodel collection when the model collection changes. If you change the viewmodel the change is simply redirected to the model collection.
The GetViewItem function gives you some flexibility in how the viewmodel objects are related to his model counterparts.
You can find the details here.
(I admit that the construction is quite complex and I will be very happy to listen to suggestions).
With my ObservableComputations library you can create ObservableCollection that is result of computing over another ObservableCollection and is synchoronized with it. You make standart operations (add, remove) on the source colection: computed Collection reflects all the changes:
Filtering<Order> expensiveOrders = orders.Filtering(o => o.Price > 25);
expensiveOrders.CollectionChanged += (sender, eventArgs) =>
{
// see the changes (add, remove, replace, move, reset) here
};
// Start the changing...
orders.Add(new Order(8, 30));
orders.RemoveAt(1);
orders[0].Price = 60;
orders[4].Price = 10;
orders.Move(5, 1);
orders[1] = new Order(10, 17);
Currently for ASP.Net stuff I use a request model where a context is created per request (Only when needed) and is disposed of at the end of that request. I've found this to be a good balance between not having to do the old Using per query model and not having a context around forever. Now the problem is that in WPF, I don't know of anything that could be used like the request model. Right now it looks like its to keep the same context forever (Which can be a nightmare) or go back to the annoying Using per query model that is a huge pain. I haven't seen a good answer on this yet.
My first thought was to have an Open and Close (Or whatever name) situation where the top level method being called (Say an event handling method like Something_Click) would "open" the context and "close" it at the end. Since I don't have anything on the UI project aware of the context (All queries are contained in methods on partial classes that "extend" the generated entity classes effectively creating a pseudo layer between the entities and the UI), this seems like it would make the entity layer dependent on the UI layer.
Really at a loss since I'm not hugely familiar with state programming.
Addition:
I've read up on using threads, but the
problem I have with a context just
sitting around is error and recovery.
Say I have a form that updates user
information and there's an error. The
user form will now display the changes
to the user object in the context
which is good since it makes a better
user experience not to have to retype
all the changes.
Now what if the user decides to go to
another form. Those changes are still
in the context. At this point I'm
stuck with either an incorrect User
object in the context or I have to get
the UI to tell the Context to reset
that user. I suppose that's not
horrible (A reload method on the user
class?) but I don't know if that
really solves the issue.
Have you thought about trying a unit of work? I had a similar issue where I essentially needed to be able to open and close a context without exposing my EF context. I think we're using different architectures (I'm using an IoC container and repository layer), so I have to cut up this code a bit to show it to you. I hope it helps.
First, when it comes to that "Something_Click" method, I'd have code that looked something like:
using (var unitOfWork = container.Resolve<IUnitOfWork>){
// do a bunch of stuff to multiple repositories,
// all which will share the same context from the unit of work
if (isError == false)
unitOfWork.Commit();
}
In each of my repositories, I'd have to check to see if I was in a unit of work. If I was, I'd use the unit of work's context. If not, I'd have to instantiate my own context. So in each repository, I'd have code that went something like:
if (UnitOfWork.Current != null)
{
return UnitOfWork.Current.ObjectContext;
}
else
{
return container.Resolve<Entities>();
}
So what about that UnitOfWork? Not much there. I had to cut out some comments and code, so don't take this class as working completely, but... here you go:
public class UnitOfWork : IUnitOfWork
{
private static LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("UnitOfWork");
private Entities entities;
public UnitOfWork(Entities entities)
{
this.entities = entities;
Thread.SetData(slot, this);
}
public Entities ObjectContext
{
get
{
return this.Entities;
}
}
public static IUnitOfWork Current
{
get { return (UnitOfWork)Thread.GetData(slot); }
}
public void Commit()
{
this.Entities.SaveChanges();
}
public void Dispose()
{
entities.Dispose();
Thread.SetData(slot, null);
}
}
It might take some work to factor this into your solution, but this might be an option.