2013-02-24 2 views
0

Я создал приложение Prism с использованием WPF, .Net 4, Prism 4.1 и Unity. Я использую DirectoryModuleCatalog для поиска модулей во время выполнения. Мои представления отображаются в TabControl (MainRegion). Когда я удаляю представление из региона, view и viewmodel остаются в памяти и никогда не получают сбор мусора - tabitem удаляется. После многих часов поиска я не могу понять, что я делаю неправильно.WPF & Prism 4.1 Сбор мусора/проблемы с памятью

Вот мой Загрузчик:

public class Bootstrapper : UnityBootstrapper 
{ 
    protected override void InitializeShell() 
    { 
     base.InitializeShell(); 
     App.Current.MainWindow = (Window)Shell; 
     App.Current.MainWindow.Show(); 
    } 

    protected override DependencyObject CreateShell() 
    { 
     var shell = new Shell(); 
     return shell; 
    } 

    protected override IModuleCatalog CreateModuleCatalog() 
    { 
     return new DirectoryModuleCatalog() { ModulePath = @".\Modules" }; 
    } 
} 

Вот мой модуль:

[Module(ModuleName = "ModuleA")] 
public class Module : IModule 
{ 
    private IRegionManager _regionManager; 

    public Module(IRegionManager regionManager) 
    { 
     _regionManager = regionManager; 
    } 

    public void Initialize() 
    { 
     var view = new UserControl1(); 
     //_regionManager.RegisterViewWithRegion("MainRegion", typeof(UserControl1)); 
     _regionManager.Regions["MainRegion"].Add(view, "ModuleA"); 
     _regionManager.Regions["MainRegion"].Activate(view); 
    } 
} 

И Херес ViewModel для моей точки зрения, что будет добавлено в регионе:

public class ViewModel 
{ 
    public DelegateCommand RemoveView { get; set; } 

    public ViewModel() 
    { 
     RemoveView = new DelegateCommand(() => 
      { 
       var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>(); 
       var view = regionManager.Regions["MainRegion"].GetView("ModuleA"); 
       regionManager.Regions["MainRegion"].Deactivate(view); 
       regionManager.Regions["MainRegion"].Remove(view); 
      }); 
    } 
} 

И вот код сзади для представления:

public partial class UserControl1 : UserControl 
{ 
    public UserControl1() 
    { 
     InitializeComponent(); 

     this.DataContext = new ViewModel(); 
    } 
} 

Я читал, что это может быть потому, что я создаю представление в модуле или, возможно, в viewmodel в представлении? Когда я использую Profiler Profiler Red Gate и удаляю представление через DelegateCommand, модель представления и viewmodel помечены как неспособные к сбору мусора. Где ссылка, которую я не правильно разрезаю?

Heres сохранение графика от Муравьев: https://docs.google.com/file/d/0B4XjO9pUQxBXbGFHS1luNUtyOTg/edit?usp=sharing

Вот test solution показывая вопрос.

Кроме того, я разместил вопрос на CodePlex.

+0

Дикий удар в темноте: HTTP: // StackOverflow.com/questions/516617/what-is-the-weak-event-pattern-used-in-wpf-applications –

+1

Эй, Крис. Я посмотрел на ваш проект. Похоже, ваш ViewModel реализует IDisposable. Я обнаружил, что это может иметь некоторые странные последствия для жизни, если Dispose() не вызывается (WPF, в отличие от WinForms, не очень использует IDisposable). IDisposable хорош для таких вещей, как закрытие неуправляемых ресурсов, таких как соединения с БД, и т. Д., Но для этого есть лучшие методы. Попробуйте взять его и посмотреть, что произойдет. –

+0

Еще одна вещь, которую я должен упомянуть, заключается в том, что вы должны убедиться, что вы являетесь хорошим измерением на всю жизнь своего объекта. Коллекция не очень детерминирована, и я нашел в приложениях WPF, что иногда может потребоваться некоторое время для сбора объекта. Ваш график в ANT выглядит нормально - вещи, имеющие ссылки на вашу модель просмотра, выглядят так, что там где-то есть разрыв WeakReference, поэтому они не должны закреплять объект. –

ответ

0

Наконец нашел причину моей проблемы ....

В нашем Shell.xaml мы вяжем IsDefault в одном из наших кнопок IsKeyboardFocused PasswordBox в:

<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" /> 

IsKeyboardFocused, является свойство зависимости в соответствии с MSDN - так должно быть хорошо с этой целью.

Это связано с приложением, которое мы имели в поле «Пароль», которое позволяет нам привязываться к введенному паролю. Основное внимание было уделено этому паролю даже после того, как мы спрятали ChildWindow (из расширенного набора инструментов WPF). Поэтому вместо использования IsDefault я добавил событие keydown в PasswordBox, и если бы это был Key.Enter, я бы сменил сфокусированный пользовательский интерфейс и зарегистрировал человека в программе.

