Mapsui does not support MVVM pattern for WPF and Xamarin

Created on 13 Dec 2019  路  32Comments  路  Source: Mapsui/Mapsui

I would like to enhance Mapsui to support MVVM pattern, Let me know if you guys agree with this, then i will go further.

enhancement

Most helpful comment

If I understand it correct, then this is only a problem of WPF and Xamarin.Forms. iOS, Mac and Android don't use this. So it would be enough to implement a DependencyProperty for this two (perhaps UWP also?). Adding a

public Map Map
{
    get { return (Map)this.GetValue(MapProperty); }
    set { this.SetValue(MapProperty, value); }
}

public static readonly DependencyProperty MapProperty = DependencyProperty.Register(
"Map", typeof(Map), typeof(MapControlControl),new PropertyMetadata(false));

And some logic in OnPropertyChanged(). Would do this the work?

All 32 comments

Help is always welcome, but could you please tell a little bit more about what you want to do?

MapControl has a property called Map, which contains different layers as you guys knows.

In WPF I am generating (in my project) different layers in code behind C# class and this property can't be used in data binding as it a normal CLR property, but doing in this way breaks the MVVM pattern.

If we expose the Map property as DependencyProperty, so that we can bind list of layers collection in xaml page ex: Map="{Bnding Path=MapLayers}" which is not possible as of now.

Any update regarding Mapsui supporting MVVM?

There is sample project by @RidaNoor here

There is nothing specifically planned with respect to MVVM support.

Before we add new feature we have to look at the maintenance burden en complexity it brings. Turning all properties into DependencyProperties is not something that appeals to me. On the other hand if we could make some simple changes to make it easier to use it in an MVVM project that may be possible.

So what I like to know is what exactly are you looking for? What does MVVM support mean to you? Could you name some other comparable component that do have MVVM support? If so, we could take a look at that and perhaps get some idea about what needs to be done.

We are now planning feature for v3, so this is a good moment to bring this up. So also this issue.

First of all - congratulations for huge and successful effort with Mapsui :-) I really looking forward to use it in some projects and because of this I want to share my experience with MVVM in GIS applications and also some comparable components and approaches I use now and you can look at.

In my work as GIS developer I use ESRI ArcGIS Runtime SDK for .NET. I started using it several versions ago when it has no MVVM support. So several people (me including) persistently requested MVVM support to be built-in (read: write angry mails to ESRI - they were not much keen on adding it due to required breaking changes).

As I grown up as regular .NET developer I strictly follow MVVM pattern. This means - no single line of code in code-behind (this is a little bit hard and some people allow code here if it manipulates view only) and no reference to view in view model. If you look at Mapsui MVVMCross sample - this is not the case: there is reference to MapView in MapViewModel.cs and there is code in MapPage.xaml.cs.

So when ESRI finally adds MVVM support I was (a little bit) disappointed. They separated Map from MapView just as @syedamjad asks above. That way Map content can be manipulated from ViewModel if you think of it as a data (completely replace map, manipulate map - add/remove layers, manipulate layer properties, etc). But you cannot control map as a whole at presentation level (zoom, pan, etc) and also handling MapView events is a pain with event-to-command approach. You cannot even do properly Identify because it is a method on MapView and you cannot call it from ViewModel.

This is the current state - you can look at it in SDK reference:
MapView
Map

I started to think of a workaround approach and find this one:
Navigating the MapView from a ViewModel

Only after reading this article I start think that MapView is a special case of many and rich view operations and it will be a lot of code (and maybe not correct) to make them all bindable to ViewModel or remove them from MapView and put them on some other object (or on Map). So approach seems to me more correct and right balance between MVVM (binding Map/Layers only) and Service (this way I call Controller class from the article).

Now I use this approach for every UI control I need to manipulate from ViewModel. Because of this, I think vector tiles and NTS geometries are more urgent for Mapsui - MVVM can be completely replaced with Service/Controller approach. Anyway - for v3 I think binding Map/Layers to MapView only is the golden mean between easy implementation and high usage.

Note: This article is from ESRI's Morten Nielsen but here he have important contributions as @dotMorten.

