MVVM - View and ViewModel Problems

6894
19
03-20-2012 06:40 AM
CorySchinkel
New Contributor II
Hi all,

I am using the MVVM pattern and the WPF API to build a mapping app.  I have a map object defined in a View and my app logic is defined in a ViewModel.  The ViewModel is set as the datacontext of the View.  Everything works great until I try to do something like work with the draw object in the ViewModel.  The draw constructor requires a reference to the map which I cannot access in the ViewModel.  This of course prevents me from defining the draw object in the ViewModel which is needed to respond to events on the draw object.  I could define the draw object in the View but it goes against the MVVM pattern to reference the View from the ViewModel.  So the dilemma is that I have no way of getting a reference to the map or draw object in the ViewModel and thus no way to handle events raised by those objects and no way to access their properties. 

If I were to define the draw object in my view I could setup commanding to handle some of the logic in the ViewModel, but I have requirements that would be difficult to implement with that design.  I would prefer to have the draw object (and other esri related objects) defined in the ViewModel and bound to the View somehow, but finding a good way to accomplish that is elusive.
 
Since I am using MVVM (fairly strictly) I do not want any (or very limited) amounts of code in the view code-behind.

Can someone offer guidance on best practices for handling this type of scenario?  Is there a way to make this work?

Thanks,
Cory
0 Kudos
19 Replies
AlexAgranov
New Contributor
I'm having a similar issue. My application is also strict MVVM. If I declare my <esri:Map> in a DataTemplate, that is part of the UI and not DataContext(view-model). So in the ViewModel side, I have no access to the esri:Map for doing things like setting off a saved extent. It seems that despite the updates to GraphicsLayer, the esri:Map is not MVVM friendly.
0 Kudos
JeffJackson
New Contributor III
We ran into the same problems building ArcGIS Explorer Online with the ArcGIS Silverlight API, and most recently with the ArcGIS Viewer for Windows and the WPF API.

I'm at the DevSummit this week (and actually doing a session on MVVM) so if you're around I'd love to talk with you about it. When I have some spare cycles I'll put together some thoughts on how we've approached MVVM and the Map control and post it back to this thread.

-Jeff
0 Kudos
AvnerKashtan
New Contributor II
We ran into the same problems building ArcGIS Explorer Online with the ArcGIS Silverlight API, and most recently with the ArcGIS Viewer for Windows and the WPF API.

I'm at the DevSummit this week (and actually doing a session on MVVM) so if you're around I'd love to talk with you about it. When I have some spare cycles I'll put together some thoughts on how we've approached MVVM and the Map control and post it back to this thread.

-Jeff


That would be wonderful. I've been going through the same problems that others here have trying to force the WPF API into the MVVM mindset, and I have to admit that my solution was to be more flexible with my MVVMness - my MapView class has rather a lot of logic in it, unfortunately, with all of the map-specific functionality (selection, zoom/pan, etc) handled in the View, while the MapViewModel mostly handles bringing in the Layers and interacting with other parts of the system. It's not ideal, but it works. I'd love to hear a good write-up of the ups and downs of MVVM with the ArcGIS APIs.
0 Kudos
JaredWhite
New Contributor III
We ran into the same problems building ArcGIS Explorer Online with the ArcGIS Silverlight API, and most recently with the ArcGIS Viewer for Windows and the WPF API.

I'm at the DevSummit this week (and actually doing a session on MVVM) so if you're around I'd love to talk with you about it. When I have some spare cycles I'll put together some thoughts on how we've approached MVVM and the Map control and post it back to this thread.

-Jeff


Hi Jeff,
I just finished the video on esri's site of your seminar session. Did you ever get around to sparing a few cycles on MVVM and map control?
0 Kudos
JeffJackson
New Contributor III
I was hoping to write a series of blog posts on blogs.esri.com but the new site doesn�??t do a good job formatting source code so I�??ve been waiting for some improvements before investing a lot of time on it. In the meantime, here�??s a sample MVVM application (attached) that shows how you can work around the fact that the Map control does not expose a dependency property for its Extent.

