다음을 통해 공유


M-V-VM training day sample application and decks

During the M-V-VM training (last saturday) with Karl Shifflett I showed and referred often to this Southridge application below..
The app is not very clean(when it comes to resources and styles in particular) but it is a fairly good ViewModel example, and it served well to illustrate a few of the points we made at the training (both good and bad practices).  So I am sharing it (as promised).   

The application consists of three views sharing one ViewModel around real-estate data (called MainViewModel).

The application's chrome includes a ribbon that drives all the Viewmodel via its filtering functionality.  The two filter commands that work are Bathrooms and bedrooms [if you change the selection for these, the results on the views should filter].

image 

I purposedly did not make the main window with the ribbon a view because I often run into applications that transition views so I wanted to show the concept of having a constant element in the chrome.   The ribbon is functional ( you can see its resize behavior).
The checkbox columns are bound to the MainViewModel, and drive the columns that show up in the Search view (below).

The views
Again, the views are the results for the search from the criteria in the ribbon. 

image image

Both of these views are 100% XAML. 

The grid is very functional, sort, selection, etc.   I call it SearchView.
The map is simply a Listbox with a datatemplate and a tooltip on the ListBoxItems. I need to add more to that UI.  I call it the MapView :)
You navigate to the map view, by either clicking on the map icon on the left hand side of any row of the data grid, or by clicking on the Map Ribbon button on the Search Results tab. 
The full details of course include an OpenMap Command exposed in the MainViewModel that does the work.

To go back to SearchView (from any view), just click on the Search tabs, and it automatically goes back.  The way that happens is through an attached behavior in the RibbonTab:

 <r:RibbonTab Name="SearchTab" Label="Search Criteria" view:RibbonTabSelectedBehavior.SelectionChanged="{Binding SearchSelectedCommand}" 

This is a standard attached behavior that delegates the event and 'forwards' it the SearchSelectedCommand on the MainViewModel.  You can see the implementation on the RibbonTabSelectedBehavior class.

There is another totally different View, called Profile.. 

image

This view demonstrates data validation using IDataErrorInfo. It has an InfoTextBox (from Kevin Moore's bag-o-tricks) and shows the fields that are invalid in red.

The validation is trivial, mostly checks for empty strings on all fields, except zipcode, around ZipCode it enforces the 5 digits or 9 digits with a dash format for US Zipcodes.

[For details, check the Profile class] in XMLData project. ]

You can see how the "Save Profile" command is wired to only be enabled when the data is valid.

The Profile view purposedly contains a "PreferencesEditor" usercontrol that is not a view itself; it takes data context and drives its UI from it, but does not have full view functionality (again to illustrate a point: not every thing is a view).

The profile window has both a DataTemplate ( for everything in "Contact information"). 

The listboxes in Location are interesting because they State is populated with read-only data (I call it meta-data) that is not coming from the view model,  but the views are bound to the Viewmodel when selection happens (county is driven by the selection on State).   Be aware that the data is not big and some states (e.g. CA) might not have valid data. Try WA to see it the way I see it.

 

The final screen is the Listing Details screen. This is a modal window that shows Listing details. Nothing too exciting here but it is a wired view model, including the Make appointment button. You may notice that the "Neighborhood" data template in this view is the exact same data template from the Tooltip on the map.   yes, I am lazy and I drive reuse, that is why viewmodel is my friend.

The Listbox has a very "view specific" behavior implemented by 3 lines of code behind, it is used to implement selection when mouse enteimagers a listboxitem. I purposedly left it as is, though I am sure some M-V-VM purist will tell me I should have implemented it as an attached behavior. I chose not to because I did not want the viewmodel to manipulate the view on a quirk like this one. The view can self-contain the behavior and yes, if I try to move the code to Silverlight it will need the same 3 liner. I was OK with that.  I felt it was equally bad to have the ViewModel be listening for view events and "manipulate the view" directly (which I would have needed).

At last, there is one class that keeps it all together, I called it the ViewManager. This is an over-simplistic implementation of a Presenter for the views that handles transitions across the views.  The views register with the viewmanager as they are instantiated, and the ViewModel can trigger call into the ViewManager ( a singleton) of course to trigger transitions across views. The viewManager itself can have a ViewModel if needed; in this case I did not use it, but in other apps I have used it.

That is it, if you were at the class I hope you remember this.  If you were not, then maybe looking at the deck might help, though I must say the class was quite driven by example, so the slides are a bit presentation-zen-like. Please try the notes for a bit more context and drop me a comment or email if that is not cutting it.

At last, thanks to all those that attended the class. It was a lot of fun, and I really enjoyed meeting all of you.

Thanks also to Matthias & Bruno & Karl for puttting it together and for inviting me. 

Presentation (ppt deck) is here.   Code is here.  The code requires the WPF toolkit, all assemblies needed should be included in the project but if you get a compilation error, try getting the toolkit and the ribbon from codeplex