But don鈥檛 we have this? We have a MapControl, which is the view, and a Viewport, which holds information about which part of the map are shown, and a Navigator, which moves the Viewport around. Or did I understand something wrong?

I realized that almost all code links in article are broken so first I will again explain the idea behind the article to make sure we understand each other.

MapControl is View. It shouldn't be accessed from ViewModel so we add to architecture MapControlService - think of it as a wrapper around MapControl. It exposes methods which ViewModel calls and they call methods of MapControl. Also, MapControlService internally handles MapControl events and exposes them through own events to which ViewModel subscribes. Crucial here is that MapControlService is attached to MapControl in XAML (no code-behind) and that its methods and events can differ in signature/values from MapControl's so they do not expose view classes (e.g. sender in events refer to MapViewService and not MapView; also for EventArgs some remapping can be necessary).

So - on the questions:

  1. MapControlService is NOT necessary NOR required by Mapsui. It is necessary only when MVVM is used in an application - and MVVM is not mandatory. In addition - there are some alternatives - see below.

  2. Where is place for MapControlService to be defined? In my opinion in application and NOT in Mapsui. Beside reason in point 1 above there are also more important: MapControlService can expose only application-needed properties/methods/events of MapControl and in addition can contain application-specific code.

  3. @charlenni Why MapControlService is necessary to an MVVM application? (I haven't used Mapsui till now so correct me if I'm wrong) Because you must obtain Viewport and Navigator from MapControl, i.e. this shouldn't be done in ViewModel because you will require reference to MapControl in ViewModel.

  4. Are there MVVM alternatives to MapControlService? You can handle MapControl events with Event-to-Command approach in XAML and wire them into ViewModel. But this will not work if you need access to MapControl properties or methods.

Now forget for article (and MapControlService) and imagine simple map application - main screen is a map, user zooms/pans using gestures in MapControl, you only handle SingleTap (and eventually LongTap) as user input - you use two Event-to-Commands, provided info from events is sufficient to show it or query database and to start all data flows and you do not need MapControl reference and MapControlService at all. All you need is to show/switch some map/layers, control visibility, etc. If you do this in an MVVM application you need to bind Map/Layers from ViewModel.

Because of this I think Map/Layers needs to be bindable - this is necessary for all MVVM applications and is sufficient for simple MVVM applications. So I think this is golden mean between make all properties bindable (lot of code) and force all (even simple) MVVM applications to implement something like MapControlService. Finally - if you need more control over view like Navigator - either violate MVVM or use something like MapControlService.

If I understand all correct, then you want to set Map and Navigator of MapControl. That is possible. No problem. The only thing, that doesn't work, is that Map and Navigator is bindable. What isn't possible, is to set the Viewport.

var mapControl = new MapControl();
mapControl.Map = new Map();
mapControl.Navigator = new Navigator();

@pauldendulk asks for an idea how others do MVVM in map controls and what does MVVM support mean to me. Also - can MVVM be done easier and with simple changes,

I give my experience with ESRI SDKs approach and why my thinking evolve from "all properties need to be bindable" to "only Map is sufficient to be bindable". Let's summarize:

  1. I would like to only have Map to be bindable so it can be set from XAML - just like @syedamjad proposed.
  2. For many regular MVVM map applications this can be all developer needs.
  3. For large MVVM GIS applications developer will need more control on the view and anyway will self implement MVVM-friendly approach like MapControlService to access Navigator and Viewport.
  4. If there are solid reasons that Map cannot be made bindable - leave it as is now. Developers still can use MapControlService approach as MVVM-friendly way to manipulate Map and layers.
  5. You can make Navigator bindable but this will lead to "make bindable this property and this and this...". So I think viable solution is to make bindable only Map - for all other properties there will be option developer to use MapControlService approach.
  6. MapControlService approach can be used also in cases when property is read-only (e.g. Viewport) or when you need to call methods on MapControl.

We see (points 3, 4, 5, 6) that MapControlService approach is fairly universal and can handle many cases when there is no built-in MVVM support in some UI control. So I described it in detail here as this is @dotMorten's idea how to overcome these cases and every developer using Mapsui can easily find it and use it to write MVVM-compliant applications.

