I'm using this :
Messenger.Default.Send<NameMessage>(new NameMessage("Test"));
This message is register in two View models.
ViewModel 1 :
Messenger.Default.Register<NameMessage>(this, MethodInMyViewModel1);
ViewModel 2 :
Messenger.Default.Register<NameMessage>(this, MethodInMyViewModel2);
When I send the message, the Two Methods are called.
But I have an Instance of my view at each time.
So the application stop.
How can I call a specific view model with the same message ?
Assuming you are talking about the mvvmlight messenging namespace, you could always send the same message with different value based on which VM you are targeting, so each VM should register like so (the one bellow is for VM1):
Messenger.Default.Register<NameMessage>(this, (m) =>
{
switch (m.Value)
{
case "VM1":
MethodInMyViewModel1();
break;
}
});
Do the same for VM2, and when sending the message pass the appropriate targeted VM message to the NameMessage Value.
Related
I am having a strange issue with MVVM Light messaging. I am using it through my application and it has worked fine. I am now having a problem where my message is received roughly 10% of the time I try to send it. I'm assuming it gets shot off in to oblivion the other 90% of the time and I receive no errors and my application goes on as if nothing happened.
What I have is a context menu in a view that has a collection of statuses. Each user has a status and you can change it via this context menu. Every time the status changes I need to broadcast a message to another view model. This is the message that fails. Here is some code, feel free to ask me more questions if this is not clear enough for you.
This is located in my constructor, fired from a context menu item click:
this.UpdateUserStatus = new RelayCommand<UserStatusVM>(x =>
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
UpdateStatus(x);
}));
UpdateStatus method from relay command:
private void UpdateStatus(UserStatusVM status)
{
var userStatusEventId = _userService.CreateUserEvent(status.Id, CurrentUser.Id);
//This returns a business object that I would like to send
//over the wire to the other view model
var userEvent = _userService.GetUserEvent(userStatusEventId);
if (userEvent != null)
{
Messenger.Default.Send<UserEvent>(userEvent, "Broadcast");
}
//These fire off a collection changed event
CurrentUser.StatusId = status.Id;
CurrentUser.Status = status.Name;
CurrentUser.UpdatedDate = DateTime.Now;
CurrentUser.IsOnDuty = UserStatuses.IsOnDuty(status.Id);
}
and the message reception in the other view model:
Messenger.Default.Register<UserEvent>(this, "Broadcast", (userEvent) =>
{
proxy.Invoke("NewUserEvent", userEvent);
});
I am working on a wpf application using mvvm light toolkit. Whenever something goes outside the business logic we'll prompt the user with the message box and I send the message from view model to view using
Messenger.Default.Send(Token,"Some text message here");
Now I am writing test cases for view models and in some cases, Code Under Test is linked with such message calls. These are exceptions to me but test cases does not treat them exception as long as it is not being called by throw exception("message")
Suggestions.
Assuming you are sending message from VM to View, which handles the actual "showing logic", then just register for the message in your VM tests and verify that it has been sent/received. For example:
[TestMethod]
public void SendSomethingBadHappenedMessageTest()
{
const string expected = "oh noes!";
string actual = null;
// Register for message to ensure message was sent from VM
Messenger.Default.Register<SomethingBadHappenedMessage>(this,
message => actual = message.Message);
// Assuming command triggers Messenger.Send
_viewModel.SomethingBadHappenedCommand.Execute(expected);
Assert.AreEqual(expected, actual);
}
If you test for exceptions, just mark test method with ExpectedException attribute.
I assume you are trying to verify that the business logic detects a problem and the correct message box is being "shown" in your test case. If that's so, here's what I do:
Instead of using messaging, create a UserNotificationService and IUserNotificationService interface. The concrete implementation would be something like:
public class UserNotificationService : IUserNotificationService
{
public void MessageBox(string message)
{
// code to make the message box pop up goes here
}
}
Inject IUserNotificationService in your view model's constructor (you already have SimpleIoC since you are using mvvm-light) and use it to communicate with the user.
When unit testing, either mock the IUserNotificationService or create a new FakeUserNotificationService class that is capable of verifying the correct message/error was sent. Put that in the constructor of the view model being tested.
public class FakeUserNotificationService : IUserNotificationService
{
public void MessageBox(string message)
{
LastMessage = message;
}
public string LastMessage {get; set;}
}
In your test:
var ns = new FakeUserNotificationService();
var viewModel = new MyViewModel(ns);
viewModel.DoSomethingBad();
Assert.AreEqual(ns.LastMessage, "expected error message");
I have some wicket panel store in a static Hashmap from different sessions, i want to do some like if some panel notifies the map, then the map notifies all other panel.
for example:
public class PanelMap{
private static Map<Long, List<MyPanel>> map = new HashMap<Long, List<MyPanel>>();
public static void subscribe(Long id, MyPanel panel){
if (!map.containsKey(id)){
map.put(id, new ArrayList<MyPanel>());
}
map.get(id).add(panel);
}
}
public static void notify(Long id, String notification){
if (map.containsKey(id)){
List<MyPanel> panels = map.get(id);
for(MyPanel panel : panels){
panel.newNotification(notification);
}
}
}
}
In Panel, newNotification(String notification) i want to send request to server and refresh my panel in browser.
public void String newNotification(String notification){
// do some business logic depends on notification
onMyRequest();
}
i've made some search among wicket behavior source files and about i found in AbstractDefaultAjaxBehavior i tried to make my own onRequest method inside my wicket panel as follows
private void onMyRequest(){
AjaxRequestTarget target = ((WebApplication)getApplication()).newAjaxRequestTarget(getPage());
target.add( _some_wicket_components_ );
RequestCycle.get().scheduleRequestHandlerAfterCurrent(target);
}
but all i did is some Ajax error in Wicket Ajax Debug about
Wicket.Ajax.Call.processComponent: Component with id _containerdiv_ was not found while trying to perform markup update.
ERROR: Cannot find element with id: _someComponentIdOnPanel_
(those components are exist)
How could i send my own request to server (or how can i get valid AjaxRequestTarget to update my components? )
Update: I need inter-session communication.
To update panels on different user's sessions, you obviously can't use the current AjaxRequestTarget as this in a way represents a single communication between the server and the requesting user of which another user's Browser has no way of knowing. (Very very basically spoken)
You could either use an AjaxSelfUpdatingTimerBehavior to poll for updates. This would generate new AjaxRequestTarget for every user at regular intervals that you can use to attach changed panels to. It's a very basic and simple implementation that will most likely impact your systems performance and generate quite some traffic.
The other way would be to use something like Atmosphere, which is supported by Wicket-Atmosphere (quickstart can be found here) and has some examples over at the wicket-library.com, but that's all I know about this.
Use Wicket event bus system. Have a look to the "Wicket events infrastructure" chapter of the free Wicket guide.
First you need to create one class to encapsulate the notification and the AjaxRequestTarget and pass them using the events infrastructure.
private class Notification {
private String message;
private AjaxRequestTarget target;
... constructor, getters, setters...
}
Then the panels that want to recive the event have to override onEvent method, something like this:
public void onEvent(IEvent<?> event) {
if (event.getPayload() instanceof Notification) {
Notification notification = (Notification) event.getPayload();
... do whatever you want before updating the panel ...
// Update the panel
notification.getTarget().add(this);
}
}
All the components will recive all the events that are send using Wicket events infrastructure. So you can send the event from any other panel using one method like this
protected void sendMessage(String message, AjaxRequestTarget target) {
send(getSession(), Broadcast.BREADTH, new Notification(message, target));
}
Remember that if you want to update the components using AJAX, you need to set setOutputMarkupId(true). And if it's a component that can be hidden and you want to make it visible using AJAX, then you need to set setOutputMarkupPlaceholderTag(true).
I have a strange problem.
I'm using the MVVM Light framework from GalaSoft and everything so far works fine.
I'm using the messenger system to send messages between ViewModels just fine, until I tried to do the following:
I have a singleton class, GateKeeper, that sends messages.
This class is NOT a ViewModel and thus does not inherit from ViewModelBase.
If it sends a message it is not received anywhere.
I have tried the following:
Letting GateKeeper inherit from ViewModeBase -> No success.
Registering GateKeeper to receive messages, thus seeing if it would catch/receive the message that was actually sent from itself -> No Success.
Changing GateKeeper from Singleton to normal instantiation -> no Success
Creating a MVVM ViewModel that is not connected to a view, and letting it send messages, just like the GateKeeper -> no Success
All my viewModels that are connected to a view can send messages and they will be received.
It almost seems like a viewmodel must be "linked" to a view before the messenger works,but imo. that is a major limitation.
Below is the current, very simplified setup.
Calling ApplicationInitialize on GateKeeper does not trigger a message received on the mainviewmodel nor the GateKeeper class itselves.
I hope that someone has suggestions to this problem.
Thanks..
Example setup:
MainViewModel constructor:
public MainViewModel()
{
Messenger.Default.Register<LoadViewMessage>(this, (message) =>
{
if (message.Sender is GateKeeper) CurrentView = message.View;
else if (message.Sender is LoginViewModel) CurrentView = message.View;
else if (message.Sender is MenuItemBarViewModel) CurrentView = message.View;
});
GateKeeper:
public class GateKeeper : IGateKeeper
{
private readonly IEmployeeService _employeeService;
#region Implementation of IGateKeeper
public void ApplicationInitialize()
{
Messenger.Default.Send<LoadViewMessage>(new LoadViewMessage(ObjectLocator.MainMapView), this);
}
public void LoginSucceeded(Employee employee)
{
//This is where we retrieve the available services for the current employee
//TODO: add methods for retrieving service info from backend
//Send a message that should make the mainview load the map into its currentview property
Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView), this);
}
#endregion
public GateKeeper(IEmployeeService employeeService)
{
_employeeService = employeeService;
//Test.. Is not triggered
//Just used for debugging, thus nothing happens inhere.
Messenger.Default.Register<LoadViewMessage>(this, (message) =>
{
if (message.Sender is GateKeeper) ;
else if (message.Sender is LoginViewModel) ;
else if (message.Sender is MenuItemBarViewModel);
});
}
Message class: LoadViewMessage
public class LoadViewMessage : MessageBase
{
public UserControl View { get; protected set; }
public LoadViewMessage(UserControl view, object sender): base(sender)
{
View = view;
}
public LoadViewMessage(UserControl view):this(view, null){}
}
PS: ObjectLocator is a NinJect class that handles all instantiation of objects and their lifecycle
#UPDATE
LBugnion (Creator of MVVM Light) pointed out that the problem lied in the send method, where i was actually using a overload of Send that takes a token.
#This will not work in my situation
Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView), this);
#This WILL work
Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView, this));
this was supposed to be passed to the loadViewMessage and NOT the Send method as a token
Your problem is on the Send method. You are using the overload of the method that takes a token as second parameter. You are passing "this" as token. It means that you are (probably by mistake) using the sender of the message as token.
If you are sending with a token, you also need to register the receiver with the same token (in that case, the exact same instance than the one used in the Send method). Since you didn't register with a token, the message is never sent by the Messenger, which is an optimization mechanism.
My guess is that you misunderstood the usage of the token in the Send method. Tokens are only here as a way to build a "private messenging network" if you want, where two objects can use the same token to register/send and establish a private communication.
In your case, if you want to send the Sender together with the message, you'll need to save the Sender in the message itself, which is what MessageBase and the derived classes do.
Hope this helps
Laurent
I am currently developing a WCF Publish Subscribe service. The subscriber is a winform app. As the subscriber needs to implement the callback method for the service, which in my case is the PostReceived() method, and the publisher has the PublishPost() method.
For the PostReceived() method for my winform, it is unable to access the UI thread of my winform. The subscribe method is done on my main method. How do I program my PostReceived() method in such a way that it is able to access the labels and such of my mainForm?
EDIT
what I have tried so far is calling the mainForm object from my program.cs but it crashes when i run all 3 , stating the error that it is unable to access the UI thread.
EDIT 2
I have tried using the following code but there is an error for it.
mainForm b;
public void PostReceived(string postSampleData)
{
b.BeginInvoke((MethodInvoker)delegate()
{
b.lblSearch.Text = "lakjslkaja";
});
After running the code, there is an error of
Object reference not set to an instance of an object.
Any idea how to fix it?
Your PostReceived method should be something like this
void PostReceived()
{
yourform.BeginInvoke((MethodInvoker)delegate()
{
yourform.button.Text = "new label";
//More stuff here
});
}
This will guarantee that all the stuff after BeginInvoke is invoked in the UI thread.