WPF Memory leak with derived TextBoxes - wpf

In one of the apps of mine I have a performance problem I can’t resolve:
The app is built with input controls derived from the TextBox-class, having their own ControlTemplate in Themes\Generic.xaml.
My problem is, that these controls will not be released after they are no more used. If I look at them with SciTech MemoryProfiler, I see that they are hold by an instance of System.Windows.Documents.TextEditor and the TextEditor-instance is hold through the finalizer queue.
Memory profiler attaches a warning to the TextEditor-instance, saying “Instance indirectly rooted by finalizer queue”.
Has anyone an idea what’s going on here? Is it not allowed to derive directly from TextBox? Or have I forgotten something important to implement?
Additional information for the implementation:
The implementation of some of these derived controls is very simple. In class constructor, the DefaultStyleKeyProperty’s metadata is overridden and no event handlers are attached to elements contained in the control template. Something like:
public class MyDerivedTextBox : TextBox{
static MyDerivedTextBox(){
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyDerivedTextBox), new FrameworkPropertyMetadata(typeof(MyDerivedTextBox)));
}
}
The (simplified) style looks something like:
<Style TargetType="{x:Type myApp_controls:MyDerivedTextBox}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="UndoLimit" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type myApp_controls:MyDerivedTextBox }">
<Border Name="Border" ... >
<ScrollViewer Margin="1" x:Name="PART_ContentHost" />
</Border>
</Setter.Value>
</Setter>
</Style>

Finalizer queue
The answer for the question about the finalizer queue is, that the observed effect is not a constant one: The finalization was simply not finished at the point I have analyzed the memory. The problem here is to understand the tool (and the environment) I have used.
Memory Leak
The leak itselfs however was a real problem and it turned out, that it was the same thing I also observed at other positions (in the same app). Bindings to CLR-properties of classes not implementing INotifyPropertyChanged! http://support.microsoft.com/kb/938416/en-us
It was one of my first WPF projects and in the meantime it grew to a huge application. At the time I started, I was not aware, that WPF has problems with bindings mentioned above, and during development I have tried to do as much as possible with data binding but didn't care about the target objects. Now after the app has grown so big and the number of clients has increased dramatically, those memory problems came to light (and led to very strange effects).
After resolving the most problematic bindings, also the effect with the finalizer queue has been decreased drastically. It seems that before, the memory leaks had led to a deferred execution of the object finalization (this is only an assumption, I have not digged deeper into GC-behaviour).
Derived TextBoxes:
I have created a little sample project with such derived text-box controls, using them in some stresstests within memory profiler. As far as I can say from my observation of the test project, derive from TextBoxes as I did works perfectly well.
Fazit
I can only emphasize the importance of checking the target objects of Bindings during application creation. Otherwise, it will be a lot of work to identify the leaking spots in an application. I hope this explanation helps someone not doing the same mistakes as I did.