Finally: make only Map property bindable - for all other cases there is MapControlService approach.

@plamen-i Thanks a lot for your extensive feedback. In order to make a decision I really need to play with some sample code. In general my solution would be to allow MVVM without implementing it ourselves.

One more question. In Mapsui v1 we only had to the Map to hold data. In v2 we separated the Viewport and Navigator, which is a good thing it itself, but now they are two more fields on the MapControl. Would it be an improvement if we had just one field to hold al data. Like a MapModel with a Map, Viewport and Navigator.

I鈥檇 recommend for UI controls any property should be a get/set dependency property (and allow nulls for when data context isn鈥檛 available yet). That way you get mvvm support pretty much for free.
Method execution / view operations are not so simple though, but that doesn鈥檛 sound like that鈥檚 what is being asked for.

Generally I agree with @dotmorten. At least all View properties which is expected to be manipulated from code should be bindable. And because some can be threated bothways (e.g. UseDoubleTap can be set statically in XAML when developer decide whether double tap is used in application or can be set by user in settings whether he/she wants to use double tap) - safest way is to make all them bindable.

But as @dotMorten said even this is not enough to completely remove MapControlService approach as there still will be methods in View to call from ViewModel. This is valid also for proposed MapModel (though I think MapViewModel is more appropriate). It looks exactly like MapControlService but it should be fully-featured with methods calling which will bring more work to do in addition to breaking changes.

I think at this stage we can start MVVM support with absolutely necessary things - bindable Map property and as much as possible bindable other properties. If at later stage we decide MapModel is necessary - we will make the same breaking changes to include it - just lets postpone them for now.

At this point, MapControl.Viewport shouldn't have any methods, that should be called outside of the library. All changes to Viewport are made by the Navigator, which could be set (could made bindable).

So I would say, in the moment, there are only two objects, that should be bindable: Map and Navigator. And perhaps the values from Viewport as readonly. Anything else?

Seems to be sufficient for now.

@dotMorten Thanks for the advice. Making just a few properties on MapView/MapControl bindable is a small investment. I would rather not add bindable properties to components like Viewport, Map and the LayerCollection.

And also thanks for developing SharpMap! It was the starting point of my open source work and Mapsui has gradually evolved from that code base.

@charlenni @plamen-i Okay, Let's add just those two. A sample project might be nice. Not sure if it should be in the Mapsui repository.

Something else. MVVM is out, MVU is in. See here an issue on Mapsui https://github.com/dotnet/maui/issues/40

btw, I was very happy with the Maui announcement. Windows itself will be more important (UWP now seems to be an afterthought), this is good news for the future of Mapsui.

I use Prism as MVVM framework and the good news is that they are also looking forward to MAUI: #2118

I suppose there will be support for both MVVM and MVU so if we start now with MVVM we can add MVU at later stage when we know more about MAUI and MVU support.

though I think MapViewModel is more appropriate

@plamen-i The problem I have with that is that this would make Mapsui into an MVVM component and I don't want it to be. I want it to allow MVVM but I want Mapsui to just be a component, like so many other UI components.

Mapsui itself wouldn't get a MVVM component, but MapControl.

But we have only one version of the MapControl right now. Would you suggest to create an MVVM version and MVU version as separate components? Perhaps we could. My idea was to ship one component that can be used in several scenarios.

If I understand it correct, then this is only a problem of WPF and Xamarin.Forms. iOS, Mac and Android don't use this. So it would be enough to implement a DependencyProperty for this two (perhaps UWP also?). Adding a

public Map Map
{
    get { return (Map)this.GetValue(MapProperty); }
    set { this.SetValue(MapProperty, value); }
}

public static readonly DependencyProperty MapProperty = DependencyProperty.Register(
"Map", typeof(Map), typeof(MapControlControl),new PropertyMetadata(false));

And some logic in OnPropertyChanged(). Would do this the work?

This is typically how we do it. Use something like

public Map Map
{
#if __IOS__ || __ANDROID__ || NETSTANDARD
   // basic property
#elif NETFX_CORE || WPF
  // Dependency Property
#endif
}

