|
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.
|
|
|
|
|
With more details.
I have this code in big method:
private void F()
{
DatabaseServiceClient client = new DatabaseServiceClient();
client.GetSubsystemsByTestRunIDCompleted +=
new EventHandler<GetSubsystemsByTestRunIDCompletedEventArgs>(client_GetSubsystemsByTestRunIDCompleted);
client.GetSubsystemsByTestRunIDAsync(testRunID);
treeAdditionalInfo.Items.Clear();
foreach (string subsystemName in lstSubsystems)
{
TreeViewItem subsystemItem = new TreeViewItem();
subsystemItem.Header = subsystemName;
subsystemItem.FontSize = 14;
treeAdditionalInfo.Items.Add(subsystemItem);
}
List<int> lstIDsForForms = new List<int>();
client.GetFormsAndActionsCompleted +=
new EventHandler<GetFormsAndActionsCompletedEventArgs>(client_GetFormsAndActionsCompleted);
client.GetFormsAndActionsAsync(testRunID);
...
}
The code in first 2 callbacks is this:
private void client_GetSubsystemsByTestRunIDCompleted(
object sender, GetSubsystemsByTestRunIDCompletedEventArgs e)
{
lstSubsystems = new List<string>();
if (e.Result != null)
lstSubsystems = e.Result;
}
private void client_GetFormsAndActionsCompleted(
object sender, GetFormsAndActionsCompletedEventArgs e)
{
lstFormsAndActions = new List<KeyValuePair<string, List<TestLogSmall>>>();
if (e.Result != null)
lstFormsAndActions = e.Result;
}
I need to get the list after this call:
client.GetSubsystemsByTestRunIDAsync(testRunID);
And after that to get the list after this call:
client.GetFormsAndActionsAsync(testRunID);
How can I do that? What should I write?
Thank you.
|
|
|
|
|
In the first call back you can then call the next query and repeat for the third. This is not a good solution if those queries are called for any other operation than this one.
if (e.Result != null)
{
lstFormsAndActions = e.Result;
}
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
You've undestood my problem very well. Thanks.
I've already tried it and it doesn't work for me. Also, the body of method is "everywhere", which is basic incorrectness.
|
|
|
|
|
Take a look at this reply [^]from Ian, it may help (and amuse)
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
It sounds like you have a fundamental architectural problem here. As you've just found out, Silverlight applications are not WPF applications, no matter how much they look like them. There are things you take for granted in WPF that you just can't do in the same way in Silverlight, which is why it is sometimes impossible to use the same code in both types of applications. If I were you, I'd take a long hard look at the underlying infrastructure and see what - if anything - you can do to change it to suit a more asynchronous style of programming. This is going to require you to have a think about how the application flow is meant to proceed. Only you can do this - we don't know the overall architecture of your system, and the constraints it has to operate in. Now that you know what the problem is, it's up to you.
|
|
|
|
|
Ok. I'm trying to adapt my code to be async-friendly in my Silverlight application, but new problem came up. I have this function in WCF service:
[OperationContract]
List<KeyValuePair<string, List<TestLogSmall>>> GetFormsAndActions(int testRunID);
It works fine in WPF application. But, in Silverlight application doesn't. It returns the correct number of items in the list, but they are empty. Does anybody know what could be the problem with KeyValuePair object?
Thank you in advance.
|
|
|
|
|
Ok.
I've made it work in Silverlight using async approach.
Thank you guys for your help.
|
|
|
|
|
Congratulations.
|
|
|
|
|
<ListBox Margin="1,1,0,0" Grid.Row="2" Grid.Column="2" Name="lstEntityType" VerticalAlignment="Top"
SelectedItem="Binding Path=SelectedEntityList,UpdateSourceTrigger=PropertyChanged" ItemsSource="{Binding Path=SearchEntitiesTypes, Mode=OneWay}"
Width="120" Height="59" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Left" Grid.RowSpan="2">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chklstEntityType" ClickMode="Press" IsChecked="true" Height="15" >
<CheckBox.Content>
<TextBlock Text="{Binding Path=SearchType}"></TextBlock>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="true"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
These checkboxes should be checked by default . THe checkboxes are linked to a property. I have used mvvm pattern.
|
|
|
|
|
Set the value of the property to true before the view loads.
Make sure INotifyPropertyChanged is set on this property.
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.
|
|
|
|
|
I'm trying to simulate a translucent tube with an object travelling through it. For simplicity, the tube is a rectangle and the object is a circle behind it.
I want the tube to be transparent in the middle and opaque on the edges so only the middle of the 'inside' object is clearly visible. I've got the tube's brush's OpacityMask set to a linear gradient - but it has no affect (can't see inside). I can lower the rectangle's Opacity property but that's not giving me the tube affect I'm looking for.
I'm using .NET 4.
Thanks,
Dan
<UserControl x:Class="SilverlightTest2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<LinearGradientBrush x:Key="TubeBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="White" Offset="0.459"/>
<GradientStop Color="White" Offset="0.604"/>
</LinearGradientBrush>
</UserControl.Resources>
<Canvas x:Name="LayoutRoot" Background="White">
<Ellipse Fill="Green" Height="40" Canvas.Left="336" Stroke="Black" Canvas.Top="41" Width="40" OpacityMask="{StaticResource TubeBrush}"/>
<Rectangle Fill="Blue" Height="39" Stroke="Black" Canvas.Top="41" Width="360" OpacityMask="{StaticResource TubeBrush}"/>
</Canvas>
</UserControl>
|
|
|
|
|
An easier solution would be to use the LinearGradientBrush for the FILL and use #aarrggbb syntax for the color stops. I.e. #ff00ff00 for opaque green and #0000ff00 for transparent green. 50% transparent green would obviously be #7f00ff00.
|
|
|
|
|
Thanks! I'm still curious as to why the OpacityMask did nothing but I can dig into that later.
Dan
|
|
|
|
|
I have no idea of both. But i want to learn.
So which one i can go for?
|
|
|
|
|