Not sure if it will change anything, but instead of the static constructor in your control, have you tried something like:
public MyDerivedTextBox()
{
this.DefaultStyleKey = typeof(MyDerivedTextBox);
}
That's the pattern I'm more familiar with. Perhaps the MetaDataOverride is doing something wonky.
As an aside, one thing I did notice with some Silverlight memory issues, was the presence of unexplained AutomationPeers brought on by Tablet PC Input Services (see http://www.wintellect.com/CS/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx).

Related

WPF Setter Value inlining

I have this code:
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="{x:Static SystemParameters.CaptionHeight}"/>
</Setter.Value>
</Setter>
The application starts correctly, but it's verbose.
I tried to inline my code, so I wrote this:
<Setter Property="WindowChrome.WindowChrome"
Value="{x:Static SystemParameters.CaptionHeight}"/>
But now if I run the application, it doesn't start. Why?
The property is of type WindowChrome, so it expects values of type WindowChrome.
In the first case, it happens well. Also, you give to the WindowChrome instance a value of the correct type for its property CaptionHeight.
In the second case, you're trying to assign to the WindowChrome property a value of a totally different type (the type of CaptionHeight).
Now, if in your application there is only one single instance of WindowChrome, you can declare it as a StaticResource:
<App.Resources>
<WindowChrome x:Key="WindowChromeResource"
CaptionHeight="{x:Static SystemParameters.CaptionHeight}"/>
</WindowChrome>
</App.Resources>
And then call it every time you need:
<Setter Property="WindowChrome.WindowChrome"
Value="{StaticResource WindowChromeResource}"/>
If instead you need a dynamic number of different instances, it's definitely not possible to do this inline.
Many developers had claimed about the WPF verbosity many times before you, but the WPF team has never improved that aspect. See this and this for wider discussion.

Windows Phone 8.1, How to bind style to view?

I wrote a style for button. Unfortunately, I encountered a problem: font size of the button depends on a value, which I can compute only in the view (specifically, that's DisplayInformation stuff).
Ideally, I would do the following:
<Style x:key="MyStyle" BasedOn="{StaticResource SomeStyle}" TargetType="Button">
<Setter Property="FontSize" Value="{Binding ElementName=rootControl, Path=SomeProperty" />
</Style>
Then, I would simply provide necessary properties in view class. But that simply doesn't work (does nothing, no messages, no errors).
How can I solve this problem?
Assigning a binding to a setter value is not supported in the Windows Runtime. (It might be supported in WPF and Silverlight 5, however).
If you google around, there are workarounds, but they're kind of hacky. See this, which uses attached properties and a binding helper class.

Custom control theme disappear on rebuild

I am busy building a custom control. The Theme has a Generic.xaml that consist of a MergedDictionary referencing 2 resource dictionaries (1 - Generic brush and 2 - ControlTemplate) which is situated in a Generic folder.
I have a tester project (standard Window) added to the solution to test the control.
Issue:
When I add the control for the first time - the look of the control looks correct. However when I do some changes on the control and rebuild it - the control disappear from the window and I have to Unload the Window project and Reload it again to make the control's look reappear again.
When I run debugger the control does appear correctly - it is just in design mode that it becomes difficult to work with.
Is there a solution / workaround for this occurrence that does not involve unloading and reloading the window on each rebuild?
EDIT
I have run a test where I copied all the info from separate resource dictionaries into the Generic.Xaml and commented out the Merge Dictionary. It seems the issue does not lie with MergeDictionary operations as the problem is still there - but perhaps with ComponentResourceKey / or static properties. One of my ResourceDictionaries for instance contain a lot of the following
<SolidColorBrush x:Key="{x:Static keys:Disabled.ForeGroundKey}" Color="Gray"/>
Where ForeGroundKey is linked to a static class with for example:
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox),"ContainerBackground");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
I guess seeing that the theme work sometimes mean that there is nothing wrong with the above approach and there is something wrong with how VS handles the rebuild of the control. What I do not understand though is why doesn't VS recognize either the old values / new values, instead it ignores all values linked to ComponentResourceKey - Ps. during runtime the control works perfectly.
Well it seems like I have 3 options here.
Use the method I have been using all along:
Define ComponentResourceKey in C# Class
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox), "Normal.Background");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
Assign Key in Resource Dictionary
<SolidColorBrush x:Key="{x:Static keys:Normal.BackGroundKey}" Color="Yellow"/>
Use Key in Resource Dictionary:
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static keys:Normal.BorderBrushKey}}"/>
The advantage is that when you have multiple themes to assign and use the resource key seems simplistic and chances of errors occurring in typing over the name is reduced as you have the help of intelisence.
The disadvantage is that when you are working on a large project, and you make a small adjustment to your control - none of the ComponentResourceKeys are loaded and your project looks completely lookless at design time. To fix this issue, one either has to reboot VS or unload the project that uses your control and reload it again. Ps this is only at design time. Running the project will give the correct result. This is a silly problem to have in VS!!
return to the more verbose method of defining Component resource keys in XAML i.e.
Define and Assign the resourceKey.
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}" Color="Yellow"/>
Use the resourceKey:
<Setter Property="BorderBrush" Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}}"/>
The Advantage is VS is now working everytime in design time. Also you do not have to create a separate C# class to hold all your resourcekeys
The Disadvantage is that you have to remember the ResourceId names for each resourceId and type it out as it was defined. Also using this in a control with multiple themes becomes frustrating.
Use a mixture of the 2 above i.e.
You still Define the ResourceKey in C# library
You Assing the ResourceKey as per method 2. But the ResourceId is the "text" field assigned in the C# class and not the x:static method i.e.
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox), "Normal.Background");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
//To assign
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}" Color="Yellow"/>
//Thus Normal.Background and not Keys:Normal.Background!! where keys = referenced to the C# Class
Then to use
<Setter Property="Background" Value="{DynamicResource {x:Static keys:Normal.BackGroundKey}}"/>
//i.e we now can reference the C# class and have intelisence
The advantage is that 1 you have a static class that hold all the ResourceKeys in C# (goes slightly against lookless philosophy). you also have access to proper intelisence at least in the using side. But best of all VS works perfectly in design time.
This however does not shorten the assign side at all and the disadvantages therefore are still that you need to type out verbose text to assign a color to the resourcekey. Having multiple themes each with its own set of colors means that you have only shorten the Style of the control a bit and seems silly to use this method
Thus if you want the best solution and do not care about the design time look I'd prefer option1.
If you prefer design time visuals then I'd go for method 2 unless you have to define the style at a number of places too, then option 3 will suffice.
Alternatively create a designtime ResouceDictionary and a compile time ResourceDictionary. Where at designtime method 3 is used and compile time method 2 -> Not sure how to do this automatically. I am doing this by use of Merged Dictionaries, and uncommenting the proper dictionary when Control is ready to be compiled, and deleting the designtime ResourceDictionary.
Hope this helps someone, some day as I had to spend the whole day trouble shooting this (I thought there was something wrong with my control - turns out there is something wrong with VS).

