I'm having a right barney getting my head around how everything fits together using the MVVM pattern. It all seems quite simple in practice but trying to implement it I seem to be breaking various other rules that I try to code by.
Just as a side note, I'm trying to implement the pattern using Flex, not Silverlight or WPF, so if anyone can come with good reasons why this shouldn't be done, then I'd like to hear them.
I have a problem where I have several views. Sometimes I have to display two views on the page at the same time; sometimes I switch back to a single view. In my normal Flex brain I would have a main view with a code-behind which contained all my other views (equally with code-behinds). That main view would then do the switching of the other individual views.
When I try to implement this in MVVM I'm trying to stick to the principles of MVVM by using binding which decouples my Views from the ViewModels. Let's say I create a ViewModel for application-wide state and my ApplicationView binds to that data and does all the switching of the sub views.
Now, where should I create my view models for my subviews? I've tried inside the ApplicationView -- that didn't seem right. And then I've tried outside of the application view and passing and instance of it into the ApplicationView and then my sub models a bind to it. Am I missing something? None of these methods seem to fit the whole point of trying to decouple this.
Any good books or links that explain this problem would be much appreciated.
Cheers,
James
The approach you are referring to is ViewModel composition. Its where you have multiple complex view parts that need to bind to their own ViewModel entity. The approach entails constructing a root ViewModel with properties for each child ViewModel. Then the root View is bound to the root View Model and each View (whether displayed or collapsed) is bound to the corresponding property on the root ViewModel.
The ViewModel would look like this:
public class RootViewModel
{
ChildViewModelA ChildA { get; set; }
ChildViewModelB ChildB { get; set; }
}
The View would look like this:
<Grid>
<ChildViewA DataContext="{Binding ChildA}" />
<ChildViewB DataContext="{Binding ChildB}" />
</Grid>
You could also implement this in away to allow yourself to select an active workspace.
The ViewModel would look like this:
public class RootViewModel
{
public List<ViewModel> ChildWorkspaces { get; set; }
public ViewModel ActiveWorkspace { get; set; }
public RootViewModel()
{
ChildWorkspaces.Add(ChildViewModelA);
ChildWorkspaces.Add(ChildViewModelB);
}
}
The View would look like this:
<Grid>
<Grid.Resources>
<DataTemplate DataType="ChildViewModelA">
<ChildViewA />
</DataTemplate>
<DataTemplate DataType="ChildViewModelB">
<ChildViewB />
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding ActiveWorkspace}" />
</Grid>
This will result in the appropriate visual representation being selected implicity based on the type of the actual object stored in ActiveWorkspace.
Pardon my response was in WPF. I tried my hardest to not get caught up in the syntax of it all :-)
As you can see the plurality of "ViewModel" can be ambiguous. Often times we find the need to construct multiple sub-entities to structure the ViewModel appropriately. But all ViewModel entities would be somewhere within the root View Model object.
When implementing MVVM in WPF, I prefer to infer what visual element to apply data context implicitly (as illustrated in the later half of this response). In more complex scenarios I prefer to use a DataTemplateSelector to conduct that decisioning. But in super simple cases you can explicitly apply DataContext either imperatively in C#/ActionScript or declaratively through bindings.
Hope this helps!
I've seen variants of the MVVM approach used on a couple different Flex projects, but I haven't seen an approach that feels perfectly right to me. That said, I think using Presentation Models makes testing in Flex a lot easier, so I'm pretty sure that there will start to be more applications designed around this pattern.
The easiest approach I've seen to implementing MVVM in Flex is to place the individual ViewModels within the application Model / ModelLoactor. The ModelLoactor contains any global data and also serves as an accessor to all ViewModels. ApplicationViews can then bind to their particular ViewModel through the ModelLocator, while ViewModels can be updated both through Commands and through bindings to their parent ModelLocator. One benefit of this approach is that all of the data logic is localized; of course, this could also be seen as a drawback, with the central ModelLocator being a touch brittle due to its hard coded references to all ViewModels.
I've seen cleaner approaches work by using the Mate framework. Mate allows for a much more decentralized injection of ViewModels into the appropriate ApplicationViews. (I suppose this could also be accomplished with Swiz, I'm just not as familiar with that framework). With Mate, each ApplicationView has its ViewModel injected via a Map. What's cool with this approach is how ViewModels can be updated using an EventMap (the Mate version of a FrontController). Essentially, your ApplicationViews will dispatch events that are handled by one or more EventMaps, and these Maps can then make changes to one or more of the ViewModels. This approach allows for a user gesture or event from one ApplicationView to change the state of several ViewModels at once. In addition, because this logic is extracted into Mate's EventMaps, it's very easy to change how events are handled or which ViewModels are changed. Of course, the major drawback of this approach is that you're committing to using Mate as a framework, which may not be an option depending on the requirements of the project.
I hope that helps!
I wanted to share a comparison I wrote up of MVVM (Silverlight) vs PresentionModel (Flex). It shows how the two implementations of the same pattern differ/compare:
http://houseofbilz.com/archives/2010/12/29/cross-training-in-silverlight-flexmvvm-vs-presentation-model/
Related
I have just fired up a WPF project and I want to use Caliburn.Micro.
I have a button
<Button Content="Button" Name="AppendData">
and in my ViewModel I have a method void AppendData(){..}
It doesn't work! There is no binding between the two! But when I do this
<Button Content="Button" cal:Message.Attach="AppendData()">
it suddenly works. What can be the cause of this?
Edit:
I have created a test application where the conventions doesn't work: http://ge.tt/8sNsu201?c
You can make it work, by replacing the controls in MyView with
<Button cal:Message.Attach="SetText()" Content="Button" HorizontalAlignment="Left" Margin="106,153,0,0" VerticalAlignment="Top" Width="75"/>
<Label Content="{Binding Text}" HorizontalAlignment="Left" Margin="124,104,0,0" VerticalAlignment="Top"/>
After taking a look at your source code, I noticed a major mistake which is causing all of this confusion:
public MyView()
{
InitializeComponent();
DataContext = new MyViewModel(); // SOURCE OF TROUBLE
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
In Caliburn.Micro you don't set the DataContext for your view manually like that, instead you let Caliburn.Micro use its conventions to find the appropriate view for your view-model, then it will bind the two together (by setting the view-model as the DataContext of the view), after that it will apply a number of conventions to make everything work correctly.
Explaining why using cal:MessageAttach() would work and directly using AppendData won't work would take a lot of explanation because it seems you don't know the basics of CM.
So I advise you to take a look at the documentation wiki first and go through the first 5 articles at least, then here is a hint that will help you discover why the first method worked and the second didn't:
Message Bubbling
Because this would expand the comments maximum length, I write it as an answer.
As you mentioned in your answer, doing DataContext = new MyViewModel() is a kind of code smell in CM. If you want to hook up it manually in your view, this would be the right way (view first). Check out the CM documentation regarding this one though, because I think there might be missing something:
var viewModel = new MyViewModel();
var view = this;
ViewModelBinder.Bind(viewModel, view, null);
You can accomplish this in the XAML of your view, either. Add the following into the UserControl tag of your view (view first, as well):
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="MyViewModel"
View model first would be done quite the same, in case you are not willing to use the default behavior you described in your answer:
xmlns:cal="http://www.caliburnproject.org"
cal:View.Model="MyViewModel"
I am not sure, but I think you have to add an explicitly named export contract to your view model, if you want to use View.Model or Bind.Model, but it might be it works without as well. Try it out:
[Export("MyViewModel", typeof(MyViewModel))]
public class MyViewModel : Screen
{
// ...
}
Design time views have nothing to do with view first or view model first though!
Design-time view support is accomplished as follows:
xmlns:cal="http://www.caliburnproject.org"
d:DataContext="{d:DesignInstance viewModels:MyViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True"
I am currently not able to test all those things, so I hope there are not any mistakes!
I just started using Caliburn.Micro and I've noticed in all the examples that the methods are all public. I decided to test this by adding a button with:
x:Name="CloseMainWindow"
In my VM I added a method:
private void CloseMainWindow()
{
TryClose();
}
When I click the button, nothing happens and I don't hit the breakpoint, but if I change the method to public it works.
I can't see this being the best way to do this.
Would creating ICommand properties for all the methods be an acceptable solution?
Edit: I just read the answer to the question immediately above, there is not and never will be ICommands in Caliburn.Micro. So my original question still needs an answer, why does everything have to be public in the VM and is this safe?
I don't know what you mean by "is this safe?". Safer than what?
Anyway, Caliburn.Micro could have been designed to allow its conventions to bind to private methods, but that has a couple of drawbacks. First, it wouldn't work in partial-trust environments, like Silverlight or XBAPs or sandboxed plugins. You need full trust to use Reflection to access private members, and Caliburn.Micro is designed to be able to run in partial-trust (it does support Silverlight, after all).
But a bigger reason is that it would violate encapsulation. These are methods that you intend to be called from outside the class. (The view is a separate class, after all; you'd have to make the viewmodel method public if you were wiring it up yourself in the code-behind.) There's a word for "I intend to call this from outside my own class" in the language specification, and that's public. If you set up some magic that calls private methods from outside the class, you're violating both encapsulation and the Principle of Least Astonishment, because that's not what private means.
If you really want to be able to bind to private methods, you can customize the conventions. But it would make your code much harder to understand, so I wouldn't recommend it unless you can come up with a really good justification.
I want to create a user control, which can be bound to some data given from outside the control (requirement A), and some XAML properties must be bound to properties of the control itself (requirement B).
Let's say that I have a data class named StudentData and a control named StudentControl. I'm using this control inside a DataGrid. I put the StudentControl in the grid with a DataGridTemplateColumn. I somehow bind the StudentData in the current cell to the control. This is requirement A. This StudentControl wants to specify if some of the controls inside it are editable or not. The best way to do this is exposing a property, like StudentControl.AreSomeControlsEditable. Then I can bind the IsEnabled property of those controls to this property. This is requirement B.
Here's my first idea. I bind the current StudentData to a DP of StudentControl, then, inside the StudentControl, I change the data context to the control itself:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox
Content="{Binding Path=ExposedStudentData.Field1}" *reqA*
IsEnabled="{Binding Path=OtherProperty1}" /> *reqB*
</UserControl>
This way, I figured, the StudentControl gets the StudentData from outside, exposes this same StudentData and the other new properties (so the data and other properties are in one place now, not two), and then I can bind to these exposed properties in XAML (reqA+reqB are fulfilled at the same time). Except this doesn't work because of this bug. Basically, if I set the DataContext of the control to itself, then it gets set before the outer binding is applied. So if my binding path inside the cell is X, and then the data context changes, the binding engine will look for X in the new, self data context, and not the outer, grid data context.
Here's my second idea. I bind the current StudentData to the DataContext or a DP of the StudentControl. Then, to access other exposed properties of the StudentControl, I give a name to the UserControl and use ElementName binding:
<UserControl x:Name="self">
<TextBox
Content="{Binding Path=Field1}" *reqA*
IsEnabled="{Binding ElementName=self, Path=OtherProperty1" /> *reqB*
</UserControl>
This way, I figured, the current data context is the StudentData, and it's not changed, and I can bind to that with a simple path (reqA), and I can bind to the other exposed properties with the ElementName stuff (reqB). It works in basic scenarios, but not in a DataGrid, because of this bug. I'm guessing the problem arises when there are multiple controls with the same name in the visual tree.
I'm really starting to hate Silverlight, I started using it a month ago, and I already reported 9 bugs. Whenever I try to achive something other than a simple hello world application or something that Microsoft and everyone else seems to be using Silverlight for, I encounter a new bug. So what now, how would you bind to a data class given from outside the control and some other properties exposed by the control at the same time? Without, of course, setting up the bindings from code (which I'm doing now, but it's a nightmare with ListBoxes and DataTemplates) or not using binding at all.
I think your problem is with the DataContext and how it is inherited, as well as namescopes.
For the first, you should know that if a control doesn't specify its own DataContext, it will inherit that of its parent, and for ItemsControl derived controls, each Item will have its DataContext set to one of the data items in the ItemsSource collection.
For your second problem, if you're inside a DataTemplate, you're in a different namescope than outside of it. You can't access controls "by name" outside of a DataTemplate. What you can do here (for Silverlight 4 and below) is to traverse the Visual Tree yourself and look for the control you want. This, however, is going to become much easier and less painful with some new features in SL5, specifically the "FindAncestor" feature (which already exist in WPF).
Here's an article on MSDN about Namescopes in Silverlight. And here's another one about DataBinding which mentions how DataContext is inherited.
To achieve what you're looking for, I think this blog post should help you. It shows you how to implement "RelativeSource Binding with FindAncestor"-like behavior in Silverlight.
Hope this helps :)
On another forum, they told me to use MVVM. As it turns out, it can make my first idea a little better. Instead of binding my StudentControl to a StudentData, and then exposing this data and other properties, I should create a viewmodel, let's say StudentControlData, which contains a StudentData and additional properties required by the control. If I bind my control to this, then in the inherited data context of the control, I have access to all properties that I need. Now the only problem left is that inside a ListBox in my StudentControl, I lose this data context.
In other languages (PHP/Python), I am used to creating a class which represents a given HTML page layout. These classes can have an unlimited number of attributes and dynamic features, such as Navigation (multi level), error messages, info messages, footer text, etc... Most of these items have defaults, but may be overridden on a per-page level. For example:
Layout = MainPage()
Layout.Title = "Google Image Search"
Layout.Nav1.Add("Google", "http://www.google.com", Selected=True)
Layout.Nav1.Add("Yahoo", "http://www.yahoo.com")
Layout.Nav1.Add("Bing", "http://www.bing.com")
Layout.Nav2.Add("Google Image Search", "http://......", Selected=True)
Layout.Nav2.Add("Google Shopping Search", "http://......")
Layout.Nav2.Add("Google Video Search", "http://......")
Layout.Nav2.Add("Google Web Search", "http://......")
or, handling errors:
try:
# do something
except ValidationError as e:
Layout.Error.Add(e)
or a php example:
$Layout->Info[] = "Your changes have been saved!";
My question is: how do I implement similar functionality into ASP.Net MVC 3 Razor (VB)?
So far, I have been able to see how you can use ViewData to pass various bits of data to a view. Also, I have been working with strongly typed views.
Frankly, I'm not sure who's job it is to have this logic, the controller or the view (or is there a model that should be in there somewhere).
Here is a summary of what I am shooting for:
A single place to initialize the default values for the layout (like the first layer of navigation, etc...)
Strongly typed attributes, such as Public Readonly Property Nav1 as List(of NavElement)
And a single place to render these layout elements (I assume _Layout.vbhtml)
Please forgive the here-and-there of this post... I'm trying to figure out the "right way" it's done on a platform that is both new (Razor) and new to me (ASP.Net MVC).
General advise very welcome!
I usually have a controller property (MainMenu) which I add to the ViewData dictionary in Controller.OnResultExecuting in my BaseController. Note that it's named ViewBag in mvc3 and it's a dynamic object.
Another approach would be to use sections in razor. Look at this question: ContentPlaceHolder in Razor?
I lean toward the fat models, skinny controllers perspective. If it were me I would create a base class for your page models that provides support for your common data. You can then inherit from that for individual page models and store your page specific data there.
The MVC implementations that have worked well for me usually have relatively clean Controllers. The controller is just the connector, getting the data from the request into the model and then handing off the prepared model to the correct view.
As for how you store collections of things in .Net - look at the classes that implement IEnumerable interface. Specifically focus on the Dictionary and the List classes. Dictionary objects store name/value pairs and can include nested dictionaries. You can work with them almost exactly like you can use multi-dimensional arrays in PHP. List objects are just indexed collections of items of the same type. You can work with them just like a simple array in PHP.
One side note - if you're just getting started in .Net and coming from a PHP/Python background, it might be better if you can switch to C#. You'll find the syntax much more comfortable and the tutorials/examples more plentiful (especially in the asp.net mvc world)
It's not difficult! :-)
If layout model is of the same type of the content page, the association is automatic! Here is the simplest example...
This is a test layout:
#model string
<style>
.test
{
background:#Model;
}
</style>
<div class="test">
Ciao
</div>
#RenderBody()
And this is a test content page
#{
Layout = "~/Views/Shared/_Test.cshtml";
}
#model string
...blah blah...
Just call the View with something like:
...
return View("Name", (object)"Green");
and it's done! The model is the same in the content page and in the layout page!
Andrea
P.S.: Believe me! This is useful!!! Maybe it's not the best for purists, but it's really useful!!! :-)
I am trying to understand how best to organize some common Dropdown lists used in several views (some are cascading)
Is it best to create
a single \Models\CommonQueries
then create a webservice for each dropdown used in cascading situation
then have a single controller that contains actions for each dropdowns
This way I can follow DRY principle and not repeat the dropdown logics since they are used in various views.
Much Thanks and Regards for reading my question and taking the your time.
+ab
When you say your dropdowns are used in several views, do you still consider these dropdowns as part of the view that is rendering them? If so, I think using a custom HTML helper or a partial view (ascx) is appropriate. Then, like you suggest, you can populate the data for the dropdowns using a common service from your domain layer. I think that is a very reasonable approach.
However, if you feel the dropdowns are somewhat external/unrelated to the view, then you might find that using Html.RenderAction() gives you a much cleaner result. Using Html.RenderAction(), you can output the result of an Action method directly into any other view. Therefore, you can create 1 controller with the necessary Action method(s) to populate those dropdowns. For example, let say you have a view with roughly something like:
<div>
<div id="coreView1">
<!-- some view code here -->
</div>
</div>
<div id="commonDropdowns">
<% Html.RenderAction("Create", "Dropdown"); %>
</div>
where Create is the name of your method in the DropdownController.
For example:
public class DropdownController : Controller
{
public ViewResult Create()
{
// do stuff here to create the ViewResult of the common Dropdowns
}
}
Note: Some people dislike this approach as it doesn't fit the typical MVC seperation of concerns. However, it can be a really great fit for some cases.
Hope one of these approaches can help.