[Disclaimer again, the code needs some heavy scrubbing on the UI and resources; also I had to take out the data that I normally use, so if you see #if SQLCE, simply ignore these.  I replaced all the data with an XML file (for easy tweaking). It is not real data, so don't be surprised when you see a Seatttle address in an Issaquah neighborhood.  I merely invented the data on the fly].

Comments

  • Anonymous
    February 09, 2009
    PingBack from http://www.clickandsolve.com/?p=5699

  • Anonymous
    February 09, 2009
    Some questions (since the link to the source does not work, so I can't look for myself): Can you use a mouse doubleclick to go from the SearchView to the Map? How did you bind the list header click to sorting the content? (these questions about binding events to the VM are my main problem around M-V-VM, thats why I ask). thanks in advance, Sam

  • Anonymous
    February 09, 2009
    Code and ppt decks aren't available. Sam

  • Anonymous
    February 10, 2009
    Awesome - Wish I could have been there. Any plans on doing this out East? Both the code link and ppt links are broke. Can't wait to see the material. regards, Bill

  • Anonymous
    February 10, 2009
    Links should be: http://www.cookingwithxaml.com/meals/southridge/decknotes.pptx http://www.cookingwithxaml.com/meals/southridge/southridge.zip

  • Anonymous
    February 10, 2009
    Sorry about the links..  I tested them but some how between my test and publish I made them relative... Huseyint them right, and the actual links have been fixed.. Sorry again..  I need to stop blogging at midnight so I am awake when I hit publish ..

  • Anonymous
    February 11, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Anonymous
    February 13, 2009
    Thank you very much Jaime for sharing this project. David Roh

  • Anonymous
    February 13, 2009
    Good stuff Jamie. MVVM is a nice pattern for WPF and Silverlight that fits great with the DataContext and XAML binding techniques. I'm no pattern extremist by any means, but I like combining it with Ninject or Unity for some DI to improve testability. Thanks for sharing!

  • Anonymous
    March 03, 2009
    Hi Jamie, I've been working my way through the Southridge sample (both the original from codeplex/PDC2008 and your current example here). There are many differences and I'm having trouble making the leap. In the Southridge Lab from PDC there is a section on the Ribbon. They create the RibbonCommand_CanExecute and the RibbonCommand_Executed method - but unfortunately they never get set up to do anything. The CanExecute just sets "true" and the Executed pops up a messagebox. I'm really having trouble figuring out how to connect these methods up to my ICommand methods. I'm sure that I'm just missing something really simple but I can't figure it out. In this example you changed the way that was set up for the Commands and you added the NonRoutedRibbonCommandDelegator. Then in your RibbonCommands in the xaml you use the view:NonRoutedRibbonCommandDelegator. You also use the DelegateCommand here instead of the more simple RelayCommand. If I could find one example of how to hook up the RibbonCommand with the ViewModel I think that I would be on my way. I just can't figure out how to get from the Executed to the ICommand for each one. Thanks in advance and I really appreciate all the work you put into your blogs! I'm hoping you guys will make a trip out to the Midwest or East on your tour so we from Cleveland can come see the real presentation. It sounds awesome! regards, Bill

  • Anonymous
    March 03, 2009
    The comment has been removed

  • Anonymous
    March 04, 2009
    Hi Jamie, Yes - the HOL is what I was referring to and unfortunately that doesn't use the ViewModel so everything is in the code behind. I appreciate the info. I'm close but I'm still missing something really simple. Probably one line of code somewhere :) I get my RibbonControl in my app, but the ICommands are never getting called so all my ribbon buttons are disabled  I've set breakpoints in the and don't hit them. Here's what I have: [mainwindow.xaml]    <r:RibbonWindow.Resources>        <ResourceDictionary>            <local:NonRoutedRibbonCommandDelegator                x:Key="CopyCommand"                LabelTitle="Copy Project"                LabelDescription="Copy a project"                ToolTipDescription="Copy Project from your disk."                ToolTipTitle="Copy Project"                SmallImageSource="Images/copy.png"                LargeImageSource="Images/copy.png" /> ... [mainwindow.xaml.cs] mainwindow() {            this.DataContext = new MainWindowViewModel();            InitializeComponent(); ... [mainwindowviewmodel.cs] ... private ICommand _copyCommand; ...        public ICommand CopyCommand        {            get            {                if (_copyCommand == null)                {                    _copyCommand = new RelayCommand(CopyExecute, CanCopyExecute);                }                return _copyCommand;            }        } ...        private bool CanCopyExecute(object param)        {            return true;        }        private void CopyExecute(object param)        {        } ... I'm using your NRRCD class as well. I've noticed that when I set a breakpoint in your Southridge sample code that it hits the getter for the NewProfile ICommand, for example, which is exactly what I would expect. The call stack window shows [External Code] which I am suspecting is the RibbonControlLibrary dll? Thanks in advance for anything you might see here that I am forgetting. I'm sure it going to be one of those things that makes me go "Duh!" regards, Bill

  • Anonymous
    March 04, 2009
    Hi Jamie - I meant to add the ribbonbutton to what I have in the mainwindow.xaml <r:RibbonButton Command="{StaticResource CopyCommand}" /> thanks! Bill

  • Anonymous
    March 04, 2009
    The comment has been removed