Reputation: 7
I have been trying to apply the MVVM
design pattern to my latest project. I have watched countless videos and read a large number of tutorials, whilst also looking through people's demo code.
I have a Job object and a Task object. I have created a ViewModel
for each. I have also created an AllJobs and AllTasks ViewModel
that creates an ObservableCollection
of JobViewModels
and TaskViewModels
respectively.
If I set my DataContext of the MainWindow to an instance of AllJobsViewModel
then I can access the ObservableCollection
named 'AllJobs' and use it as I wish. However, giving that this is the DataContext, how do I then access my AllTasksViewModel AllTasks collection in the same window?
Upvotes: 0
Views: 242
Reputation: 11595
You can use an EventAggregator or MessageBus.
The idea is to have your view-models subscribe to events that they would like to respond to.
I use the Publish Subscribe pattern for complicated class-dependencies:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
Window:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
You can leverage Bizmonger.Patterns to get the MessageBus.
MessageBus
public class MessageBus
{
#region Singleton
static MessageBus _messageBus = null;
private MessageBus() { }
public static MessageBus Instance
{
get
{
if (_messageBus == null)
{
_messageBus = new MessageBus();
}
return _messageBus;
}
}
#endregion
#region Members
List<Observer> _observers = new List<Observer>();
List<Observer> _oneTimeObservers = new List<Observer>();
List<Observer> _waitingSubscribers = new List<Observer>();
List<Observer> _waitingUnsubscribers = new List<Observer>();
int _publishingCount = 0;
#endregion
public void Subscribe(string message, Action<object> response)
{
Subscribe(message, response, _observers);
}
public void SubscribeFirstPublication(string message, Action<object> response)
{
Subscribe(message, response, _oneTimeObservers);
}
public int Unsubscribe(string message, Action<object> response)
{
var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public int Unsubscribe(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public void Publish(string message, object payload)
{
_publishingCount++;
Publish(_observers, message, payload);
Publish(_oneTimeObservers, message, payload);
Publish(_waitingSubscribers, message, payload);
_oneTimeObservers.RemoveAll(o => o.Subscription == message);
_waitingUnsubscribers.Clear();
_publishingCount--;
}
private void Publish(List<Observer> observers, string message, object payload)
{
Debug.Assert(_publishingCount >= 0);
var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());
foreach (var subscriber in subscribers)
{
subscriber.Respond(payload);
}
}
public IEnumerable<Observer> GetObservers(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
return observers;
}
public void Clear()
{
_observers.Clear();
_oneTimeObservers.Clear();
}
#region Helpers
private void Subscribe(string message, Action<object> response, List<Observer> observers)
{
Debug.Assert(_publishingCount >= 0);
var observer = new Observer() { Subscription = message, Respond = response };
if (_publishingCount == 0)
{
observers.Add(observer);
}
else
{
_waitingSubscribers.Add(observer);
}
}
#endregion
}
}
Subscription
public class Subscription
{
#region Members
List<Observer> _observerList = new List<Observer>();
#endregion
public void Unsubscribe(string subscription)
{
var observers = _observerList.Where(o => o.Subscription == subscription);
foreach (var observer in observers)
{
MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
}
_observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
}
public void Subscribe(string subscription, Action<object> response)
{
MessageBus.Instance.Subscribe(subscription, response);
_observerList.Add(new Observer() { Subscription = subscription, Respond = response });
}
public void SubscribeFirstPublication(string subscription, Action<object> response)
{
MessageBus.Instance.SubscribeFirstPublication(subscription, response);
}
}
Upvotes: 0
Reputation: 11717
You're doing it the wrong direction if you design your ViewModels to mirror your business objects. Rather, a ViewModel should be specific to a view. There should be a strict 1:1 relationship between the ViewModel and its View - in other words: they form a pair.
The model part is independent from this distinction, which depends solely on the business case and the platform you want to target.
Your requirements (the use cases) govern the view part of your application. The business domain governs the design of your model. The ViewModel then is there to mediate between the two. It merely fulfils a technical requirement for the view part of your application.
Upvotes: 1
Reputation: 4352
You don't need to create separate view models for each items. You need to create a single view model which contains all the information you need and make that viewmodel as a DataContext for your main window.
Or else, Create another one viewmodel which contains your both AllJobsViewModel and AllTasksViewModel. And make the new viewmodel as a datacontext for your MainWindow.
Upvotes: 0
Reputation: 13600
There is rarely a 1:1 relation when it comes to models and viewmodels. You can have ViewModels that combine multiple models or even multiple viewmodels into one. I believe that's the preferred solution in your situation as well.
The main point behind MVVM is to separate layers as much as possible, especially UI and the Model. See my latest answer to see what I mean by that What goes into the Model/Viewmodel?
Upvotes: 0