Mam aplikację z widokiem szczegółów szczegółowych. Po wybraniu elementu z listy "master" wypełni obszar "szczegóły" obrazami (utworzonymi za pomocą RenderTargetBitmap).RenderTargetBitmap Przeciek uchwytu GDI w widoku szczegółów szczegółowych
Za każdym razem, gdy wybieram inny element główny z listy, liczba uchwytów GDI używanych przez moją aplikację (zgodnie z raportem w Eksploratorze procesów) wzrasta - i ostatecznie spada (lub czasami blokuje się) przy 10 000 uchwytach GDI w użyciu.
Nie mam pojęcia, jak to naprawić, więc wszelkie sugestie dotyczące tego, co robię źle (lub tylko sugestie, jak uzyskać więcej informacji) byłyby bardzo mile widziane.
Mam uproszczone mojej aplikacji w dół do następujących w nowej aplikacji WPF (.NET 4.0) o nazwie "DoesThisLeak":
W MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
ViewModel = new MasterViewModel();
InitializeComponent();
}
public MasterViewModel ViewModel { get; set; }
}
public class MasterViewModel : INotifyPropertyChanged
{
private MasterItem selectedMasterItem;
public IEnumerable<MasterItem> MasterItems
{
get
{
for (int i = 0; i < 100; i++)
{
yield return new MasterItem(i);
}
}
}
public MasterItem SelectedMasterItem
{
get { return selectedMasterItem; }
set
{
if (selectedMasterItem != value)
{
selectedMasterItem = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedMasterItem"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MasterItem
{
private readonly int seed;
public MasterItem(int seed)
{
this.seed = seed;
}
public IEnumerable<ImageSource> Images
{
get
{
GC.Collect(); // Make sure it's not the lack of collections causing the problem
var random = new Random(seed);
for (int i = 0; i < 150; i++)
{
yield return MakeImage(random);
}
}
}
private ImageSource MakeImage(Random random)
{
const int size = 180;
var drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(Brushes.Red, null, new Rect(random.NextDouble() * size, random.NextDouble() * size, random.NextDouble() * size, random.NextDouble() * size));
}
var bitmap = new RenderTargetBitmap(size, size, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(drawingVisual);
bitmap.Freeze();
return bitmap;
}
}
W MainWindow.xaml
<Window x:Class="DoesThisLeak.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="900" Width="1100"
x:Name="self">
<Grid DataContext="{Binding ElementName=self, Path=ViewModel}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="210"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding MasterItems}" SelectedItem="{Binding SelectedMasterItem}"/>
<ItemsControl Grid.Column="1" ItemsSource="{Binding Path=SelectedMasterItem.Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Możesz odtworzyć problem, klikając pierwszy element na liście, a następnie przytrzymując klawisz kursora w dół.
Spoglądając na! Gcroot w WinDbg z SOS, nie mogę znaleźć niczego, co utrzyma przy życiu te obiekty RenderTargetBitmap, ale jeśli wykonam !dumpheap -type System.Windows.Media.Imaging.RenderTargetBitmap
, to nadal pokazuje kilka tysięcy z nich, które jeszcze nie zostały zebrane.
Uwaga, próbowałem również buforować ObservableCollection. Niestety, przechowywanie kolekcji wydaje się ostatecznie utrzymywać obsługę GDI. –
Dzięki, to świetnie. Naprawia to dla przykładowej aplikacji, po prostu muszę spróbować dopasować ją do prawdziwej aplikacji. Nie jestem pewien, dlaczego ObservableCollection pomaga tutaj. Jeśli był to tylko rozmiar, lista powinna mieć taki sam efekt. –
Wilka