Вот полное содержание нашего Shell.xaml файла

<Grid x:Name="MainGrid" core:SharedResourceDictionary.MergedDictionaries="TabControlThemes;MenuThemes;ButtonThemes;DataGridThemes;TreeViewThemes;ComboBoxThemes;ListBoxThemes;GroupBoxThemes;ToggleSwitchThemes"> 

    <DockPanel> 
     <ContentControl x:Name="menuContent" DockPanel.Dock="Top" prism:RegionManager.RegionName="MenuRegion" /> 
     <ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="FooterRegion" /> 
     <ContentControl DockPanel.Dock="Top" prism:RegionManager.RegionName="MainContentRegion" /> 
    </DockPanel> 

    <StackPanel Orientation="Horizontal" Panel.ZIndex="4" HorizontalAlignment="Right" VerticalAlignment="Top"> 
     <Button Visibility="{Binding IsFullScreenToggleVisible, Converter={StaticResource visConv}}" Command="{Binding ToggleFullScreen}" Height="50" Name="button4" Width="70" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top"> 
      <Button.Content> 
       <TextBlock FontSize="12" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text=" Toggle Full Screen" /> 
      </Button.Content> 
     </Button> 

     <Button Visibility="{Binding IsAppCloseButtonVisible, Converter={StaticResource visConv}}" Command="{Binding ShutdownApplication}" Height="50" Name="button3" Width="50" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top"> 
      <Button.Content> 
       <Image Source="..\Graphics\close.png" Name="image1" /> 
      </Button.Content> 
     </Button> 
    </StackPanel> 

    <xctk:ChildWindow Name="loginChildWindow" Panel.ZIndex="3" CloseButtonVisibility="Collapsed" FocusedElement="{Binding ElementName=usernameTextBox}" WindowStartupLocation="Center" WindowState="{Binding IsVisible, Mode=TwoWay, Converter={StaticResource boolConverter}}" IsModal="True" OverlayOpacity="1" Caption="Pioneer Login" Height="164" Width="261"> 
     <xctk:ChildWindow.OverlayBrush> 
      <ImageBrush Stretch="None" Viewport="0,0,46,29" ViewportUnits="Absolute" ImageSource="../Graphics/escheresque.png" TileMode="Tile" /> 
     </xctk:ChildWindow.OverlayBrush> 
     <xctk:BusyIndicator IsBusy="{Binding IsLoginBusy}" BusyContent="Authenticating..."> 
      <Grid> 
       <TextBox GotFocus="usernameTextBox_GotFocus" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="99,20,0,0" Name="usernameTextBox" VerticalAlignment="Top" Width="120"> 
        <TextBox.InputBindings> 
         <KeyBinding Key="Enter" Command="{Binding LoginCommand}" /> 
        </TextBox.InputBindings> 
       </TextBox> 
       <Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="28,18,0,0" Name="label1" VerticalAlignment="Top" /> 
       <Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="29,47,0,0" Name="label2" VerticalAlignment="Top" /> 
       <PasswordBox attach:PasswordBoxAssistant.BindPassword="True" attach:PasswordBoxAssistant.BoundPassword="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="100,50,0,0" Name="passwordBox1" VerticalAlignment="Top" Width="120" /> 
       <Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" /> 
       <Button Style="{DynamicResource RedSubmitButtonStyle}" Command="{Binding LazyLoginCommand}" Visibility="{Binding IsDebugMode, Converter={StaticResource visConv}}" Content="Quick Login" Height="23" HorizontalAlignment="Left" Margin="23,87,0,0" Name="button2" VerticalAlignment="Top" Width="89" /> 
      </Grid> 
     </xctk:BusyIndicator> 
    </xctk:ChildWindow> 

</Grid> 
0

Похоже, у вас есть привязка к нему по-прежнему в вашем графике хранения.

Читать и понимать следующее:

A memory leak may occur when you use data binding in Windows Presentation Foundation

Я думаю, что это может быть ваша проблема, но не показывают фактические привязок.

+0

Привет, Алан, да, действительно, это похоже на привязку. Я создал тестовое решение здесь, где мой модуль только связывает (через INotifyPropertyChanged) с 1 текстовым полем, но режим просмотра и viewmodel в модуле остается в памяти. https://docs.google.com/file/d/0B4XjO9pUQxBXVEtJaW8yYWV1SGs/edit?usp=sharing. Проблема заключается в том, чтобы найти, что привязка получает goofed вверх –

+0

В моем примере приложения есть две привязки. Свойство string для текстовых полей Text и DelegateCommand для команды кнопки. Объект, который содержит эти свойства, реализует INotifyPropertyChanged. Даже когда я устанавливаю привязку TextBox к OneTime, объекты остаются в памяти. В нашем реальном приложении есть множество привязок (MVVM и viewmodelfirst через DataTemplates), поэтому очистка всех этих привязок не является вариантом. –

Смежные вопросы