Visualization layers are removed from the RadMap in a moment when MapShapeData objects are being processes (their geometries are processed). This leads to exception in MapShapeData.cs ' SetLogicalWidth method. Available in R2 2017 SP
Hi Thomas,
Glad to hear your team has decided to upgrade. I will make sure the mentioned protected methods will get into the R1 2020 SP release later this month.
I know you might not be using it but they might be helpful for other users. Also if we end up with a better fix for the old issue regarding the disconnection of layer, I will make sure this is noted somehow in this thread.
Regards,
Petar Mladenov
Progress Telerik
Hello Petar,
Thank you very much.
We discussed on our side the need to update our license if this problem is our only priority or not and we figured out that the latest version will give us a lot of helpful component/upgrade besides this problem so we decided to renew for 1 year our license.
Until your fix, we will use your first solution (CustomRadMap) to be able to use the map the same way we do right now but we hope you can find a solution to it.
I will stay tuned to this thread to wait your update.
Thank you for your great support
Regards,
Thomas
Hello Thomas,
Yes we clearly understand your point. We will discuss if we can provide a better fix for the issue in 2017 and will post an update here.
Regards,
Petar Mladenov
Progress Telerik
Hello Petar,
Thanks for your answer but maybe I wasn't clear on what we want from the start sorry.
Right now with our version 2016.R3 we can work with layer with minimal errors (bypass for SetLogicalWidth and ClearScaleTransformOnShape hidden to the user), it's not really beautiful but it works.
From 2017.R3, to "fix" SetLogicalWidth you broke a basic usage of the map (we can no longer attaching, detaching same instance layer)
Now we are looking to upgrade our version of Telerik to get all the fixes found from 2016 and all improvements for components.
But for that we don't want to add more work around code to Telerik's components to use them, we would prefer to remove our work around instead.
I'm also not in a rush to update our version so if you need time to discuss on your side if you can find a better way to fix this thread issue (where the source of the problem is known as you said the MapControl of layer is null) without breaking usage of the map, it's fine for me I can wait a new release.
My goal was mostly to inform you that this fix broke something else.
As for the solution you suggested, it's a work around to the broken behavior.
Moreover it will add some more complex behavior from that work around (we would need on our side to manage the life time of layer still attached by your component), so a work around on another work around it's never ending
Now I just want to ask you as a "Feature request" or "Bug Report" :
Does the broken behavior :
could be fixed in a future version ?
Thanks you for your time until now, you tried to fix my problem right now and it's really nice.
I hope that this is more clear now ?
Regards,
Thomas
Hi Thomas,
Both reported exceptions have same origin - when layer is removed from the Map, its MapControl property is set to null (and both exception are due to null mapcontrol at a later stage):
/// <summary>
/// Undoes the effects of the PrepareContainerForItemOverride method.
/// </summary>
/// <param name="element">The container element.</param>
/// <param name="item">The item.</param>
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
base.ClearContainerForItemOverride(element, item);
ILayer layer = element as ILayer;
if (layer != null)
{
if (this.MasterImage != null)
{
this.SetupMultiscaleImageEventsToLayer(layer, true);
}
VisualizationLayer vizLayer = layer as VisualizationLayer;
if (vizLayer != null)
{
// VisualizationLayer should be disposed which stops its background threads.
// Otherwise exception might be hit cause these threads need live MapControl reference.
vizLayer.Dispose();
}
layer.MapControl = null;
}
}
In your app you can handle this by overriding this ClearContainerForItemOverride method:
public class CustomMap : RadMap
{
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
}
}
I can see you have ended up with such solution on your own but you miss the ClearContainers method. That is why I can suggest the following new protected method (DisposeContainers):
/// <summary>
/// Called when the layer is being disposed.
/// </summary>
protected virtual void OnDispose()
{
this.StopBackgroundThread();
this.DisposeContainers();
}
/// <summary>
/// Called when visual containers are being disposed.
/// </summary>
protected virtual void DisposeContainers()
{
foreach (int zoomLevel in this.layerCells.Keys)
{
Dictionary<int, MapLayerCell> zoomLevelCells = this.layerCells[zoomLevel];
foreach (MapLayerCell cell in zoomLevelCells.Values)
{
cell.Dispose();
}
zoomLevelCells.Clear();
}
this.RemoveAllInternal();
}
You can find it added in the attached dlls. Let us know if this helps you move forward.
Regards,
Petar Mladenov
Progress Telerik
Hello Petar,
Thank you for the dlls but unfortunately I reproduced every error I got in 2016.R3
To be sure that this will fix our problem or not I reproduced in my test project the error we got the more often when manipulating layers:
- MapShapeData.SetLogicalWidth (I explain later how we bypass this error with our override)
- MapShapeData.ClearScaleTransformOnShape : we don't know how to fix this one
We also have other errors that we fixed by overriding the VisualizationLayer (class MTDVisualizationLayer in the project), I will explain later what we do in it that could be helpful for you
After that I tried your new dlls, without overriding classes we don't have any error but the the simple actions Attach and Detach the same instance layer doesn't work as we previously said.
If I override the layer OnDispose() method to do only StopBackgroundThread then attach/detach a layer works but we get both error listed above
If we also override the ClearContainerForItemOverride of RadMap we don't have the errors but Layers will never be cleaned in memory as it will always be attached to the Map from a Property inside the layer and we tried to clean our layers when we can as they can be heavy
Also I didn't find the method ClearLayerContainers you talked about (in VisualizationLayer or RadMap) ?
To reproduce errors, I send you my test project. The code is ready to use with your dll, you will maybe need to reference the Telerik dll on your side.
First I made a modification from the last one, now the layer display a PolygonData as we need a MapShapeData to reproduce the error
The process done by the buttons below reproduce what can happen in our application's basic usage
Step to reproduce MapShapeData.SetLogicalWidth:
Step to reproduce MapShapeData.ClearScaleTransformOnShape:
In the project you can found our override of the VisualizationLayer (class MTDVisualizationLayer) we use in our projects if you want to check what we do.
There is a lot of stuff inside so it's maybe not easy to read but here is a summarize:
Method UpdateHotSpot solved an issue of position when the size of the visual component of the item in the layer changed (I didn't try with the dlls you sent us if this is fixed)
We reimplemented new selection mode Simple, Multiple and Extended, to add some behavior like a circular selection when item are overlapping with each other
If you want to use our MTDVisualizationLayer, you need to change this in MainWindow.xaml.cs :
So your dlls don't fix the error of MapShapeData if we want to keep all components inside the layer
If you want more information on the errors or the test project, feel free to ask
Regards,
Thomas
Hi Thomas,
I am sending the two dlls you need to build your project:
Telerik.Windows.Conmtrols.dll
Telerik.Windows.Controls.DataVisualization.dll
Regards,
Petar Mladenov
Progress Telerik
Hello Petar,
Ok that's great, this solution should be okay.
Yes I would be glad to test it on my test project (the one I sent you)
I will also check if it still fixes this thread's bug with the custom code.
Thank you very much
Regards,
Thomas
Hello Thomas,
Yes you will need to make the following override:
public class CustomLayer : VisualizationLayer
{
protected override void OnDispose()
{
this.StopBackgroundThread();
}
}
There is no need to support the reload process because this is done internally:
private void VisualizationLayerLoaded(object sender, RoutedEventArgs e)
{
if (this.VirtualizationSource != null)
{
this.StartBackgroundThread();
}
}
private void VisualizationLayerUnloaded(object sender, RoutedEventArgs e)
{
this.StopBackgroundThread();
}
Actually these background thread only works when the Virtualization source is used. We can send you a trial dlls these days so you can do test on your side. How does this sound ?
Regards,
Petar Mladenov
Progress Telerik
Hello Petar,
If I understandcorrectly, when overriding VisualizationLayer I will be able to do this code :
protected override void OnDispose()
{
StopbackgroundThreads();
}
I will not use ClearLayerContainers for my purpose to keep alive all the VisualTree's items but this method will be available as a protected method if I need it at some point.
When I reattach the layer do I have to call something to restart the background threads as it will be stopped ?
The layer will be already loaded so maybe it will not be automatic and I need it to update MapShapeData and other items in the layer I think ?
Thank you
Regards,
Thomas
Hi Thomas,
Forum is pretty much similar to this portal so no need to open new post. I will suggest a new API here so it might be useful to other users using the Map.
So pretty much the Dispose of the VisualizationLayer consist of 2 steps:
public void Dispose() { 1 - stop background threads 2 - dispose and clean the related cells in the map }
Stopping the background threads resolves the exception which might happen in the MapShapeData (with constant geometry updates) during unloading Map / Layer.
Step 2 clears the UI that is prepared for the layer and then needs recreation if the same layer is added back to the map.
If we create protected methods , for example OnDispose / StopbackgroundThreads / ClearLayerContainers you will have more control when Layer is disposed and should be able to retain the performance you need without thread exception. Let me know what you think about this approach.
Regards,
Petar Mladenov
Progress Telerik
Hello Petar
Thank you for your answer but this is not a solution for us and not because we can't do a CustomRadMap but because if we upgrade our version, this is mostly to fix the bug of this thread : MapShapeData.SetLogicalWidth error
The fix you suggest to me remove the "fix" done in the version 2017.R3 so it will be the same as not to upgrade at all.
As we have an old version we don't have support anymore so I can't create a new support thread except in the forum if it's what you suggest ?
Also we wanted to upgrade to fix the MapShapeData's bug, as we have an old version we would like to upgrade to a complete new license to get the latest version but as this fix will impact us too much we can't do that either
For now, I can continue with our current version if there is no possibility to fix quickly the impact of the MapShapeData's fix
But is it possible to work on a better way to fix this issue in a future version that doesn't impact adding/removing a simple layer without recreating everything (so no reset of the ItemsSource either) ?
We could wait the next release and then try it to upgrade our version
As for now we will continue with our current version 2016.R3.
Thank you for your time
Regards
Thomas
Hello Thomas,
Initializing the items from itemssource and generating the containers for them begins in the ItemsSourceChanged internal method of VisualizationLayer. When you detach and attach, containers are cleared (disposed) but itemssource remains the same and this is why no items are visible in the viewport.
You can make the following test - on detach - set the itemssource to null and on attach - reset the itemssource. (Uncomment the commented lines):
private void AttachLayerButton_OnClick(object sender, RoutedEventArgs e)
{
if (Layer == null)
{
Layer = CreateLayer();
}
else
{
//Layer.SetBinding(VisualizationLayer.ItemsSourceProperty, new Binding(nameof(DataSource.Items)) { Source = _dataContext });
}
if (!Map.Items.Contains(Layer))
Map.Items.Add(Layer);
Layer.UpdateLayout();
}
private void DetachLayerButton_OnClick(object sender, RoutedEventArgs e)
{
if (Layer == null)
return;
// this.Layer.ItemsSource = null;
Map.Items.Remove(Layer);
}
However, I guess this won't lead to the performance level you need for your application.
Instead , I wanted to propose the following work around for not disposing the layer. What I have missed in my previous post is that the Dispose is also called when the layer is removed from the items collectiton of the map (actually what you do in your app). So the idea is to override the ClearContainerForItemOverride method and do no call the base implementation which disposes the layer:
public class CustomMap : RadMap
{
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
}
}
I hope this could be a valid way for you to upgrade. Also I wanted to note that you better open a new support thread where you will usually receive a faster response. Thank you for your understanding.
Regards,
Petar Mladenov
Progress Telerik
Hello Petar
I do a simple Add or Remove in the property Items of RadMap.
If I do Add, Remove then Add the same instance of VisualizationLayer then items don't show up in the map
After some digging, I found that the layer Cells of the VisualizationLayer where the information of items are kept is empty after the Remove and for some reason cannot be repopulate when it is reattached to the map.
I also tried to call the method ResetItems(true) of the VisualizationLayer but there is no changes
I created a little project showing the problem, I succeed to reproduce it with a really basic example
Use button to Attach and Detach the layer created in code behind
For your last question I didn't call any Dispose method manually.
To add some context of my problem, we use intensively the map in our application as it is the core of it. We have so many layers of items (one layer can extend to 1000 items inside) that we can't keep them all in the map because it will impact the performance, the VisualTree being too big + Refresh of map (move, zoom, etc...) + the rest of the application.
We created a service that manages which layer is currently in the map or not and other properties of layers, for example we also used the Visibility in some cases but we can't do that everytime
PS: I only tried with version 2017.R3 of Telerik (latest version available with my license) and my sample has this problem with this version.
Regards
Thomas
Hello Thomas,
VisualizationLayer is disposed when Map's Dispose method is invoked. Internally, there is no call to the Dispose method of RadMap. Couls you please add more details regarding your scenario - how do you populate Map with Layers, Layers with items and how do you remove them / re-add them to the map? Do you call any Dispose method manually in your application ?
Regards,
Petar Mladenov
Progress Telerik
Hello,
I tried to upgrade my version of Telerik 2016.R3 to 2017.R3 to fix this bug.
But I noticed now that the layer is disposed when disconnected from the map
This behavior is really annoying as it forces me to recreate the whole layer and all children components.
So when we manipulate a layer with many elements inside it freezes for a long time the application as it needs to recreate everything inside (visual tree).
Is there a way to disconnect a layer from the map without disposing it (so all the visual tree is kept in memory to not have to recreate it) and disconnect all events that update children elements to avoid this bug ?
Thank you