It seems that the BingMapProvider has a few problems that cause memory leaks.
Please find some sample code below.
My sample application basically contains a grid to which I add a 'MapWrapper' control. Every 3 seconds, the MapWrapper is removed, and a new MapWrapper is added.
The MapWrapper is a simple control that contains a RadMap.
There are two buttons shown on top of the MapWrapper.
The first button allows to turn on/off the 3-second timer.
The second button allows to switch between a BingMapProvider and an OpenStreetMapProvider. Every time a new MapWrapper is instantiated, a provider of the chosen type is set as provider for the RadMap.
Letting the application run for a while using OSM, there are no problems.
However, when using the BingMapProvider, two problems occur:
1) Not a single instance of BingMapProvider is ever released, and this creates a huge memory leak, especially in our live application where the Map control has several layers of data.
(OpenStreetMapProvider objects on the other hand are always removed -- even without any custom implementation of Dispose)
2) Frequently and irregularly, WinDbg shows the following two lines while creating the BingMapProvider:
(604.c74): CLR exception - code e0434352 (first chance)
(604.1868): CLR exception - code e0434352 (first chance)
Every time that this exception occurs, the map simply fails to show anything.
Both problems are huge game breakers and prevent us from using Bing in our application.
One thing I haven't tried is setting the Bing provider in XAML instead of in code behind -- who knows it might solve the issue. One reason is that I'm just not sure how to do that (with OSM it's pretty straightforward, but Bing requires a key and stuff). The second reason is that in our live application we want our client to be able to switch between providers on the fly, so we HAVE to be able to it in code-behind anyway.
I've already created a support ticket for this issue, but I'm posting it here to in case anyone has any ideas.
Please find some sample code below.
My sample application basically contains a grid to which I add a 'MapWrapper' control. Every 3 seconds, the MapWrapper is removed, and a new MapWrapper is added.
The MapWrapper is a simple control that contains a RadMap.
There are two buttons shown on top of the MapWrapper.
The first button allows to turn on/off the 3-second timer.
The second button allows to switch between a BingMapProvider and an OpenStreetMapProvider. Every time a new MapWrapper is instantiated, a provider of the chosen type is set as provider for the RadMap.
Letting the application run for a while using OSM, there are no problems.
However, when using the BingMapProvider, two problems occur:
1) Not a single instance of BingMapProvider is ever released, and this creates a huge memory leak, especially in our live application where the Map control has several layers of data.
(OpenStreetMapProvider objects on the other hand are always removed -- even without any custom implementation of Dispose)
2) Frequently and irregularly, WinDbg shows the following two lines while creating the BingMapProvider:
(604.c74): CLR exception - code e0434352 (first chance)
(604.1868): CLR exception - code e0434352 (first chance)
Every time that this exception occurs, the map simply fails to show anything.
Both problems are huge game breakers and prevent us from using Bing in our application.
One thing I haven't tried is setting the Bing provider in XAML instead of in code behind -- who knows it might solve the issue. One reason is that I'm just not sure how to do that (with OSM it's pretty straightforward, but Bing requires a key and stuff). The second reason is that in our live application we want our client to be able to switch between providers on the fly, so we HAVE to be able to it in code-behind anyway.
I've already created a support ticket for this issue, but I'm posting it here to in case anyone has any ideas.
<
UserControl
x:Class
=
"TestRadMapBlurriness.MainPage"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"400"
>
<
Grid
x:Name
=
"LayoutRoot"
Background
=
"White"
>
<
Grid
Name
=
"GridMap"
/>
<
StackPanel
HorizontalAlignment
=
"Left"
VerticalAlignment
=
"Top"
Margin
=
"30"
>
<
Button
Name
=
"BtnTimer"
Content
=
"Start"
Click
=
"BtnTimer_Click"
MinWidth
=
"120"
/>
<
Button
Name
=
"BtnProvider"
Content
=
"BING"
Click
=
"BtnProvider_Click"
MinWidth
=
"120"
/>
</
StackPanel
>
</
Grid
>
</
UserControl
>
public
partial
class
MainPage : UserControl
{
public
MainPage()
{
InitializeComponent();
// Prepare the timer that will recreate a new MapWrapper every x seconds
_refreshDataTimer =
new
DispatcherTimer { Interval =
new
TimeSpan(0, 0, 3) };
_refreshDataTimer.Tick += refreshDataTimer_Tick;
// Label the buttons
BtnTimer.Content =
"Start"
;
BtnProvider.Content =
"BING"
;
}
private
DispatcherTimer _refreshDataTimer;
void
refreshDataTimer_Tick(
object
sender, EventArgs e)
{
Refresh();
}
private
int
_refreshCounter = 0;
private
void
Refresh()
{
// Remove any existing maps
while
(
true
)
{
MapWrapper mapWrapper = GridMap.Children.OfType<MapWrapper>().FirstOrDefault();
if
(mapWrapper ==
null
)
break
;
Debug.WriteLine((_refreshCounter).ToString() +
": {0:x16} Removing MapWrapper"
, mapWrapper.GetHashCode());
mapWrapper.Dispose();
GridMap.Children.Remove(mapWrapper);
}
// Add a new map
MapWrapper newMapWrapper =
new
MapWrapper(_providerName);
GridMap.Children.Add(newMapWrapper);
Debug.WriteLine((_refreshCounter++).ToString() +
": {0:x16} Added new MapWrapper"
, newMapWrapper.GetHashCode());
}
/// <summary>
/// Start/stop the timer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private
void
BtnTimer_Click(
object
sender, RoutedEventArgs e)
{
if
(BtnTimer.Content.ToString() ==
"Stop"
)
{
_refreshDataTimer.Stop();
BtnTimer.Content =
"Start"
;
}
else
{
_refreshDataTimer.Start();
BtnTimer.Content =
"Stop"
;
}
}
private
string
_providerName =
""
;
/// <summary>
/// Change the provider. The next new MapWrapper will be initialized with the new provider.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private
void
BtnProvider_Click(
object
sender, RoutedEventArgs e)
{
if
(BtnProvider.Content.ToString() ==
"BING"
)
{
_providerName =
"OpenStreetMapProvider"
;
BtnProvider.Content =
"OSM"
;
}
else
{
_providerName =
""
;
BtnProvider.Content =
"BING"
;
}
}
}
<
UserControl
x:Class
=
"TestRadMapBlurriness.MapWrapper"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"400"
>
<
Grid
x:Name
=
"LayoutRoot"
Background
=
"White"
>
<
telerik:RadMap
x:Name
=
"RadMap1"
Center
=
"38.8763793426777, -77.0368488348389"
ZoomLevel
=
"14"
MiniMapExpanderVisibility
=
"Collapsed"
>
<!--<telerik:RadMap.Providers>-->
<!--<telerik:OpenStreetMapProvider />-->
<!--<telerik:BingMapTrafficProvider />-->
<!--</telerik:RadMap.Providers>-->
</
telerik:RadMap
>
</
Grid
>
</
UserControl
>
public
partial
class
MapWrapper : UserControl, IDisposable
{
private
string
_providerName =
""
;
public
MapWrapper(
string
providerName)
{
InitializeComponent();
_providerName = providerName;
SetProvider();
}
private
const
double
BaseAerialLayerOpacity = 1.0;
private
const
double
RoadLayerOpacity = 0.5;
private
void
SetProvider()
{
MapProviderBase provider;
if
(_providerName ==
"OpenStreetMapProvider"
)
{
provider =
new
OpenStreetMapProvider();
}
else
{
var bingMapId = ((Microsoft.Maps.MapControl.ApplicationIdCredentialsProvider)App.Current.Resources[
"AppId"
]).ApplicationId;
BingMapProvider baseAerialProvider =
new
BingMapProvider(MapMode.Aerial,
true
, bingMapId);
provider = baseAerialProvider;
}
this
.RadMap1.Provider = provider;
}
#region Dispose
private
bool
_disposed =
false
;
public
void
Dispose()
{
Dispose(
true
);
GC.SuppressFinalize(
this
);
}
protected
virtual
void
Dispose(
bool
disposing)
{
try
{
Debug.WriteLine(
"Dispose("
+ disposing.ToString() +
") {0:x16}"
,
this
.GetHashCode());
if
(!_disposed)
{
if
(disposing)
{
// Free managed objects
//this.RadMap1.Providers.Clear();
//this.RadMap1.Provider = null;
RadMap1.Dispose();
Debug.WriteLine(
"Dispose manually {0:x16}"
,
this
.GetHashCode());
}
else
{
}
//this.RadMap1.Providers.RemoveAll();
//Debug.WriteLine("this.RadMap1.Providers.RemoveAll();");
_disposed =
true
;
}
}
catch
(Exception ex)
{
Debug.WriteLine(
"Dispose("
+ disposing.ToString() +
") failed: "
+ ex.Message);
}
}
~MapWrapper()
{
Dispose(
false
);
}
#endregion