edmolko Posted June 12, 2012 at 05:14 PM Report Share #462321 Posted June 12, 2012 at 05:14 PM Boas! Eu fiz uma aplicação em WPF de teste para tentar perceber porque é que as meus child viewmodels não são destruídos. A aplicação consiste numa view principal (main) que tem 3 botões: cada botão muda o child viewmodel. O problema é que o destrutor do child view model só é chamado quando o utilizador navega 2 vezes depois. Por exemplo: O child viewmodel inicial é o SolidWoods_VM; O utilizador clica em "Show Boards"; O destrutor de SolidWoods_VM não é chamado; O utilizador clica em "Show Acessories"; O destrutor de SolidWoods é chamado; O destrutor de Boards_VM não é chamado; Código: Main View: <Window.DataContext> <my:Main_VM /> </Window.DataContext> <Window.Resources> <DataTemplate DataType="{x:Type my:SolidWoods_VM}"> <my:SolidWoods /> </DataTemplate> <DataTemplate DataType="{x:Type my:Boards_VM}"> <my:Boards /> </DataTemplate> <DataTemplate DataType="{x:Type my:Acessories_VM}"> <my:Acessories /> </DataTemplate> </Window.Resources> <StackPanel> <Button Content="Show Solidwoods" Command="{Binding NavSolidWoods}"/> <Button Content="Show Boards" Command="{Binding NavBoards}"/> <Button Content="Show Acessories" Command="{Binding NavAcessories}"/> <ContentControl Content="{Binding CurrentView}" /> </StackPanel> Main viewmodel: public class Main_VM : ViewModelBase { #region Fields private object _currentView; public object CurrentView { get { return _currentView; } set { if (_currentView != value) { _currentView = value; NotifyPropertyChanged("CurrentView"); } } } #endregion #region Commands #region Declarations private DelegateCommand _navSolidWoods; public DelegateCommand NavSolidWoods { get { if (_navSolidWoods == null) _navSolidWoods = new DelegateCommand(ShowSolidWoods); return _navSolidWoods; } } private DelegateCommand _navBoards; public DelegateCommand NavBoards { get { if (_navBoards == null) _navBoards = new DelegateCommand(ShowBoards); return _navBoards; } } private DelegateCommand _navAcessories; public DelegateCommand NavAcessories { get { if (_navAcessories == null) _navAcessories = new DelegateCommand(ShowAcessories); return _navAcessories; } } #endregion #region Actions private void ShowSolidWoods(object o) { CurrentView = null; GC.Collect(); CurrentView = new SolidWoods_VM(); } private void ShowBoards(object o) { CurrentView = null; GC.Collect(); CurrentView = new Boards_VM(); } private void ShowAcessories(object o) { CurrentView = null; GC.Collect(); CurrentView = new Acessories_VM(); } #endregion #endregion public Main_VM() { CurrentView = new SolidWoods_VM(); } Exemplo de um childviewmodel: public class SolidWoods_VM { public SolidWoods_VM() { } ~SolidWoods_VM() { Console.WriteLine("SolidWoods_VM is dying..."); } } Link to comment Share on other sites More sharing options...
petvetbr Posted June 12, 2012 at 05:59 PM Report Share #462332 Posted June 12, 2012 at 05:59 PM Não tive tempo de pesquisar este caso em particular, porém a recomendação geral em .net é deixar sempre o garbage collector fazer o gerenciamento da memória e nunca chamar o GC.collect manualmente e também utilizar IDisposable ao invés de destrutores/desconstrutores. Existe um ebook do Ricky Leeks disponível gratuitamente que explica a razão disto. Procure por ".net memory management Ricky Leeks". Também existe um podcast do autor no .net rocks que é bem interessante. Fernando Lage Bastos - MCP/MCTS/MCPD Link to comment Share on other sites More sharing options...
edmolko Posted June 12, 2012 at 06:15 PM Author Report Share #462335 Posted June 12, 2012 at 06:15 PM (edited) Só usei o GC.Collect aqui para demonstrar que não é por causa dele que o child viewmodel não é destruído. Não consigo encontrar na net nenhuma maneira eficaz de matar um child viewmodel imediatamente... Edited June 12, 2012 at 06:15 PM by edmolko Link to comment Share on other sites More sharing options...
petvetbr Posted June 12, 2012 at 06:57 PM Report Share #462341 Posted June 12, 2012 at 06:57 PM Mas a idéia é não ficar tendo que se preocupar com isto, é só deixar o GC fazer o trabalho dele. Existe alguma razão pela qual você quer eliminar ele de forma explícita como está tentando? Fernando Lage Bastos - MCP/MCTS/MCPD Link to comment Share on other sites More sharing options...
edmolko Posted June 12, 2012 at 08:15 PM Author Report Share #462347 Posted June 12, 2012 at 08:15 PM Sim, eu estou a usar a mediator pattern implementada desta forma: http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/ Como esta pattern não possui nenhum método para "desregistar" a subscrição de mensagens, o child viewmodel, como não é destruído, continua a receber estas mensagens... Link to comment Share on other sites More sharing options...
bruno1234 Posted June 12, 2012 at 09:03 PM Report Share #462356 Posted June 12, 2012 at 09:03 PM No link que forneceste não é necessário desregistar a mensagem. Isso só é possível porque está a ser usada uma weak reference para o evento. No teu caso também tens uma weak reference? Matraquilhos para Android. Gratuito na Play Store. https://play.google.com/store/apps/details?id=pt.bca.matraquilhos Link to comment Share on other sites More sharing options...
edmolko Posted June 13, 2012 at 08:12 AM Author Report Share #462412 Posted June 13, 2012 at 08:12 AM Sim, tenho /// <summary> /// This class is an implementation detail of the MessageToActionsMap class. /// </summary> internal class WeakAction { readonly MethodInfo method; readonly Type delegateType; readonly WeakReference weakRef; /// <summary> /// Constructs a WeakAction /// </summary> /// <param name="target">The instance to be stored as a weak reference</param> /// <param name="method">The Method Info to create the action for</param> /// <param name="parameterType">The type of parameter to be passed in the action. Pass null if there is not a paramater</param> internal WeakAction(object target, MethodInfo method, Type parameterType) { //create a Weakefernce to store the instance of the target in which the Method resides weakRef = new WeakReference(target); this.method = method; // JAS - Added logic to construct callback type. if (parameterType == null) this.delegateType = typeof(Action); else this.delegateType = typeof(Action<>).MakeGenericType(parameterType); } /// <summary> /// Creates a "throw away" delegate to invoke the method on the target /// </summary> /// <returns></returns> internal Delegate CreateAction() { object target = weakRef.Target; if (target != null) { // Rehydrate into a real Action // object, so that the method // can be invoked on the target. return Delegate.CreateDelegate( this.delegateType, weakRef.Target, method); } else { return null; } } /// <summary> /// returns true if the target is still in memory /// </summary> public bool IsAlive { get { return weakRef.IsAlive; } } } MessageToActionsMap /// <summary> /// This class is an implementation detail of the Mediator class. /// This will store all actions to be invoked /// </summary> internal class MessageToActionsMap { //store a hash where the key is the message and the value is the list of Actions to call readonly Dictionary<string, List<WeakAction>> map = new Dictionary<string, List<WeakAction>>(); /// <summary> /// Adds an action to the list /// </summary> /// <param name="message">The message to register to </param> /// <param name="target">The target object to invoke</param> /// <param name="method">The method in the target object to invoke</param> /// <param name="actionType">The Type of the action</param> internal void AddAction(string message, object target, MethodInfo method, Type actionType) { if (message == null) throw new ArgumentNullException("message"); if (method == null) throw new ArgumentNullException("method"); lock (map)//lock on the dictionary { if (!map.ContainsKey(message)) map[message] = new List<WeakAction>(); map[message].Add(new WeakAction(target, method, actionType)); } } /// <summary> /// Gets the list of actions to be invoked for the specified message /// </summary> /// <param name="message">The message to get the actions for</param> /// <returns>Returns a list of actions that are registered to the specified message</returns> internal List<Delegate> GetActions(string message) { if (message == null) throw new ArgumentNullException("message"); List<Delegate> actions; lock (map) { if (!map.ContainsKey(message)) return null; List<WeakAction> weakActions = map[message]; actions = new List<Delegate>(weakActions.Count); for (int i = weakActions.Count - 1; i > -1; --i) { WeakAction weakAction = weakActions[i]; if (!weakAction.IsAlive) weakActions.RemoveAt(i); else actions.Add(weakAction.CreateAction()); } //delete the list from the hash if it is now empty if (weakActions.Count == 0) map.Remove(message); } return actions; } } No entanto a condição if(!weakAction.IsAlive) é sempre falsa porque IsAlive é sempre = true; No 1º exemplo deste tópico nem sequer estou a utilizar a mediator pattern. O que eu acho é que o binding no ficheiro .xaml, de alguma forma mantém uma referência ao child viewmodel que eu não sei como remover. <ContentControl Content="{Binding CurrentView}" /> Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now