在我的应用程序中,我使用多个Bing Maps WPF控件显示一层图钉。我使用MVVM,地图放在一个可由用户打开和关闭的视图中。当视图关闭时,地图将从可视树中删除,然后正确处理。
然而,在关闭视图后,它们似乎保留在内存中。使用Memory Profiler进行检查后,地图会以某种方式保留对视图的引用,因此不会将其删除。
我做了一个简单的测试应用来证明泄漏:
public partial class Window1 : Window
{
private Map map;
public Window1()
{
InitializeComponent();
map = new Map();
map.CredentialsProvider = new ApplicationIdCredentialsProvider("apikey");
map.AnimationLevel = AnimationLevel.None;
map.SetView(new Location(2, 2), 10);
this.Content = map;
}
protected override void OnClosed(EventArgs e)
{
this.Content = null;
map.Dispose();
base.OnClosed(e);
}
}
从辅助窗口使用Window1.ShowDialog();
打开窗口。
以下图片显示了打开和关闭其他几个窗口后第一个窗口的参考地图,所有这些都调用map.Dispose();
)
这确实是地图中的错误吗?你知道一种强制地图真正删除所有强引用的方法吗?我尝试从地图选项中禁用多个,例如关闭动画,触摸翻译等。
修改 我在反编译的控制源中做了一些研究。看来引用是在AnimationDriver类中创建的,并且是由使用PropertyDescriptor引起的,您可能知道它会引起强引用。我将搜索一个删除PropertyDescriptor的解决方案,并在我找到解决方案时更新问题。
答案 0 :(得分:1)
调用以下内容将删除动画驱动程序引用:
TypeDescriptor.Refresh(map)
我发现我仍然得到MapConfiguration对象引起的一些引用。我设法使用反射删除了那些:
using Microsoft.Maps.MapControl.WPF;
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
public static class BingMapsKiller
{
public static void Kill(Map map)
{
try
{
TypeDescriptor.Refresh(map);
map.Dispose();
var configType = typeof(Microsoft.Maps.MapControl.WPF.Core.MapConfiguration);
var configuration = configType.GetField("configuration", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetValue(null);
var requestQueue = configuration.GetFieldValue("requestQueue");
var values = (System.Collections.IEnumerable)requestQueue.GetPropertyValue("Values");
foreach (System.Collections.IEnumerable requests in values)
foreach (var request in requests.OfType<object>().ToList())
{
var target = request.GetPropertyValue("Callback").GetPropertyValue("Target");
if (target == map)
requests.ExecuteMethod("Remove", request);
else if (target is DependencyObject)
{
var d = (DependencyObject)target;
if (d.HasParentOf(map))
requests.ExecuteMethod("Remove", request);
}
}
}
catch { }
}
private static Object GetFieldValue(this Object obj, String fieldName)
{
var type = obj.GetType();
return type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(obj);
}
private static Object GetPropertyValue(this Object obj, String fieldName)
{
var type = obj.GetType();
return type.GetProperty(fieldName, BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | BindingFlags.Instance).GetValue(obj);
}
private static Object ExecuteMethod(this Object obj, String methodName, params object[] parameters)
{
var type = obj.GetType();
return type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Invoke(obj, parameters);
}
private static Boolean HasParentOf(this DependencyObject obj, DependencyObject parent)
{
if (obj == null)
return false;
if (obj == parent)
return true;
return VisualTreeHelper.GetParent(obj).HasParentOf(parent);
}
}
答案 1 :(得分:1)
使用以下方法有效,但它有一些副作用。例如,如果您有多个地图并为其中一个调用方法,则其他地图将禁用动画(缩放,滚动)。
TypeDescriptor.Refresh(map)
为我解决问题的方法是直接使用反射访问AnimationDrivers,然后从DependencyPropertyDescriptor取消订阅OnAnimationProgressChanged处理程序。
public static class BingMapsFix
{
public static void UnhookAnimationDrivers(Map map)
{
Type type = typeof(MapCore);
object zoomAndPanAnimationDriver = type.GetField("_ZoomAndPanAnimationDriver", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(map);
object modeSwitchAnationDriver = type.GetField("_ModeSwitchAnationDriver", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(map);
UnhookAnimationDriver(zoomAndPanAnimationDriver);
UnhookAnimationDriver(modeSwitchAnationDriver);
}
private static void UnhookAnimationDriver(object animationDriver)
{
Type type = animationDriver.GetType();
var f = type.GetField("AnimationProgressProperty", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField).GetValue(animationDriver);
DependencyProperty dp = (DependencyProperty)f;
var m = type.GetMethod("OnAnimationProgressChanged", BindingFlags.Instance | BindingFlags.NonPublic);
EventHandler eh = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), animationDriver, m);
DependencyPropertyDescriptor.FromProperty(dp, type).RemoveValueChanged(animationDriver, eh);
}
}