The app shows a map of the US and lets you enter text and search for states. The results are displayed on the map and listed beside it. Clicking on a state in the list will zoom the map to its extent.


This is made possible by an attached property defined in the static class MapHelpers and it specified in the View like this:

<esri:Map local:MapHelpers.Extent="{Binding MapExtent}">


The attached property is straight forward. The only logic is in OnExtentPropertyChanged where the DependencyObject is cast to a Map so that ZoomTo can be called, passing in the NewValue. This implementation is very straight forward in that it only allows for one way binding �?? from the VM to the Map.( In some cases you might want a two-way binding such that when the end user zooms or pans the current extent of the map is propagated back to the VM. That is also possible by wiring into the Map�??s ExtentChanged property.)


// note that I tried very hard to paste the code in question here
// so you could see what i was talking about
// but this lame resource center forum wouldn't allow it. Sheesh...


Note that I didn�??t use the Map�??s Extent property to do the zooming. The ZoomTo method provides a nicer implementation, using a smooth animation when you zoom in or out.

Let me know if this was helpful at all. Next I�??ll look into the elusive Draw object�?�

/Jeff
0 Kudos
JeffJackson
New Contributor III
I�??ve given the question of how to use the Draw object in an MVVM-friendly way some thought and tried a few different things. Here�??s my take on the cleanest solution (attached), though it�??s certainly got some blemishes�?�

I agree with Cory�??s basic principle �?? that the Draw object makes sense as part of the ViewModel. So I declare it there and it�??s easy enough to create and use. The only question is how to get the Map set into it. For that I rely on an EventTrigger defined on the View�??s root Grid. An InvokeCommandAction lets me call a method on the ViewModel and pass in the map.

      <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
          <i:InvokeCommandAction Command="{Binding ConverterParameter=SetMap, Converter={framework:CommandGenerator} }"
                                 CommandParameter="{Binding ElementName=MyMap}" />

        </i:EventTrigger>
      </i:Interaction.Triggers>

Then the SetMap method simply connects the map to the Draw object.

The thing to watch out for when you �??cheat�?? like this is to make sure that the various methods on the ViewModel are still testable.

/Jeff
0 Kudos
KeithWeber1
New Contributor III
Jeff,

I am new to the MVVM pattern and I am trying to learn how to use your MvvmFramework to setup methods to handle events in a ViewModel using your CommandGenerator. In your previous post, it appears that you are using a library from Expression Studios (System.Windows.Interactivity). Is it possible to use the CommandGenerator to setup a method to handle an event for something like the SelectionChanged event of a combobox without using the Expression Studios library? Any help is greatly appreciated.
0 Kudos
JeffJackson
New Contributor III
Keith,

If you want to wire up a method on your ViewModel to some arbitrary event, then you need to use something like the InvokeCommandAction which is defined in System.Windows.Interactivity. Then you can generate a command using the CommandGenerator and you're set.

In the case you describe though, you don't really need to use an event to determine if the ComboBox selection changes. You can simply bind to another property in your ViewModel instead.

For example, your view ComboBox might be defined like this:


  <ComboBox ItemsSource="{Binding States}"
            SelectedItem="{Binding CurrentState}"
            Height="25" 
            Width="120"/>




By binding to the SelectedItem property, the setter on you ViewModel property (CurrentState) will get called any time the selection changes.

Hope that helps,
Jeff
0 Kudos
EladNachmias
New Contributor
MVVM should be implemented in a manner that the ViewModel is unaware of the View (if you're working ViewModel first, which is, to my opinion - the best way of working since it avoids spaghetti code which i've seen on several projects).
that way you don't need to handle DataContext on xaml level which is a huge advantge since the WPF engine sometimes has some Visual tree quirks that might cause problems when looking for DataContext from the visual tree.
From my experience (after doing many projects with Prism, Caliburn.Micro(best solution)), When you try to do something that you dont have access to (manipulating view from viewmodel), that means you've reached the logical limit of your design pattern.
So don't break the design pattern.
Best practice is these cases is to build your own control (or some wrapper control), create some dependency properties on it, bind to the data source on the view model, and handle changes in the data through that control, not in the VM.
best of luck.
0 Kudos