Tuesday, October 11, 2011

Finding out element style sources in WPF

Hi everyone passing by to look through this blog. Currently I’m working on the project where main technology is WPF. We are developing add-ins for MS Excel. Recently I noticed some style weirdness with the ContextMenu-controls.
We are hosting WPF content in a Custom Task Pane through WinForm’s ElementHost. It basically has two tabs currently. In one of the tabs we’re hosting a legacy control:

Image 1

You are able to see a stylized context menu in the picture above (the white menu in the right lower corner). However if I’m switching to Tab2 and then returning back to Tab1, smth is getting changed in the order of our legacy libraries loading (I don’t know what exactly). Some other styles are getting higher priority. Weird enough that all the styling is preserved… except for the ContextMenu. This one is looking unstylized:

Image 2

In my previous working experience I was using Snoop WPF to detect styling etc. However in my current environment this great tool is not working. We were having a TestHarness to run our content outside of MS Excel environment (in a simple WPF application), but unfortunately as the time was passing by we gave up supporting it unintentionally because of the rare usage.

I found myself in a very unpleasant situation. At first I tried to look through style usages in our huge application. It hasn’t led me to finding this canonic style for ContextMenu. Then full-text search across all solution files followed… Too many places to examine, too much time spent & again no luck. What would one do next?

I can’t but say: in this legacy code they didn’t have any specific style applied to this context menu inline (explicitly), hence it relied on some common style for this control defined somewhere up the visual tree in the resources.

I noticed that I was having too many TextBoxes (actually they are custom controls) neighboring the elements which execute this Context Menu. So I decided to handle these custom TextBoxes’ GotFocus-event before and after Tab-switching. Inside the handler I was able to see what this ContextMenu default style is getting resolved into. To obtain resources in WPF we have a wonderful method FindResource(). The documentation on MSDN is too minimalistic (as always), it relies on all developers reading books and use MSDN just as a reference source. And the example in this article also tells nothing. By these words I really mean they don’t deeply touch the topic of what can be passed in as resourceKey. It is the same as we do in XAML, i.e. it can be any of string (resource name), ComponentResourceKey or System.Type (the default resource for the type), perhaps even more.

var defaultStyle = (Style)this.FindResource(typeof (TextBlock));
var someStyle = (Style)this.FindResource("SomeStyle");
var componentResourceKey = new ComponentResourceKey();
var someComponentStyle = (Style)this.FindResource(componentResourceKey);

Alright, here is what I had in my case:

this.GotFocus += OnControlGotFocus;

private void OnControlGotFocus(object sender, RoutedEventArgs e)
{
    var defaultStyle = (Style)this.FindResource(typeof (ContextMenu));
}

After the first running I discovered that before switching tabs, defaultStyle had been resolved to the proper style (as expected), then after switching tabs and returning to the first Tab, it was resolved to default style from PresentationFramework.Aero assembly (!!!). This confused me even more, because it didn’t look like applying some new style but rather like resetting the applied one.

Now I was obliged to discover where (the hell) it takes this default style from. I obviously had to debug inside of .NET’s FindResource() method. I don’t know much about current situation with version compatibility of debug symbols & sources from Microsoft public servers with the locally-installed .NET framework. But I remember the times of Visual Studio 2008 & .NET Framework 3.5 SP1, it was downloading some debug symbols and sources, but (in my case) they appeared not to be compatible with installed libraries. Hence I decided to use Reflector VSPro (I have a trial version) directly to generate pdb and sources out of PresentationFramework.dll. Debugging appeared to be rather convenient except for the fact that the library was compiled as “optimized”, so you can see almost nothingSmile in the debugger. Fortunately, some important information is still available.




In the picture above: I stepped in here by pressing F11, hence I know what actually is this optimized expression.

So it goes to the FindResourceInternal() method where all the magic happens:



From FindResourceInTree() method we are navigating to the FindResourceOnSelf() method.



Here my breakpoint didn’t get hit (perhaps because of optimizations) :



So I found myself directly inside of dictionary.Contains()



As one might notice, this is the main method I’m gonna debug. It is here were I’ll get to know what dictionary returned me the weird resource.

So when hovering over dictionary instance, you’ll see that almost all of its fields cannot be evaluated because of the optimizations. But the field named _source is luckily accessible:



You see that m_String contains all we need. So I personally added it to my watches list to be always up-to-date:



Okay, finally flag got the value of true in the dictionary.Contains() method:



Please pay attention to the Watches list. I couldn’t believe my eyes!! Someone managed to add PresentationFramework.Aero’s dictionary reference to the collection of merged dictionaries… But it appeared to be true when I opened the corresponding xaml-file:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="pack://application:,,,/PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component/themes/aero.normalcolor.xaml" />  
    <ResourceDictionary Source="pack://application:,,,/WPFToolKit;V3.5.40128.1;;component/themes/aero.normalcolor.xaml" />
</ResourceDictionary.MergedDictionaries>


How did I get to this dictionary?
This resource dictionary (b.t.w.) was taken from the following path:
1) our control was inheriting from one control defined in another library.
2) that other library had some dictionary references in its generic.xaml
3) climbing through those references two libraries up, I found the target resource dictionary.
A bit sophisticated isn't it? This way debugging helped me greatly.

Fortunately, the styles from these dictionaries were not required in my code base, I removed these merged dictionary references & the problem was gone. Still, I don’t know why the styles were resolved the correct/desired way on the first load, but at least I managed to find the source of the weird styles.

Regards,
Sid.

UPDATE 1. I think I have a clue why this restyling happens. Each time the control is activated (I mean after switching tabs), it might be reconstructing all of its child UI-objects, and by that time the assembly with undesired styles may have already been loaded.
UPDATE 2. There is a way to disable code optimizations. Unfortunately I couldn't have found it by the time I was writing this post. Please refer to Cory Plotts’ post and of course read his details on debugging .NET source code with Reflector VS Pro.

No comments:

Post a Comment