Reputation: 321
I'm using MVVM and (still - I'm very new to it) struggling a bit with patterns. I have an object called a Mission which has 2 children, Network and Platform. A mission may have 1 to many platforms and has 1 or 2 Networks (primary which always exists and alternative which is optional). There is a child of Network and Platform called NetworkPlatform and the number of those will depend on the number of platforms and networks - if there are 5 platforms and they haven't defined the alternate network there would be 5 NetworkPlatforms otherwise 10. So if a platform is added I need to add either one or two network platforms depending on whether the alternative network exists. Similarly if they choose to define the alternate network where it didn't exist previously I'd need to add a network platform per platform. Ditto removal.
I have a sort of umbrella ViewModel for Mission which instantiates some child ViewModels like this
public MissionFullDataViewModel(Mission mission):base(mission)
{
MissionVM = new MissionViewModel(mission);
PlatformsVM = new PlatformsViewModel(mission);
PrimaryNetworkVM = new NetworkViewModel(mission, mission.PrimaryNetwork);
//AlternateNetworkVM = new NetworkViewModel(mission, mission.AlternateNetwork);
}
I've commented the alternate one because I'm not quite sure how to deal with it yet. A user will probably indicate that they want one via a checkbox, but the application will also be used to show existent data so will need to work out whether the alternate one exists or not. Not quite sure about that yet.
I'm dealing with the NetworkPlatforms in the NetworkViewModel, they are an observablecollection like this:
private ObservableCollection<NetworkPlatform> networkPlatforms;
public ObservableCollection<NetworkPlatform> NetworkPlatforms
{
get
{
if (networkPlatforms == null)
{
networkPlatforms = new ObservableCollection<NetworkPlatform>(Network.NetworkPlatforms);
}
return networkPlatforms;
}
}
}
and I will clearly need to write some methods to add and remove a NetworkPlatform from that collection. My platforms are an observable collection in the PlatformsViewModel. I have a number spinner for user to specify the number of platforms and a function that will add new ones or remove the last x ones like this:
private void ResyncPlatforms(int newValue)
{
int oldValue = this.Platforms.Count;
int diff = newValue - oldValue;
if (diff > 0)
{
for (var i = 0; i < diff; i++)
{
var newPlatform = new Platform();
newPlatform.Mission = Mission;
this.Platforms.Add(newPlatform);
missionRepository.AddPlatformToMission(Mission, newPlatform);//TODO need to tell the Network VM to add a network platform
}
}
else
{
for (var i = 0; i > diff; i--)
{
var platToRemove = Platforms.Last();
this.Platforms.Remove(platToRemove);
missionRepository.RemovePlatformFromMission(Mission, platToRemove);//TODO need to tell the Network VM to remove network platform
}
}
}
The repository is a wrapper with CRUD options to a LINQToSQL backend. The Add...and Remove...methods tell the database to insert/delete when committed (when user saves).
So how do I get my NetworkVM to add/remove NetworkPlatforms when the PlatformsVM adds/removes a platform in the resync method?
I know I could have my Resync function call to methods in my NetworkViewModel which will add and remove networkplatforms its collection, and I could probably do something similar in the umbrella view model when the user toggles the alternate network on or off. But I think there must be a better way, because doing it that way my Platforms VM needs to have access to the NetworkviewModels for primary and alternate (if it exists) networks, and I can't really see how to do it when the alternate network is toggled on and off.
Would it be better to have my networkviewmodel listen for changes to the PlatformVM's Platforms observable collection and if so how?
I'm not really clear what to do and would appreciate some help. Thanks.
Upvotes: 1
Views: 150
Reputation: 31454
Correct, this is best handled with observer pattern which in .NET is events system. You can either:
PlatformsViewModel
(think PlatformAdded
& PlatformRemoved
)ObservableCollection
Difference between those two approaches is that in second one, your NetworkViewModel
will be more coupled to PlatformsViewModel
-- ie. it needs to know how it stores platforms, and that this storage is observable collection which events it may need to handle.
Note that this might be partially mitigated if your aggregating view model (MissionFullDataViewModel
) participates as kind of man in the middle of this communication, as in - it will subscribe to PVM obserable collection's events and invoke NVM methods as a response to this.
Either way, you should use your aggregating view model to bind those two objects, say:
public MissionFullDataViewModel(Mission mission):base(mission)
{
MissionVM = new MissionViewModel(mission);
PlatformsVM = new PlatformsViewModel(mission);
PrimaryNetworkVM = new NetworkViewModel(mission, mission.PrimaryNetwork);
// #1: direct binding -- tightly coupled
PlatformsVM.Platforms.CollectionChanged += NetworkVM.PlatformsChangedHandler;
// #2: direct binding -- less tightly coupled
PlatformsVM.PlatformAdded += NetworkVM.PlatformsChangedHandler;
PlatformsVM.PlatformRemoved += NetworkVM.PlatformsChangedHandler;
// #3: indirect binding -- less tightly coupled
PlatformsVM.Platforms.CollectionChanged += this.HandlePlatformsChange;
// #4: indirect binding -- loosely coupled
PlatformsVM.PlatformAdded += this.HandlePlaftormsChange;
PlatformsVM.PlatformRemoved += this.HandlePlatformsChange;
}
HandlePlatformsChange
serves as kind of mediator pattern utility; it might prepare data that's coming from PlatformsVM
events before it finally calls appropriate methods on NetworkVM
.
In the last scenario, nobody knows anything - just the required data to perform task. And this is approach I'd suggest. Platforms and Networks don't need to know much about one another and coupling logic is performed by entity that knows about those two either way - MissionFullDataViewModel
.
Upvotes: 1
Reputation: 21251
I'm sorry but I really can't follow exactly what you want. Is there any way you can make it a bit more succinct?
Would it be better to have my networkviewmodel listen for changes to the PlatformVM's Platforms observable collection and if so how?
Now this I can answer. ObservableCollection is, well, observable. It will emit CollectionChanged events when things are added and removed, so just subscribe to the event and react accordingly:
private void SubscribeToPlatformChanges()
{
((INotifyCollectionChanged)_PlatformVM.Platforms).CollectionChanged += (s, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add: //platforms were added - use e.NewItems
case NotifyCollectionChangedAction.Add: //platforms were removed - use e.OldItems
case NotifyCollectionChangedAction.Reset: //all platforms were removed
}
}
}
Upvotes: 1