|
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')
|
|
|
|
|
Hello,
I'm new in Silverlight and have 2 questions:
1) I wrote some function in WPF. It looks as follows:
void F()
{
List<string> lstSubsystems = client.GetSubsystemsByTestRunID(testRunID).ToList();
foreach (string subsystemName in lstSubsystems)
}
As you can see, I call some function from WCF service, which returns the list. After that I use that list further. Of course, it works fine.
There's a problem in Silverlight. I'm trying to do this:
List<string> lstSubsystems = null;
void F()
{
client.GetSubsystemsByTestRunIDCompleted +=
new EventHandler<GetSubsystemsByTestRunIDCompletedEventArgs>(client_GetSubsystemsByTestRunIDCompleted);
client.GetSubsystemsByTestRunIDAsync(testRunID);
foreach (string subsystemName in lstSubsystems)
}
private void client_GetSubsystemsByTestRunIDCompleted(object sender, GetSubsystemsByTestRunIDCompletedEventArgs e)
{
lstSubsystems = new List<string>();
if (e.Result != null)
lstSubsystems = e.Result;
}
But, the last function is not executed.
How can I do that in Silverlight?
2) How can I sort the items under some node in Silverlight tree view?
Thank you in advance.
Regards,
Goran
modified on Monday, February 14, 2011 5:41 AM
|
|
|
|
|
When you say the last function is not executed, what exactly do you mean? Do you mean that the callback is never reached, or that the foreach statement doesn't happen? Your logic in the calling code is flawed because you make a call out to an asynchronous service and then continue processing (the foreach part) before the asynchronous code has had a chance to complete; at this point, lstSubsystems will still be null so you will be getting an exception which (I suspect) is being consumed by the unhandled exception method in your app class. If you must do something with lstSubsystems, you have to put it in the code that is guaranteed to be executed after the asynchronous operation has completed.
|
|
|
|
|
Yes. You're right. The flow of program is going further and doesn't wait for callback to return the list. I don't know how to handle this.
|
|
|
|
|
Well, you've already got your asynchronous code in place - put the foreach in there if you must do something with it, it's the logical place after all.
|
|
|
|
|
Excuse me, but I don't understand you. This is only small part of big method. I mean on method F(). I have 2 WCF service calls more in that method and a lot of data crunching. I'd like to keep that method as I did in WPF application.
|
|
|
|
|
See the discussion above on messaging, you are running up against the async problem in SL, you need to have you callbacks raise a message and when both messages have completed then you need to continue processing.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Thank you. I know that in theory. But, I can't find any code sample that could help me practically.
Simply, what should I do in my case? It's very simple. I call one method from WCF service and that method returns the list of strings. It looks like this in WPF:
List<string> lstSubsystems = client.GetSubsystemsByTestRunID(testRunID).ToList();
The call from WCF sevice above is only part of big method. In that big method, I have more WCF service calls after and they also return something. Therefore, it means, I need all those returning objects to be in that big method. It works normally in WPF application. But, it doesn't work in Silverlight application in that way. Yes, I wrote these 2 lines in Silverlight application:
client.GetSubsystemsByTestRunIDCompleted +=
new EventHandler<GetSubsystemsByTestRunIDCompletedEventArgs>(client_GetSubsystemsByTestRunIDCompleted);
client.GetSubsystemsByTestRunIDAsync(testRunID);
And I have callback method:
private void client_GetSubsystemsByTestRunIDCompleted(
object sender, GetSubsystemsByTestRunIDCompletedEventArgs e)
{
lstSubsystems = new List<string>();
if (e.Result != null)
lstSubsystems = e.Result;
}
But, the callback method isn't executed. It's too late. The program flow is going further.
So, I know what the problem is, but I don't know how to handle it in code. I've tried a few things I found on internet, but it doesn't help. This is really basic thing in programming - call the method from another method and use the returning object further. How can I accomplish it in Silverlight?
Thank you in advance.
|
|
|
|
|