Imagine a WPF application with one main view representing a window that can be opened multiple times. This main view has several child views.
The messenger could send messages between the viewmodels of the main view and its child views' viewmodels.
Can the messenger be made to have a restricted scope so that if you had two main views open, interacting with one would message only that view's child views?
Is there another way to share an "identity" between the parent and child view models in a way that honours MVVM?
You can always choose between passing default IMessenger which will have static, application domain scope or creating new instance of messenger:
var domainWideMessenger = GalaSoft.MvvmLight.Messaging.Messenger.Default;
var localMessenger = new Messenger();
First approach is useful when you don't want to control scope of messenger. You can treat it as a central hub. "local" messengers are good for communications within VM or within some container.
Why this might be better then having tokens?! When you have advanced application with messaging built on tokens at some point you'll face difficulties handling them (picking the right one). Especially when it comes to dependency injection.
In your case you'll have new instance of Messenger per MainView which will be pushed down to all its child views and view models. To sync data and communicate between multiple instances of MainView use static Messenger from MVVM Light.
You could use the concept of "tokens" to achieve this effect.
The IMessenger interface has overloads of Register and Send that accept an object to restrict which registrants receive a message. If a message is sent with a token, the only objects that will see the message are those that registered for the message with the same token. Here, "same" means object equality, so you can use any object for the token that has sensible equality semantics and makes sense to you, i.e. a GUID, integer, or string.
As an example, consider the following objects:
public static class MessengerHelper
{
public static IMessenger Messenger { get { return GalaSoft.MvvmLight.Messaging.Messenger.Default; } }
public static object Group1Token { get { return 1; } }
public static object Group2Token { get { return 2; } }
}
public class FooChild
{
object token;
public FooChild(object token)
{
this.token = token;
MessengerHelper.Messenger.Register<IFooMessage>(this, token, HandleFooMessage);
}
void HandleFooMessage(IFooMessage fooMessage)
{
Console.WriteLine("FooChild got the message, token = " + (token ?? "(null)"));
}
}
public class FooParent
{
FooChild[] children;
public FooParent()
{
children = new [] {
new FooChild(MessengerHelper.Group1Token),
new FooChild(MessengerHelper.Group2Token),
new FooChild(null)
};
}
public void SendFooMessage(IFooMessage fooMessage, object token)
{
MessengerHelper.Messenger.Send(fooMessage, token);
}
}
Then if you create the parent and send a message with the given tokens:
FooParent parent = new FooParent();
parent.SendFooMessage(new FooMessage(), MessengerHelper.Group1Token);
parent.SendFooMessage(new FooMessage(), MessengerHelper.Group2Token);
You'll get the following output:
FooChild got the message, token = 1
FooChild got the message, token = 2
In your case, you'll want each main view models to have their own token, and pass their token to their child view models.
Related
I have a simple requirement I am trying to achieve. Basically I have a view that gets populates with a list of businesses. The property for the list of businesses is embodied in my viewmodel class which in turn is bound to the view. This is a simple MVC application with a list of business.
However, the issue I have is that I derive the list of business for another class which is a dependency to the view model, and its basically similar to a repository which I call BusinessService. Busy service is comprised of async methods and this is the dilemma I have, when the call is made from the Ctor of the viewModel or the getter of the property, my application hangs. The call is to a EF database asynchrounous too within the businessservice and am not sure what is the correct approach for this. Please see code below:
ViewModel:
#region Ctor
public BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
{
_businessService = businessService;
_builder = builder;
InitBusinesses().Wait(); //OPTION 1
}
#endregion
#region Properties
public IEnumerable<BusinessViewModel> _businesses;
public IEnumerable<BusinessViewModel> Businesses
{
get
{
if (_businesses == null)
{
InitBusinesses().Wait(); //OPTION 2
}
return _businesses;
}
set => _businesses = value;
}
private async Task InitBusinesses()
{
var response = await _businessService.Get();
Businesses = response.IsSuccessful
? response.Data.Select(p => new BusinessViewModel(_builder, p))
: new List<BusinessViewModel>();
}
BUSINESS SERVICE:
#region Service Methods
public async Task<Response<IEnumerable<Models.Business>>> Get()
{
var data = await Db.Businesses.ToListAsync();
return new Response<IEnumerable<Models.Business>>
{
IsSuccessful = true,
Message = "Successful",
Data = Mapper.Map<List<Models.Business>>(data)
};
}
Please may you advise the best pattern and the correct way to do this, I already know this is wrong> Thank you
I wrote an article on the subject.
When the UI framework asks your code to display something, it must be displayed immediately (synchronously). ViewModel constructors and data-bound properties should be synchronous and immediate. Doing network I/O is simply not an option; even if you got it working (which is possible), all that would do is block your UI thread, degrading your user experience.
A more proper solution is to synchronously initialize into a loading state ("Loading..." message, spinner, whatever) and also start the asynchronous operation. Then, when the operation completes, update the UI with the actual data.
You should consider using a factory method that returns a Task
private BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
{
_businessService = businessService;
_builder = builder;
}
public static async Task<BusinessListViewModel> Create(IBusinessService businessService, IStringBuilder builder)
{
var instance = new BusinessListViewModel(businessService, builder)
await InitBusiness();
return instance;
}
I need to pass some data that I grab when the app comes back into the foreground, I have managed to trigger the method but I can't figure out how to trigger it in the existing instance of my ViewController rather than making a new instance.
Map.cs
public delegate void beginRefreshMapLine(ReturnRouteTaken returnRouteTaken);
public void updateRouteList(ReturnRouteTaken returnRouteData)
{
coordList = new List<CLLocationCoordinate2D>();
foreach(GPSData point in returnRouteData.GPSData)
{
coordList.Add(new CLLocationCoordinate2D { Latitude = double.Parse(point.Lat), Longitude = double.Parse(point.Lng) });
updateMap(this, new EventArgs());
}
}
this is the method I need to trigger in the current instance from AppDelegate.cs
AppDelegate.cs
if (GlobalVar.BoolForKey("trackMe"))
{
ReturnRouteTaken returnRouteData = webtools.GetRouteTaken(new ReturnRouteTaken() { TestDriveID = GlobalVar.IntForKey("routeTrackedID") });
if (returnRouteData.GPSData.Count > 0)
{
}
}
Here is where I am stuck, I have tried looking into delegates and invoking the method that way but I cannot get my head around how to implement it. Any help would be appreciated
I flagged this as a possible dupe, but that thread is in Obj-C, however the same concept can easily be applied using Xamarin.iOS.
Just create a Singleton class with an array or List of UIViewControllers as a property in that class and every time you instantiate a new ViewController, add it to the array orList, but also make sure you remove a view controller from the array or List when the view controller is disposed.
e.g. your singleton could look like:
public class ViewControllerHolder
{
// make constructor private to force use of Instance property
// to create and get the instance.
private ViewControllerHolder()
{
}
private static ViewControllerHolder _instance;
public static ViewControllerHolder Instance
{
get
{
if (_instance == null)
{
_instance = new ViewControllerHolder();
_instance.Controllers = new List<UIViewController>();
}
return _instance;
}
}
public List<UIViewController> Controllers { get; private set; }
}
And then you can always get access to your List of view controllers with ViewControllerHolder.Instance.Controllers and perform any add or remove operations on it.
And if you are really only interested in the one view controller, then just add that one to the List when instantiated, but do remove it when the view controller is no longer needed so you don't try to access a disposed view controller and also so that the view controller can be garbage collected when it is no longer in use.
Creating a singleton array that holds all the living UIViewControllers works, personally I like to keep things decoupled as much as I can and do not like holding and maintaining a list of objects for no real reason...
You can pass data around via:
Selector
NoticationCenter
In any UIViewController that you need to "talk" to, you can subscribe to notifications and/or register Selectors.
In your UIViewController register for which Notifications you wish to receive...
public override void ViewDidLoad()
{
base.ViewDidLoad();
NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector(Const.StartRefresh), new NSString(Const.StartRefresh), null);
}
Still in your UIViewController, implement the selector that the notification center will perform a send_msg to:
[Export(Const.StartRefresh)]
void LocalStartRefresh(NSNotification notification)
{
if (notification.Name == Const.StartRefresh)
Console.WriteLine("StartRefresh from NotificationCenter:" + notification.Object);
}
In your UIApplicationDelegate, use the notification center to publish a new NSNotification to every active UIViewController that has subscribed:
public override void WillEnterForeground(UIApplication application)
{
NSNotificationCenter.DefaultCenter.PostNotificationName(Const.StartRefresh, new NSString("some custom data"));
}
Or, skip notifications and directly invoke the Selector:
In your UIViewController, implement the selector/method to call:
[Export(Const.StopRefresh)]
void LocalStopRefresh()
{
Console.WriteLine("StopRefresh from Selector");
}
In your UIApplicationDelegate, send an action to all instanced view controller instances that accept this Selector:
public override void DidEnterBackground(UIApplication application)
{
var vc = UIApplication.SharedApplication?.KeyWindow?.RootViewController;
while (vc != null)
{
if (vc.RespondsToSelector(new Selector(Const.StopRefresh)))
UIApplication.SharedApplication.SendAction(new Selector(Const.StopRefresh), vc, this, new UIEvent());
vc = vc.PresentedViewController;
}
}
I am writing Windows Phone chat application and I would like to implement ChatPage which will be used to hold on a conversation view. The whole idea is to create view as much similar to SMS page as it is possible, and switch between instances of that page.
And here comes the problem - on each instance of page I need to bind the Contact and ContactMessages which are stored in XML file. I think that getting proper messages from XML file for Contact can be simply written in constructor. But how to open new instance of page and send it a Contact.
I am using MVVM Light toolkit and I could use Messanger to do it but how can I get sure that my Contact wont be registered in other instances of page. Registering an instance of ChatPageViewModel in that case can't be realized in ViewModelLocator because I need to use multiple instances of that ViewModel (reprimand me if I am wrong, I am newbie in MVVM light).
Is any way to achieve that? Or maybe the way I am thinking about that is totally wrong?
You can try to make a Service with a interface. And when the customer click on the contact, pass the parameters that this service can search an return the messages with the Contact. And every ViewModel that need this service you can inyected it from the constructor. Only you need some parameter to know which xml to read.
I dont know if a IoC container. Unity or Autofac or others can solve your problem too.
If you use Autofac you can have a similar class than this:
public class ViewModelLocator
{
private IContainer container;
public ViewModelLocator()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<MainViewModel>();
container = builder.Build();
}
public MainViewModel MainViewModel
{
get { return container.Resolve<MainViewModel>(); }
}
}
And you can work with this instances like this parameters:
builder.RegisterType<MainViewModel>().InstancePerDependency();
builder.RegisterType<MainViewModel>().InstancePerLifetimeScope();
builder.RegisterType<MainViewModel>().SingleInstance();
for more info here for Autofac and here for Unity
Hope helps. Greetings!
I'd go for this scenario, based on MVVM Light:
On a central page you have a list of all your contacts. As soon as the user selects one contact you broadcast a message that a contact has been selected. The conversation viewmodel subscribes to this message and loads the conversation from the model. The contacts viewmodel uses a navigation service to navigate to the conversation view.
(Part of) The contacts ViewModel:
public ContactsViewModel(IDataService dataService, INavigationService navigationService)
{
_dataService = dataService;
_navigationService = navigationService;
}
private RelayCommand _showConversation;
public RelayCommand ShowEvents
{
get
{
return _showConversation ?? (_showConversation = new RelayCommand(ExecuteShowConversation));
}
}
private void ExecuteShowConversation()
{
Messenger.Default.Send(new ContactSelectedMessage(){Contact = Contact);
_navigationService.NavigateTo(new Uri("/Views/ConversationView.xaml", UriKind.RelativeOrAbsolute));
}
(Part of) the Conversation ViewModel:
public ConversationViewModel(IDataService dataService, INavigationService navigationService)
{
_dataService = dataService;
_navigationService = navigationService;
Messenger.Default.Register<ContactSelectedMessage>(this, OnContactSelected);
}
private void OnContactSelected(ContactSelectedMessage contactSelectedMessage)
{
_dataService.GetConversation(contactSelectedMessage.Contact);
}
In order for the viewmodel to receive messages it should be instantiated before the message is broadcast. So, you must instantiate it in the ViewModelLocator.
In the ViewModelLocator:
static ViewModelLocator()
{
/// parts missing for brevity
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<INavigationService, NavigationService>();
SimpleIoc.Default.Register<ContactsViewModel>();
SimpleIoc.Default.Register<ConversationViewModel>(true);
}
I have a user control of list of Patients which I use in other Views. However when I choose one of the Patients, the selection is propagated to all the views containing an instance of the user control. How can I make each view instantiate a new instance of the user control for each view?
I am using c#
Guessing from what you stated, I'd assume that you returning a static instance of you PatientViewModel from you locator. To solve this make sure that when the property is called a new instance of the view model is generated.
Edit: Locator with different instantiation methods
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
//if (ViewModelBase.IsInDesignModeStatic)
//{
// // Create design time view services and models
// SimpleIoc.Default.Register<IDataService, DesignDataService>();
//}
//else
//{
// // Create run time view services and models
// SimpleIoc.Default.Register<IDataService, DataService>();
//}
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public MainViewModel StaticMain
{
get
{
return _staticMain ?? (_staticMain = new MainViewModel());
}
}
private static MainViewModel _staticMain;
public MainViewModel NewMain
{
get
{
return new MainViewModel();
}
}
public MainViewModel NewIocMain
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>(Guid.NewGuid().ToString());
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
The Main property uses Lauent's SimpleIoc container to create an instance. One of the properties of this container is, that it treats every type as singleton. Thus if you use this VM generation method you will share the VM throughout the views.
The StaticMain property does much the same, but instead of using Laurent's container it holds a static instance of the VM which is also shared between the views.
The NewMain property creates a new VM upon every call, therefore, the VM is not shared between the views.
The NewIocMain property also creates a new VM upon every call and the VM is, therefore, not shared between the views. However, the SimpleIoc container holds a reference to the instance created. It does not release this instance automatically and you have to call SimpleIoc.Default.Unregister(key) with the key you used for creation (the Guid) to remove the instance from the container once you no longer need it.
Instead of using the SimpleIoc you obviously can opt to use another IOC Container - such as Unity for example - that allows you greater control how your instances are created and how long they live. Barring this, I'd opt for the NewMain approach given yor case.
We have an old Silverlight UserControl + WCF component in our framework and we would like to increase the reusability of this feature. The component should work with basic functionality by default, but we would like to extend it based on the current project (without modifying the original, so more of this control can appear in the full system with different functionality).
So we made a plan, where everything looks great, except one thing. Here is a short summary:
Silverlight UserControl can be extended and manipulated via ContentPresenter at the UI and ViewModel inheritance, events and messaging in the client logic.
Back-end business logic can be manipulated with module loading.
This gonna be okay I think. For example you can disable/remove fields from the UI with overriden ViewModel properties, and at the back-end you can avoid some action with custom modules.
The interesting part is when you add new fields via the ContentPresenter. Ok, you add new properties to the inherited ViewModel, then you can bind to them. You have the additional data. When you save base data, you know it's succeeded, then you can start saving your additional data (additional data can be anything, in a different table at back-end for example). Fine, we extended our UserControl and the back-end logic and the original userControl still doesn't know anything about our extension.
But we lost transaction. For example we can save base data, but additional data saving throws an exception, we have the updated base data but nothing in the additional table. We really doesn't want this possibility, so I came up with this idea:
One WCF call should wait for the other at the back-end, and if both arrived, we can begin cross thread communication between them, and of course, we can handle the base and the additional data in the same transaction, and the base component still doesn't know anything about the other (it just provide a feature to do something with it, but it doesn't know who gonna do it).
I made a very simplified proof of concept solution, this is the output:
1 send begins
Press return to send the second piece
2 send begins
2 send completed, returned: 1
1 send completed, returned: 2
Service
namespace MyService
{
[ServiceContract]
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service1
{
protected bool _sameArrived;
protected Piece _same;
[OperationContract]
public Piece SendPiece(Piece piece)
{
_sameArrived = false;
Mediator.Instance.WaitFor(piece, sameArrived);
while (!_sameArrived)
{
Thread.Sleep(100);
}
return _same;
}
protected void sameArrived(Piece piece)
{
_same = piece;
_sameArrived = true;
}
}
}
Piece (entity)
namespace MyService
{
[DataContract]
public class Piece
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string SameIdentifier { get; set; }
}
}
Mediator
namespace MyService
{
public sealed class Mediator
{
private static Mediator _instance;
private static object syncRoot = new Object();
private List<Tuple<Piece, Action<Piece>>> _waitsFor;
private Mediator()
{
_waitsFor = new List<Tuple<Piece, Action<Piece>>>();
}
public static Mediator Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
_instance = new Mediator();
}
}
return _instance;
}
}
public void WaitFor(Piece piece, Action<Piece> callback)
{
lock (_waitsFor)
{
var waiter = _waitsFor.Where(i => i.Item1.SameIdentifier == piece.SameIdentifier).FirstOrDefault();
if (waiter != null)
{
_waitsFor.Remove(waiter);
waiter.Item2(piece);
callback(waiter.Item1);
}
else
{
_waitsFor.Add(new Tuple<Piece, Action<Piece>>(piece, callback));
}
}
}
}
}
And the client side code
namespace MyClient
{
class Program
{
static void Main(string[] args)
{
Client c1 = new Client(new Piece()
{
ID = 1,
SameIdentifier = "customIdentifier"
});
Client c2 = new Client(new Piece()
{
ID = 2,
SameIdentifier = "customIdentifier"
});
c1.SendPiece();
Console.WriteLine("Press return to send the second piece");
Console.ReadLine();
c2.SendPiece();
Console.ReadLine();
}
}
class Client
{
protected Piece _piece;
protected Service1Client _service;
public Client(Piece piece)
{
_piece = piece;
_service = new Service1Client();
}
public void SendPiece()
{
Console.WriteLine("{0} send begins", _piece.ID);
_service.BeginSendPiece(_piece, new AsyncCallback(sendPieceCallback), null);
}
protected void sendPieceCallback(IAsyncResult result)
{
Piece returnedPiece = _service.EndSendPiece(result);
Console.WriteLine("{0} send completed, returned: {1}", _piece.ID, returnedPiece.ID);
}
}
}
So is it a good idea to wait for another WCF call (which may or may not be invoked, so in a real example it would be more complex), and process them together with cross threading communication? Or not and I should look for another solution?
Thanks in advance,
negra
If you want to extend your application without changing any existing code, you can use MEF that is Microsoft Extensibility Framework.
For using MEF with silverlight see: http://development-guides.silverbaylabs.org/Video/Silverlight-MEF
I would not wait for 2 WCF calls from Silverlight, for the following reasons:
You are making your code more complex and less maintainable
You are storing business knowledge, that two services should be called together, in the client
I would call a single service that aggreagated the two services.
It doesn't feel like a great idea to me, to be honest. I think it would be neater if you could package up both "partial" requests in a single "full" request, and wait for that. Unfortunately I don't know the best way of doing that within WCF. It's possible that there's a generalized mechanism for this, but I don't know about it. Basically you'd need some loosely typed service layer where you could represent a generalized request and a generalized response, routing the requests appropriately in the server. You could then represent a collection of requests and responses easily.
That's the approach I'd look at, personally - but I don't know how neatly it will turn out in WCF.