I'm revamping some WPF Runtime apps to the new .NET API - Desktop. I've started to use the MVVM pattern but there seems to be some missing features. The first is that:
"objects inside the MapView control are not in the page's control hierarchy and, therefore, cannot be referenced from the page."
So the DataContext for the MapView isn't available down the control hierarchy? So in order to bind data to a map overlay I need to pass in the MapView object to the ViewModel and bind it programatically like so:
var tip = _mapView.FindName("mapTip") as System.Windows.FrameworkElement; tip.DataContext = graphic;
Doesn't this negate one of the major benefits to MVVM data-binding?
Similarly, symbols can't use data-binding? For example I want a TextSymbol's text to be a Graphic attribute named LABEL. I cannot do something like:
<esri:TextSymbol Text="{Binding Attributes[LABEL]}" ...
Finally, the examples all use code-behind pages and events, but it seems we're encouraged to use MVVM. So I guess my question is are enterprise-class applications really possible with the .NET API using MVVM?
Caveat: I'm relatively new to MVVM, but an having a hard time seeing how all this is going to work.
For all #DevSummit attendees, we are having session about MVVM tomorrow:
Title : Using MVVM to build .NET apps with ArcGIS
Description : see schedule app
Tuesday, 10 Mar 2015, 5:30pm - 6:30pm
Location: Oasis 4
There definitely are benefits to go on MVVM road and it really works well in large scale enterprise applications. Actually more complex the application comes the more benefits you start to see.
I'll come back to this after DevSummit.
MVVM at first can be a pretty intimidating concept but what will ultimately end up happening is you'll be doing without thinking about it. Remember that MVVM is to separate the business logic from the view, when we look at the MapView it is a view, its to know of the viewmodel but not of the model, so you would not want to access parts of the model (i.e. Layers, Service Tables) via the view, but you would access them via the viewmodel. With that said I think that what your attempting above needs another look. Is it that you would like to set the an Overlays datacontext to the graphic??? It would be beneficial to see some more code too if that's possible. Antti had done a session with Branscombe on how to make the move over from the WPF to .NET, it is some work and I am still making the move but I have found that the esri video's can help, just look up runtime .net
Thanks for the reply Brian. Here's another example. I have a collection of business objects I want to put on a GraphicsLayer. Properties of those objects should determine the image of the Graphic and the text shown. In WPF I had a MarkerSymbol ControlTemplate for a GraphicsLayer that bound to the attributes of the graphic.
<esri:MarkerSymbol x:Key="unitPlotSymbol"> <esri:MarkerSymbol.ControlTemplate> <ControlTemplate> <Border BorderBrush="DarkGray" BorderThickness="1"> <Grid Margin="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Bottom"> <Image x:Name="unitPlotImage" Source="{Binding Attributes[IMAGE]}" Stretch="None"></Image> <TextBlock FontSize="14" Foreground="Black" FontWeight="Bold" Text="{Binding Attributes[LABEL]}" HorizontalAlignment="Center" /> </Grid> </Border> </ControlTemplate> </esri:MarkerSymbol.ControlTemplate> </esri:MarkerSymbol>
It's nice, I just created the Graphics with the proper Attributes and added them to the layer. The correct image and label were applied. I don't see how this is doable in Runtime for .NET. since TextSymbols cannot bind. Now, I need to build each graphic programmatically in the ViewModel like so:
foreach (CADws.UnitInfo u in units) { Graphic g = new Graphic(); g.Geometry = new MapPoint(u.WebMercLon, u.WebMercLat, new SpatialReference(Constants.WEB_MERC_SPATIAL_REF)); CompositeSymbol cs = new CompositeSymbol(); PictureMarkerSymbol ps = new PictureMarkerSymbol(); ps.SetSource(System.IO.File.ReadAllBytes(@"C:\GIS\Resources\" + DetermineApproriateSymbol(u))); TextSymbol ts = new TextSymbol(); ts.Color = Colors.Black; ts.Text = u.UnitNum; ts.XOffset = -14; ts.YOffset = 0; ts.Font.FontWeight = SymbolFontWeight.Bold; ts.Font.FontSize = 16; cs.Symbols.Add(ps); cs.Symbols.Add(ts); g.Symbol = cs; g.Attributes["LABEL"] = u.UnitNum; if (u.Type != "P") { g.Attributes["DETAILS1"] = String.Format("{0} {1}", u.UnitNum, GetNiceStatus(u.Status)); g.Attributes["DETAILS2"] = u.CallInfo; g.Attributes["DETAILS3"] = u.KnownLocation; g.Attributes["DETAILS4"] = ""; } else { g.Attributes["DETAILS1"] = String.Format("{0} {1} Radio:{2}", u.UnitNum,GetNiceStatus(u.Status),u.TalkGroup); g.Attributes["DETAILS2"] = u.CallInfo; g.Attributes["DETAILS3"] = u.KnownLocation; g.Attributes["DETAILS4"] = u.OfficerInfo; } graphics.Add(g); }
Then add that collection to my GraphicsLayer.
(Initially I thought I'd just expose a ObservableCollection of Graphics from the ViewModel that the GraphicsLayer could bind to, but the elements in the MapView don't inherit it's DataContext and setting it for every element in the map programmatically seemed like a poor design.)
So clearly I'm violating MVVM above. The ViewModel here is dictating how the View looks. How else can this be done?