2014-07-11 76 views
1

在我的應用程序中,我使用多個Bing Maps WPF控件顯示一層圖釘。我使用MVVM,並將地圖放置在可由用戶打開和關閉的視圖中。當視圖關閉時,地圖將從視覺樹中移除,然後妥善處置。Bing地圖WPF控制內存泄漏

然而,在關閉視圖後,它們似乎在內存中保持着。在使用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,如果我找到解決方案,將更新這個問題。

+0

我不認爲這很重要,如果'Map'有一個引用視圖;如果有任何引用仍然存在,那麼'Map'不能被垃圾回收。另外,Map無法收集垃圾,除非運行該對象所在的任何代碼的集合。像WPF控件這樣的對象可能會出現在第2代,這是運行頻率最低的一代。 –

+0

感謝您的建議。問題是'Map'已經超出了範圍。剩下的唯一強大參考是AnimationDriver使用屬性描述符。 –

回答

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