私はMaster-Detailsビューを持つアプリケーションを持っています。 「マスター」リストから項目を選択すると、「詳細」領域にいくつかの画像(RenderTargetBitmapで作成)が設定されます。RenderTargetBitmap Master-DetailsビューのGDIハンドルリーク
私は、リストから別のマスターアイテムを選択するたびに、(プロセスエクスプローラで報告されたように)自分のアプリケーションで使用されているGDIハンドルの数が増え、最終的に10,000個のGDIハンドルで落ちる使用中で。
私はこれを修正する方法を失っているので、私が間違っていることについての提案(または、より多くの情報を得る方法に関する提案)は非常に高く評価されます。
私がダウンして、 "DoesThisLeak" と呼ばれる新しいWPFアプリケーション(.NET 4.0)に次のように私のアプリを簡略化してきました:
MainWindow.xaml.cs MainWindow.xamlで
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;
}
}
で
<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>
リストの最初の項目をクリックし、次にダウンカーソルキーを押したままにすると、問題を再現できます。
WinDbgの!gcrootをSOSで見ると、これらのRenderTargetBitmapオブジェクトを維持しているものが見つかりませんでしたが、私が!dumpheap -type System.Windows.Media.Imaging.RenderTargetBitmap
を実行してもまだ収集されていない数千ものものが表示されます。
ObservableCollectionもキャッシュしようとしました。残念なことに、コレクションを保持することは最終的にGDIのハンドルも保持しているようです。 –
ありがとう、それは素晴らしいです。それは、サンプルアプリケーションのためにそれを修正します、私はちょうど実際のアプリにそれをフィッティングしようとする必要があります。なぜObservableCollectionがここで役立つのか分かりません。それがちょうどサイズのためだったら、リストは同じ効果を持っているはずです。 –
Wilka