Combobox background not being applied in windows 8

I am a little confused with wpf themes. I would like to have the wpf screens look the same on Vista, Windows 7 and Windows 8. So I have styled the components accordingly and they don't pose problems except when run on Windows 8. For example I have a combobox and I am changing its default background in xaml like this.
<Style TargetType="{x:Type ComboBox}" >
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="{StaticResource GradientButtonBackgroundBrush}"/>
</Style>
The combobox Background property has no effect in windows 8 and all I get is a flat rectangle with a arrow on right (the default windows 8 combobox, which is rather poorly designed!).
So, my question is that how do I get the combobox look the same on all version of windows. I tried adding windows Aero theme in my App.xaml like below, but it has no effect on the combobox display. Here is how I added Aero theme
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PresentationFramework.Aero;component/themes/aero.normalcolor.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
There is also another doubt regarding themes. I am building the wpf application on a windows 7 machine, which by default (I believe) has Aero Theme set. So, all my styles are based on the Aero theme when viewed on Windows 7 machine. What happens if I run the application on say XP. Then do I need to add an entry for the resource dictionary (Aero theme) in App.xaml as listed in code above?
I know my question is a bit vague, but believe me, I am really confused with default themes of wpf on different Windows versions.
EDIT:
I still can't get combobox to style according to my needs. The combobox still appears like a gray rectangle.
Here is what I did. I downloaded the Aero.NormalColor.xaml from microsoft's site and included in themes folder of application. Then I added the following in App.xaml
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/Aero.NormalColor.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Then I Compiled the application and deployed on Windows 8. Still the same combobox as was shown previously. Note that all other elements get styled properly according to the theme. I did the same with Luna.Metallic.xaml and every element gets styled except the ComboBox.
I believe that when I load a particular theme, which defines styles with ControlTemplate, then it should be picked by wpf. I am confused as to why only the ComboBox even after giving it a Aero (or Luna) Control Template doesn't change its appearance. Any ideas ?
EDIT-2
Screen shot of combobox appearance on Windows 8
Well the ComboBox clickable area is actually a ToggleButton
and if you look at the Style for that ToggleButton in Windows-8, you see something like:
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot"
Background="{StaticResource ComboBox.Static.Background}"
BorderBrush="{StaticResource ComboBox.Static.Border}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
...
As you can see from above, Background used is not a {TemplateBinding Background} but {StaticResource ComboBox.Static.Background}. Hence why you see no effect when you set the Background property in Windows-8 for that ComboBox
If your looking to carry a Style across the different OS Versions(Without having to backtrack and keep checking if new versions screwed up your over-rides), simple rule is Create it yourself.
Create a Style and set it to be applied by TargetType and without a Key to get applied automatically. That way in any OS it's your Style that gets used and not the default underlying one.
This thus guarantees your code run's as you expect on every single OS. Base your Style on the default's of any OS and tweak it to your heart's content.
side-note:
From a usability POV giving the user a Windows-7 ComboBox in an app that run's on Windows-8 is not very nice(unless your entire app looks like a Windows-7 app which is even worse). Your expecting the user to get used to your app's Style's and forget what he's used to from every other app he uses in his OS that use default Styles based on OS. If you have specific reasons for doing so, go ahead but do consider the implications.
Just for example you stated the Windows-8 Style is something your not a fan of, well I'm the opposite. I actually do like the Windows-8 clean and simple look. No distractions to the UserExperience with flashing gradients and things that throw you off the content your putting in front of them. This is an argument that goes on forever. Just be warned abt what the end-user expects and thinks than just what you think is good while writing your program.
Update:
Firstly comment on the relevant answer please. Your answer and your comment update has no relation.
Ok and as for your question edit, what you tried has not worked in windows-8 because PresentationFramework.Aero.dll does not exist in Windows-8 which is what holds Aero.NormalColor.xaml. In Windows-8 your options are PresentationFramework.Aero2.dll which is default and PresentationFramework.AeroLite.dll which I think is used by Windows Server 2012(Not Sure)
Try to compile your program on Windows-8 and you'll see it does not even want to compile.
You'll have to explicitly add a reference to PresentationFramework.Aero and also PresentationUI(which i think is part of .net3) to your project.
Then you'll have to edit your Aero.NormalColor.xaml to something like:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:ui="clr-namespace:System.Windows.Documents;assembly=PresentationUI">
...
^^ we explicitly state the assembly for Aero Theme. I don't use Windows-7 so Am not sure if that's all that's needed. but you can give that a try.
Try to compile your code in Windows-8 to make sure it will work fine on Windows-8
Finally, after hours of frustration, I came accross an explanation post here. Scroll for the LONG ANSWER. It explains exactly the scenario I have been facing particularly Aero style not getting applied to my Combobox. The link explains very well why we need to add a BasedOn attribute to every element that we style if we dont want the default OS style being picked up. So adding this BasedOn for the Combobox got it working for me.
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
Now the Aero theme style is used for the combobox. As #Viv pointed out it may also require copying of PresentationFramework.Aero.dll and PresentationUI.dll to the windows 8 machine as they are not supplied with the OS.
Thanks,
Nirvan
I did a bit of a hack on the built-in template. Not the cleanest solution, but removes the headache of having to roll my own template. The code behind basically binds the built-in template border's properties with the combo box's properties.
<Style TargetType="ComboBox">
<Setter Property="Border.Background" Value="White"/>
<EventSetter Event="Loaded" Handler="ComboBox_Loaded"/>
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="{StaticResource ResourceKey=FocusedControlBackcolorBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
var comboBox = sender as ComboBox;
var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
if (border != null)
{
Binding b = new Binding("Background");
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
BindingOperations.SetBinding(border, Control.BackgroundProperty, b);
b = new Binding("BorderBrush");
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
BindingOperations.SetBinding(border, Control.BorderBrushProperty, b);
}
}
In Response to #Viv
#Viv, I feel that your answer was very clear, but somehow your views/suggestions aren't digestable to me and I will briefly mention why
Your Suggestion
If your looking to carry a Style across the different OS Versions(Without having to backtrack and keep checking if new versions screwed up your over-rides), simple rule is Create it yourself.
It seems impracticable. The very idea of themes is creation of consistent look for all elements in the application and accross all platforms. So, if I use a particular theme provided by the framework, no matter what, I should be able to achieve a satisfactory level of consistency, atleast on major current platforms. Above all, not every one has the expertise to create all the styles and templates from scratch. The concept of themes should be,
"The framework provider is providing the themes that will work well for normal scenarios and if anyone wishing to roll out his themes he is welcome to do so".
Instead here the concept seems like
"The framework provider is providing theme and it is not guranted that it will be consistent and work without breaking. So always roll out your themes"
Your quote
As you can see from above, Background used is not a {TemplateBinding Background} but {StaticResource ComboBox.Static.Background}. Hence why you see no effect when you set the Background property in Windows-8 for that ComboBox
I don't know whose idea it was to make a template that way! Must be out of his minds or I am too dumb to understand the advantages. Just imagine that I am using some Blue colored theme in my application and tommorrow, on Windows 9, somebody defines a Red as {StaticResource ComboBox.Static.Background}. So, guess what, I have a fancy window screen now with all my elements themed "Blue" and only the combobox's appear "Red". I mean the very idea of theme is broken here!
Your side note
Just for example you stated the Windows-8 Style is something your not a fan of, well I'm the opposite. I actually do like the Windows-8 clean and simple look.
My combobox looks like a disabled button and I couldn't even change its background! The buttons don't look like they are clickable! Any way as you said this is a personal choice, ofcourse.
My Conclusion
I will wait for some time if some one has any suggestions on how I get the combobox working on windows 8. Your answer brings forward the truth, so I would be happy to mark it a correct if I don't get any alternative solution.
Nirvan
The newer template has a Grid->ToggleButton->Border with a hardcoded background colour which does not respect any background styles. Based on the answer from #Matstar I made a way to sync the background colour across.
You can still set the binding to background colour and the code will pick this up and reapply this when needed:
<ComboBox ItemsSource="{Binding Values}" x:Name="Cbo2"
Background="{Binding Path=SelectedValueBgColor}" ... />
Then in xaml.cs
cbo.Loaded += (sender, args) =>
{
var comboBox = sender as ComboBox;
if (comboBox != null)
{
var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
if (border != null)
{
var existing = BindingOperations.GetBinding(comboBox, BackgroundProperty);
BindingOperations.SetBinding(border, BackgroundProperty, existing);
}
}
};
Make sure your background binding never returns null
If you have no meaningful value return transparent or white or some other default. If binding to a background that returns null the combobox will stop working.
Make sure you notify property changed on your background binding if it needs refreshing when an event happens.

