Dependency Properties and Layer support?

Feb 15, 2010 at 11:50 PM

While working with BruTile, which is quite easy, I miss dependency properties to set things like the Map.Rootlayer, etc. Currently, I have to wrap your control with my own cooking to make it easier to behave in a MVVM fashion. Is there any particular reason why you don´t have DPs.

In addition, I was wondering about support for adding layers (for things like icons, regions, etc., preferably using MVVM, i.e. data binding DP). Will that be on your roadmap, or how do you currently do this. 

Please carry on the great work!

Erik

Coordinator
Feb 16, 2010 at 5:35 PM

There is no particular reason for that. The UI part is the youngest part and is still work in progress. Expect the interface of the MapControl to go through some more changes. Support for MVVM is definitely on our list.

For which properties do use DepencyProperties in your project?

In our own projects we use some custom code for points and other geometries. Eventually we want to use SharpMap for that. Currently it is already possible to use SharpMap tiles, and now that SharpMap has a (Bru)TileLayer it is possible to overlay tiles with features from SharpMap. Note, the SharpMap dll in the wpf sample does not use the version of SharpMap the TileLayer. I will haveto update that.

This solution is not perfect though. For one, all layers need to be rendered at once, I would like to show any data as soon as possible.  Also, only one tile can be requested at a time. So we are also looking into a more integrated solution of SharpMap in BruTile.

Paul

Feb 23, 2010 at 11:29 PM

Hi Paul,

From an MVVM viewpoint, it would be great to have several DPs, especially the RootLayer (to set the map from your VM) and the Transform (to set the center, resolution and extent). Since I didn't wanted to change your code too much, I only made the MapControl raise an INotifyPropertyChanged event for the Transform (so I also call it whenever a Refresh is called). That way, I could update the bound DPs in my user control wrapper. 

In order to display layers, I've followed the approach suggested by Egor here: In a Grid cell, I first put your MapControl and then add an ItemsPresenter on top of it. Its ItemSource is databound to ObservableCollection<GeoLayer> GeoLayers, and its ItemTemplate is set to a Canvas the same size as the Grid. Each ItemTemplate (which receives a GeoLayer as its content), in return, is also an ItemsPresenter, databound to the GeoLayer's ObservableCollection<LayerItem>. Finally, the style of each LayerItem (since every item is automatically wrapped by the ItemsPresenter inside a ContentPresenter) I use a Style to databind the Canvas.SetTop and Canvas.SetLeft position to the Left and Top property of an item. Every time the Transform is changed (since I've made it raise property changed notifications), I just need to call each Layer to change it's extent, so each item can recompute its position (BTW do you have a nice utility function to convert lat/long to X,Y on the canvas?). Due to databinding, their position is automatically updated.

When I'm satisfied with it, I'll send you a more complete example, but below you'll find a preview. It works pretty well, and I can turn layers on and off, use datatemplates to specify the view, and control visibility using properties. 

 

        <ItemsControl x:Name="layers" ItemsSource="{Binding GeoLayers}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="Transparent" IsItemsHost="True" IsHitTestVisible="False" Width="{Binding ElementName=mainGrid, Path=ActualWidth}" 
                            Height="{Binding ElementName=mainGrid, Path=ActualHeight}" ClipToBounds="True" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ItemsControl ItemsSource="{Binding Items}" >
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Canvas Background="Transparent" IsItemsHost="True" IsHitTestVisible="False" Width="{Binding ElementName=mainGrid, Path=ActualWidth}" 
                                        Height="{Binding ElementName=mainGrid, Path=ActualHeight}"  />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <!--Replaced with a '<DataTemplate DataType="{x:Type IPlugin:IconLayerItem}">' so I can change the view in a resource-->
                            <!--<ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Ellipse Fill="Blue" Width="25" Height="25" />
                                    <TextBlock Text="{Binding Name}" />
                                </StackPanel>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>-->
                        <!-- By default, each item is wrapped in a ContentPresenter (e.g. use Snoop to see this for yourself), 
                        so we need to set its Canvas.Top explicitly using styles.-->
                        <ItemsControl.ItemContainerStyle>
                            <Style>
                                <Setter Property="Canvas.Top" Value="{Binding Top}" />
                                <Setter Property="Canvas.Left" Value="{Binding Left}" />
                                <Setter Property="Control.Visibility" Value="{Binding Visible}" />
                                <Setter Property="Control.IsHitTestVisible" Value="True" />
                            </Style>
                        </ItemsControl.ItemContainerStyle>
                    </ItemsControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Control.Visibility" Value="{Binding Visible}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>

Cheers, Erik

 

 

Coordinator
Feb 28, 2010 at 11:30 PM
Edited Mar 1, 2010 at 7:39 AM

Hi Erik,

I was hoping to have some time to play with this during the weekend, but alas. So here are just some reactions.

The DepencyProperties are primarily used in the ViewModel role, but since the MapControl is a UserControl that could be defined in xaml it would also fit the View role. So maybe I should split the MapControl into a V and VM.  I need to dig deeper into MVVM.

I have not made my mind up about a lot of the things here. Adding an INotifyPropertyChanged to the MapTransform is something that has come up in other discussions. It might be a good option. Alternatively I could also completely encapsulate the MapTransform into in the MapControl. 

Do I understand correctly that you actually built some kind of renderer like this? Where a LayerItem is like a geometry?

Calculating the position of a LatLon coordinate is done in two steps. First project LatLon to the coordinate system of your map. You can use projnet for this  http://projnet.codeplex.com/.  If you use the OpenStreetMap layer from the samples the projection is SphericalMercator. Second you can use the MapTransform.WorldToMap method to calculate the screen location (but I guess you already use this).

If you have a working sample I would love to play with it. 

Paul.