Bing Maps WPF控制内存泄漏

时间:2014-07-11 09:38:52

标签: c# wpf memory-leaks bing-maps idisposable

在我的应用程序中,我使用多个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();)  Memory map

这确实是地图中的错误吗?你知道一种强制地图真正删除所有强引用的方法吗?我尝试从地图选项中禁用多个,例如关闭动画,触摸翻译等。

修改 我在反编译的控制源中做了一些研究。看来引用是在AnimationDriver类中创建的,并且是由使用PropertyDescriptor引起的,您可能知道它会引起强引用。我将搜索一个删除PropertyDescriptor的解决方案,并在我找到解决方案时更新问题。

2 个答案:

答案 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);
    }
}