Information when DynamicResource could not be found during runtime?

In my current project I have a quite large WPF based application with lots of Static and DynamicResources.
Because of many refactorings and changes in the past there are lots of DynamicResources that can not be found during runtime and therefore no value is applied.
What I like to do is run the application and get an output, exception or whatever when a DynamicResource could not be found.
I have tried to build a DefaultTraceListener and a Converter that checks for unused DynamicResources, but to no avail.
Does anyone have a solution for me on how to achieve that?
Example:
<Grid.Resources>
<Style x:Key="myStyle1" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Blue"></Setter>
</Style>
</Grid.Resources>
<StackPanel>
<TextBlock Style="{DynamicResource myStyle1}">DynamicResource exists</TextBlock>
<TextBlock Style="{DynamicResource myStyle3}">DynamicResource does not exist</TextBlock>
</StackPanel>
How can I be informed during runtime that myStyle3 does not exist?
Thanks in advance !
There are many tools that show you witch binding doesn't work..
WPF Inspector is my favorite tool, take a look. WPF Inspector
Snoop utility is one of those. In short - in the top right corner you'll find DropDown list which allows filter visuals, just select Visuals with binding Error. Source: How to locate the source of a binding error?
In Visual Studio, you can enable all exceptions (with binding errors) in the Debug menu, Exceptions, then check everything.
But I don't know if it's exactly what you want, let other people answer this...

Resources