Using updated .NET 4.0, I found a strange memory leak that can be reproduced by following sample code.
<DropShadowEffect>
whose Color
dependency property is bound to a property in App object.DropShadowEffect
resource. Does not happen on a SolidColorBrush
whose Color
is also bound to the same source.
Really appreciate if someone can tell me why this leak happens on the DropShadowEffect
but not on the SolidColorBrush
?
App.xml
<Application x:Class="WpfSimple.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<!--this one make GC unable to collect LeakWindow-->
<DropShadowEffect x:Key="AppDropShadowColor"
Color="{Binding Source={x:Static Application.Current}, Path=DropShadowColor, Mode=OneWay}" />
<!--this one does not leak-->
<SolidColorBrush x:Key="AppBackground"
Color="{Binding Source={x:Static Application.Current}, Path=DropShadowColor, Mode=OneWay}" />
</Application.Resources>
</Application>
App.xml.cs start MainWindow and implements INotifyPropertyChanged
for the property DropShadowColor
.
public partial class App : Application, INotifyPropertyChanged
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// start main window
var mainWindow = new MainWindow();
mainWindow.Show();
}
private Color _dropShadowColor = Colors.Blue;
public Color DropShadowColor
{
get { return _dropShadowColor; }
set {
_dropShadowColor = value;
OnPropertyChanged("DropShadowColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); }
}
}
MainWindow.xml and MainWindow.xml.cs has a button to create a LeakWindow
, shown below.
var win = new LeakWindow {Owner = this};
win.Show();
There is also another button to do GC.Collect()
;
LeakWindow.xml
<Window x:Class="WpfSimple.LeakWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Leak" Height="300" Width="300">
<Grid>
<!--leak-->
<Border Width="200" Height="200" BorderThickness="1" BorderBrush="Black" Effect="{StaticResource AppDropShadowColor}"/>
<!--no leak if comment out above and uncomment below-->
<!--<Border Width="200" Height="200" BorderThickness="1" BorderBrush="Black" Background="{StaticResource AppBackground}"/>-->
</Grid>
</Window>
LeakWindow.xml.cs
public partial class LeakWindow : Window
{
public LeakWindow()
{
InitializeComponent();
}
~LeakWindow()
{
Debug.WriteLine("LeakWindow finalized");
}
}
Update
OneTime
does not help either.DynamicResource
does not help.Further investigation shows the leak is caused by a EventHandler
reference from DropShadowEffect
to Border.Effect
. Probably a change notification due to the binding in DropShadowEffect
.
Still, what weird is Why this only occurs on Border.Effect
but not on Border.Background
?
Workground
Adding x:Shared=false
to <DropShadowEffect>
in app.xml can workaround this. I can now have application--wide defined resources but lose the memory efficiency.
I think the problem is caused by the way DropShadowEffect
is attached to the visual tree. Moving the DropShadowEffect
into your control templates instead of having it as a resource might solve the leak, but then you would lose having that shared resource...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With