@dotMorten But on Android and iOS is it still possible to use the MVVM pattern, right? And if so why can't we use the same mechanism on WPF and UWP?

Yes and no. The pattern there is quite different unless you create a xamarin.forms version on top. For instance on iOS you typically use MVC instead.
When it comes to UI controls you're not really cross platform any longer so don't fret trying to make it all the same every where - because the UI controls you inherit from are already quite different.

It's not really about adding mvvm support directly, but making sure that those who do want to use mvvm easily can with your controls

MVVM ist a programming model which separates the view from the model via a viewmodel which acts like a translator between both worlds.

For wpf for example the view is purely xaml so there are dependency properties to allow binding to the viewmodel.

But what if the view is written in code? No xaml means no need of dependency properties. So dependency properties are the xaml way of implementing the mvvm pattern.

@badcel Since MVVM is also about separation of roles and XAML is a way for designers (i.e. non-developers) to create View and wire it to other part of application using ViewModel - I cannot imagine UI to be written in C# for any serious application other than proof-of-concept demos.

Anyway - to state that dependency properties are required by XAML is simply not true. Dependency properties are part of MVVM and you can see here that binding can be set in C# and can be between two C# classes.

In reality XAML-defined classes are partial and they have code-behind (other part of the class) containing InitializeComponent() which essentially instantiates XAML to objects. I.e. you can replace InitializeComponent() with code-generated UI (as in sample above) and you still will have MVVM but without XAML. And you will continue to use dependency properties. You will lose benefit of role separation but you will still have all other MVVM benefits.

Anyway - to state that dependency properties are required by XAML is simply not true. Dependency properties are part of MVVM and you can see here that binding can be set in C# and can be between two C# classes.

This confuses me. The link you have shows that all bindings are set up by referring to the dependency property field, so that makes it required.
Your argument about code behind I don't get either - XAML is just one way of defining the UI tree, and is really no different from the example that defines it in code. The two are equivalent. When people say "XAML" they are sometimes referring to the object model, and not so much the actual language used to code against that object model.

The case @badcel set was XAML vs C# for UI definition.

But what if the view is written in code? No xaml means no need of dependency properties. So dependency properties are the xaml way of implementing the mvvm pattern.

Sample from link shows MVVM implemented and still uses dependency properties even UI is C#-defined.

My point is that dependency properties are part of UI control and they are there to implement MVVM pattern, not to support XAML-specific way to define UI.

@plamen-i you are right with your arguments. You can have a UI defined in code and bind it via a dependency properties.

But dependency properties are not the only way to implement mvvm. Dependency properties are just a way to bind data.

I work on a wrapper for a Linux toolkit which will probably never have dependency properties but the user should be able to separate UI and model via mvvm.

Actually I missed the point that implementing dependency properties would only affect the platforms which profit from dependency properties. So sorry for the confusion.

Yeah here's an example where I create the property different based on the target platform:
https://github.com/Esri/arcgis-toolkit-dotnet/blob/0bb4fda2ad2994c20dc985e102995ff32cb65aa6/src/Toolkit/Toolkit/UI/Controls/Legend/Legend.cs#L94-L103

If I understand it correct, then this is only a problem of WPF and Xamarin.Forms. iOS, Mac and Android don't use this. So it would be enough to implement a DependencyProperty for this two (perhaps UWP also?). Adding a

public Map Map
{
    get { return (Map)this.GetValue(MapProperty); }
    set { this.SetValue(MapProperty, value); }
}

public static readonly DependencyProperty MapProperty = DependencyProperty.Register(
"Map", typeof(Map), typeof(MapControlControl),new PropertyMetadata(false));

And some logic in OnPropertyChanged(). Would do this the work?

This would help us to follow MVMV pattern and will be used in DataBinding. In MVVM there will be CLR's property in viewmodel binded to view with the help of databinding.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheProme picture TheProme  路  4Comments

TarasParashchuk picture TarasParashchuk  路  4Comments

willy40 picture willy40  路  6Comments

JesrigPineda picture JesrigPineda  路  4Comments

ricardoboss picture ricardoboss  路  5Comments