|
Please don't hijack somebody else's thread. That's just impolite.
|
|
|
|
|
Hello,
I don't think it's impolite. It's normal communication between people. But, in any way, I won't do that in the future.
Regards,
Goran
|
|
|
|
|
It's not good form. The whole point of a threaded question is to answer the original posters problem, not to introduce something confusing into the mix. You have added nothing, other than a distraction, to the posters knowledge.
|
|
|
|
|
Again, not specifically WPF but ...
Most of the MVVM frameworks implement some sort of Messaging pattern to allow ViewModels to communicate.
Say VM1 selects a customer, it sends a "Customer Selected" message and VM2 responds to that message by displaying that customer's details.
All (that I have looked at) use a singleton Messenger object (the name varies) to which every VM has access and can subscribe to Messages or send messages.
And it all works fine and dandy.
Here's the bit I don't understand.
would it not be just as easy (if not easier) to use events? Isn't this just what the messenger is duplication?
So VM1 selects a customer and tells an "Eventor" singleton to raise the "Customer Selected" event. VM2 has subscribed to this event, and so displays that customer's details when it is raised.
I feel sure that there's a good reason for not doing this, but I'm stuffed if I can think what it is?
(Just to show I'm not a complete doofus, I am aware that most Messenger implementations avoid potential memory leaks by using WeakReferences, which may be an advantage - but is that the only reason for using it?)
Thanks
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
_Maxxx_ wrote: (Just to show I'm not a complete doofus, I am aware that most Messenger implementations avoid potential memory leaks by using WeakReferences, which may be an advantage - but is that the only reason for using it?)
No, that's not the only reason - in fact, routed events use weak references internally as well.
_Maxxx_ wrote: I feel sure that there's a good reason for not doing this, but I'm stuffed if I can think what it is?
Decoupling. Purely and simply, decoupling. With event subscription you create an implicit hard relationship between the subscriber and the subscribee, so your ViewModels end up having knowledge of each other, which makes testing much more problematic. If you accept that one of the key tenets of MVVM is increasing testability then you see that having a mechanism that allows you to mock out the source of the "event" makes it much easier to test the VM.
I hope that helps.
|
|
|
|
|
I posted my question 7 minutes ago and still don't have a reply! send codez plz!
Wow - quick reply - thanks Pete...
Pete O'Hanlon wrote: . With event subscription you create an implicit hard relationship between the subscriber and the subscribee
true - but in my scenario the VM dosn't raise an event - it tells another (singleton) object (the equivalent of the Messenger which I called Eventor) to raise the event - so the only hard relationship is between VM and Eventor.
Pete O'Hanlon wrote: If you accept that one of the key tenets of MVVM is increasing testability
I do - and maybe that's where my knowledge is failing, as I haven't got my head around TDD yet.
Pete O'Hanlon wrote: having a mechanism that allows you to mock out the source of the "event" makes it much easier to test the VM.
Any reason why I can't mock something that raises an event just as easily as mocking something that sends a message?
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
Message/Event. It's up to you how you want to do it. A message is a lighterweight method of communication, so that's why it's popular. Have a look at what Marlon and I did in MefedMVVM[^] to see how we used a drop in Mediator for VM communication
|
|
|
|
|
Pete O'Hanlon wrote: Message/Event. It's up to you how you want to do it.
Ahhrghhh - don't tell me that! I want to be told "this is the best way to do it!" Now I need to go look at more stuff!
Pete O'Hanlon wrote: lighterweight
Some sort of boxing terminology?
Pete O'Hanlon wrote: Have a look at what Marlon and I did in MefedMVVM[^]
Noooo - more frameworks?!
Seriously, though, thanks for the help. I'm getting there.
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
Personally I try to avoid messages.
Yes, it gives a view model that is easier to write tests against, but it does it at a cost:
1) The compiler (and refactoring tools) are unaware of messages. Basic errors normally displayed at compile time are now runtime errors.
2) When debugging, the event's are typically delivered without any stacktrace showing where the call came from (at least in the toolkit I have been forced to use) making simple debugging operations complex.
Personally I find the price too high and I avoid using the messages. It would be nice if we could get weak event subscriptions though.
|
|
|
|
|
lmoelleb wrote: 1) The compiler (and refactoring tools) are unaware of messages. Basic errors normally displayed at compile time are now runtime errors.
That depends on the message type. If you use an enumeration for the message, then you get compile time checking. Yes, string based messages are prone to code smell.
|
|
|
|
|
Pete O'Hanlon wrote: string based messages are prone to code smell
Ah so now I have stinky code huh. I got the impression that Silverlight does not like enums, I think they did not cross from VM to View or something.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
It can't be Silverlight per se - must be whatever MVVM Framework you're using. After all, an enum is just an integer by any other name.
Personally I was thinking (if I use messaging rather than events) of using a message class rather than a string- although the overheads of defining a class for every message seems like overkill to me...
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
_Maxxx_ wrote: It can't be Silverlight per se
I think it is SL as I ran across some issues trying to parse an enum in the code behind of a view and dropped enums from the SL app for this reason. I can't remember the details so I will have to do some more investigation.
Prompted by Pete's comments I will now spend some more time with enums. B/C of delivery pressures I tend to drop a problem issue for later investigation if there is a Q&D work around (strings fitted this area).
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
A few things I didn't see mentioned in responses:
1) In *proper* 100% by the book mvvm, ViewModels shouldn't known about each other. With that being said, how could they subscribe to events?
2) You could put all your message types in a central place for convenience. Or make static public members. Same thing. There are pros and cons to both.
3) I've never seen a messenger class with weak references. What framework was that in? Usually they do something like Messenger.GetMessage(messageType).Subscribe(callback) or Messenger.GetMessage(messageType).Handler += callback.
The one I wrote doesn't use weak references, but it handles "bad" handlers by removing the subscription. I think thats a bigger concern then mem leaks. Also, async is a big deal. No point in calling one callback and waiting for it to finish (most messenger classes I've seen do this!) before calling the next.
|
|
|
|
|
SledgeHammer01 wrote: ViewModels shouldn't known about each other. With that being said, how could they subscribe to events? Smile
They don't need to know about each other. VM1 has a reference to some singleton - let's call it Eventor. VM2 also has a reference to Eventor (in fact, all VMs have a reference to this singleton).
VM1 tells Eventor "Please let anyone who is interested know that I just selected this Customer" and passes the customer in question (or the ID as an integer, or something to identify the customer).
Eventor has a method LetAnyoneWhoIsInterestedKnowThatThisCustomerWasSelected()
This method raises the "CustomerSelected Event.
VM2 is a subscriber to this event, and so handles it.
VM1 knows nothing of VM2 which knows nothing of VM1.
SledgeHammer01 wrote: 2) You could put all your message types in a central place for convenience. Or make static public members. Same thing. There are pros and cons to both.
Yep - I started doing this using a Messenger implementation that used strings. Trouble was I wanted to pass around an IMessenger so didn't have access to strings in the concrete Messenger class (I was,perhaps, being pedantic using IMessenger though)
SledgeHammer01 wrote: What framework was that in?
I think MVVM Light does, as does MVVM Foundation
SledgeHammer01 wrote: handles "bad" handlers by removing the subscription.
Not sure I follow - how do you know they're bad??
SledgeHammer01 wrote: I think thats a bigger concern then mem leaks.
Not for me - I've worked on projects with big memory leak issues where events have been used across classes and have failed to be removed - over a period, memory usage can grind applications like this to a halt (in the instance to which I allude, the lookup Views failed to be garbage collected due to references kept alive by event handlers not unsubscribing)
SledgeHammer01 wrote: Also, async is a big deal. No point in calling one callback and waiting for it to finish
Good point - I guess if several VMs subscribe to a message/event or whatever, firing their handlers asynchronously would be sensible in most cases - but how would you do that? One thread per handler?
Thanks, SledgeHammer, for taking the time to respond - I'd be interesting in seeing your framework if it is available for public perusal.
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
_Maxxx_ wrote:
They don't need to know about each other. VM1 has a reference to some singleton - let's call it Eventor. VM2 also has a reference to Eventor (in fact, all VMs have a reference to this singleton).
VM1 tells Eventor "Please let anyone who is interested know that I just selected this Customer" and passes the customer in question (or the ID as an integer, or something to identify the customer).
Eventor has a method LetAnyoneWhoIsInterestedKnowThatThisCustomerWasSelected()
This method raises the "CustomerSelected Event.
VM2 is a subscriber to this event, and so handles it.
VM1 knows nothing of VM2 which knows nothing of VM1.
Yeah, so you need to do a lot of stuff manually (from your description -- unless I misunderstood). To me it sounds like Messenger is a more organized / compact / cleaner solution. Nobody needs to keep a ref to anything.
_Maxxx_ wrote:
Yep - I started doing this using a Messenger implementation that used strings. Trouble was I wanted to pass around an IMessenger so didn't have access to strings in the concrete Messenger class (I was,perhaps, being pedantic using IMessenger though)
In most of the frameworks I've seen, a singleton Messenger instance is exposed somewhere. What I did in mine was make it a "service" (as in the service locator pattern) and make the ServiceLocator a public static member of ViewModelBase:
IMessengerService msg = ViewModelBase.ServiceLocator.GetService<IMessengerService>();
_Maxxx_ wrote:
I think MVVM Light does, as does MVVM Foundation
I'll have to check it again. Last time I looked, I think they were strong refs, but it was a while ago, so maybe I'm confused. I looked at a lot of frameworks before I did mine.
_Maxxx_ wrote:
Not sure I follow - how do you know they're bad??
I handle the case where they throw an exception and report it. Another case is where they hang, but I don't handle that case. A possible solution might be a timeout mechanism or something.
_Maxxx_ wrote:
Not for me - I've worked on projects with big memory leak issues where events have been used across classes and have failed to be removed - over a period, memory usage can grind applications like this to a halt (in the instance to which I allude, the lookup Views failed to be garbage collected due to references kept alive by event handlers not unsubscribing)
Well, w/ messenger you'd only have that problem if something is continously being re-created and resubbing. If your views only get created once and you sub once, well, you aren't going to really "leak".
_Maxxx_ wrote:
Good point - I guess if several VMs subscribe to a message/event or whatever, firing their handlers asynchronously would be sensible in most cases - but how would you do that? One thread per handler?
I don't create seperate threads because that would make GUI calls a pain. I get the callback invocation list and enum through and call them asynchronously. In the callback completion I catch any exceptions and report / unsub them.
_Maxxx_ wrote:
Thanks, SledgeHammer, for taking the time to respond - I'd be interesting in seeing your framework if it is available for public perusal.
Sorry, its part of my "bag of tricks" assembly. Plus, I don't really have patience to support it. I figure everbody was going with mvvmlight or cinch. I just wanted to write something light weight and understand ever line of code . Plus it was a good way to learn mvvm .
|
|
|
|
|
SledgeHammer01 wrote: you need to do a lot of stuff manually (from your description -- unless I misunderstood).
I think you misunderstood - although as I haven't written it yet, you may well be right
SledgeHammer01 wrote: I don't create seperate threads
SledgeHammer01 wrote: call them asynchronously.
Isn't an asynchronous method running on a different thread?
SledgeHammer01 wrote: Sorry, its part of my "bag of tricks"
No probs.
SledgeHammer01 wrote: I figure everbody was going with mvvmlight or cinch
.. or one of the seventy-eleven other frameworks
SledgeHammer01 wrote: something light weight and understand ever line of code
Exactly my aim - I don't want a Framework I want a methodology that I understand without 'black boxes'
SledgeHammer01 wrote: Plus it was a good way to learn mvvm
Again, just what I'm doing. - hence these odd questions from time to time.
Again, thanks for taking the time to respond.
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
SledgeHammer01 wrote: 1) In *proper* 100% by the book mvvm, ViewModels shouldn't known about each other. With that being said, how could they subscribe to events?
I thought that was stated implicitly by my statement: "With event subscription you create an implicit hard relationship between the subscriber and the subscribee, so your ViewModels end up having knowledge of each other, which makes testing much more problematic."
Hmmm, on rereading that I can see you're right; it didn't make it clear enough - even though that was the intention.
|
|
|
|
|
_Maxxx_ wrote: I feel sure that there's a good reason for not doing this, but I'm stuffed if I can think what it is?
The overhead (and the risk of potential memory leaks).
IMO, too many eventor's will be a hit on resources.
The funniest thing about this particular signature is that by the time you realise it doesn't say anything it's too late to stop reading it.
|
|
|
|
|
Abhinav S wrote: The overhead
Is an event a bigger overhead than a Message, then? (See, I'm clueless!)
Abhinav S wrote: the risk of potential memory leaks
Yes - that's the one that I fear most.
Abhinav S wrote: IMO, too many eventor's will be a hit on resources.
I'd anticipate only one Eventor - which is the equivalent of the singleton Messenger class - instead of routing messages it raises events - no great overhead (unless an Event carries more baggage than a Message)
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
_Maxxx_ wrote:
Is an event a bigger overhead than a Message, then? (See, I'm clueless!)
Well... a raw event, no. A basic event, no. Remember how msgr works. I won't count the registration / subscription overhead since thats neglible and happens once, but the Send function needs to lookup your internal struct by type every time to get the handlers. Not huge overhead, but still...
Honestly, if you have 100s of messages I think you are doing something wrong. There is always room for abuse. If you change every event to msgr, I don't think thats quite right.
If you have 5 to 10 messages, it shouldn't take much time...
If you are working on a portion of code that has to happen very quickly like 10,000x / sec or something, maybe msgr should not be used there.
Or, a super duper advanced messenger impl may allow you to aggregate events.
-- Modified Tuesday, February 15, 2011 3:37 PM
|
|
|
|
|
You said:
"Say VM1 selects a customer, it sends a "Customer Selected" message and VM2 responds to that message by displaying that customer's details"
The way I see it, the viewmodel's *only* job is to describe what the view looks like. It doesn't initialise itself nore does it try to look up any sort of information or whatever - so it most certainly will not listen to any kind of requests from other viewmodels.
Yes, I use events instead of messages. But my viewmodels only raise them (actually, they ask a singleton to raise them), they don't handle them.
Handling such events (looking up cust details etc) is done by the relevant controller of these models/views. The controller will look up the cust details (either through a WCF or business call) and initialise the relevant viewmodels from the received results.
My viewmodels do NOT communicate with eachother and imo, they shouldn't. Viewmodels communicate only with the views they represent and they are initialised by the relevant controller only.
|
|
|
|
|
BubingaMan wrote: the viewmodel's *only* job is to describe what the view looks like.
? Au contraire, surely? The single thing the Vm doesn't do is to describe what the view looks like!
BubingaMan wrote: It doesn't initialise itself nore does it try to look up any sort of information
Assuming your View is databound to your ViewModel, then your ViewModel needs to get its data from somewhere - and do something with it if it is updated??
BubingaMan wrote: Handling such events (looking up cust details etc) is done by the relevant controller of these models/views. The controller will look up the cust details (either through a WCF or business call) and initialise the relevant viewmodels from the received results.
So, your VM raises an event (via some singleton) which is handled by some controller which is in a 1-1 relationship with a VM - and deals with the event. Do I follow correctly?
So User selects item in View V1 which is bound to VM1. The VM1 calls a method on EventHandler singleton - say CustomerSelected(CustomerId) - which raises a CustomerSelected event.
Controller2 has previously subscribed to this event via the singleton, and handles it by getting data and calling a method/setting a property on VM2.
Sounds good.
So you have a three-way collaboration? VMn, Vn and Cn with a Singleton as the One Ring to Bind Them All?
BubingaMan wrote: My viewmodels do NOT communicate with eachother and imo, they shouldn't.
I can see your point of view, but if my assumption regarding the 1-1 relationship between VM and C, there's really little difference, is there? Don't get me wrong, I entirely agree, but tend to design a 'pull' methodology rather than 'push' (i.e. my VM says "I need this customer's info" to the controller, rather than the Controller telling the VM "Edit this data"
I also tend toward a single, singleton Controller which, I think, takes the place of your Singleton event handler and many Controllers.
Thanks for your input.
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|
_Maxxx_ wrote: ? Au contraire, surely? The single thing the Vm doesn't do is to describe what the view looks like!
Sure it does. The data that is shown is (in my interpretation) is part of what the view looks like.
How many times did you bind a control's visibility property to a bool for example?
_Maxxx_ wrote: Assuming your View is databound to your ViewModel, then your ViewModel needs to get its data from somewhere - and do something with it if it is updated??
That's what a controller is for.
_Maxxx_ wrote: a 1-1 relationship with a VM - and deals with the event
Not necessarily 1-1, but yes.
_Maxxx_ wrote: So you have a three-way collaboration? VMn, Vn and Cn with a Singleton as the One Ring to Bind Them All?
Something like that, yeah. It's just MVC with the MV being MVVM
It's very easy to write tests as well. Like I said previously, we see the model object as being descriptive of the GUI. It's a state. The controller is the behavior. We write controller tests to test the behavior and additionally, we assert the state of the models. So we can actually test our GUI to an extent. To me, the model object is an integral part of the GUI.
_Maxxx_ wrote: I also tend toward a single, singleton Controller which, I think, takes the place of your Singleton event handler and many Controllers
Doesn't that result in a giant class? Also, it's not always 1-1. Lots of times, 1 controller will have multiple models (each model binding to a part of the screen).
Where do you do your business calls then? In the one controller or does the model itself do that?
I should also add that I have a base class for controllers wich has quite some standard functionality. Like threading for business calls while it automatically activates busy indicators on the relevant presentations etc.
For line of business apps, this works quite well.
|
|
|
|
|
BubingaMan wrote: Doesn't that result in a giant class?
Yes - that's the downside - but I split it into partial classes for ease. While it isn't particularly elegant, for medium sized projects it works well - every Vm has a reference to it (via an interface) and it is very easy to add functionality to a VM that has already been developed for another VM without the need to use multiple controllers for a single VM, for example.
BubingaMan wrote: Also, it's not always 1-1. Lots of times, 1 controller will have multiple models (each model binding to a part of the screen).
I suspect I would move toward that model in larger systems; my single controller would maintain a relationship with one area of the application - for example I would probably have an "Administrator" controller handling all of the maintenance and set up, a "reporting" controller, and an "Application" controller.
BubingaMan wrote: Where do you do your business calls then? In the one controller or does the model itself do that?
The Controller responds to requests form the VM to retrieve or store data, and does so via service calls. The controller, then, also handles the raising of events (or the sending of messages) that can be handled by other View Models
e.g.
VM1 has a list of customers.
VM2 edits customer details.
VM1 allows filtering of the list, so calls a method on the Controller to provide a suitably filtered list of Customers (in fact it provides a collection of 'cut down' customer objects containing minimal information for displaying in a list)
On selection of an item for editing, VM1 tells the controller that this event has occurred (via messaging, method call, event or whatever)
Controller instantiates (if it doesn't already have one) a CustomerEdit V and VM and uses a service to get the Customer object for that customer, which it provides to the VM (in this case, I guess, I do use the 'push' method rather than getting the VM to ask for a customer to maintain)
Vm then handles the Save command form the V by asking the controller to save this Customer.
The Controller now can raise an event (or send a message) that a) this customer has changed and b) the collection of customers has changed.
VM1 has subscribed to the event/message regarding customer collections changing - and so asks the Controller for an updated list of customers to display.
(Sorry - very long answer to pretty short question!)
When you ask
BubingaMan wrote: How many times did you bind a control's visibility property to a bool for example?
I think we're talking more or less the same thing. Yes I probably bind to a boolean - but the property in my VM is never called "Visible" - it may be "CustomerHasOutstandingOrders" or something (actually, in that case I would probably have a property of "int NumberOfOutstandingOrders" which would be bound to via a converter.
The point being that the VM is designed to say
"We need to make the User aware if a customer has outstanding orders"
Now the designer my choose to simply make the Outstanding Order grid visible or not. The users then would say they hate that, because too much real estate is taken up with the list when it is visible, for the small number of times they view it. So we re-engineer the view (and the view only) to instead have a button - Show Outstanding Orders - which the users want to be visible at all times, but disabled when there are none.
Easily done - and we're still binding to "CustomerHasOutstandingOrders" not to OutstandingOrdersVisible"
So I don't see that my VM is determining what the view looks like.
If I may permitted to harp on a bit - one of the first MVVM projects I did, jsut for playing, showed a clock
The VM had properties of Hours, Minutes and Seconds.
I then developed an AnalogClockView, a DigitalClockView and a WierdClockView
My point is that all three clock Views showed the same information, but the 'look' was entirely different - and in no way determined by the VM which merely stated "The user needs to be able to see the current time"
I suspect we're both saying more or less the same thing, here, - but I'm just more verbose (as can be seen by the message I get when posting this to CP - my apologies for those with slow connections!)
___________________________________________
.\\axxx
(That's an 'M')
|
|
|
|
|