MergedDictionaries and Resource lookup
Asked Answered
A

4

25

i have a problem with Resource dictionaries and mergeddictionaries in general, especially when it comes to resource-lookup performance. After some performance testing i found that ResourceDictionary.get_MergedDictionaries is the call with the most hits (checked in ANTS profiler). We have around ~300 resource dictionary xamls, and a lot of them are using merged dictionary to "include" other styles. Well the get_MergedDictionaries count on one part of our application, where not much is happening, was around 10 million hits. So my guess is we are doing something completely wrong with Resource dictionaries in general. So i tried to refactor everything and i want to try to get rid of all the merged dictionaries.

Now to the actual question. I tried to get rid of the mergeddictionaries but i failed. My understanding is that when you use StaticResource the lookup needs the resource to be defined before the current one. I made the following short example:

One main project and one custom control library.

the custom control library contains 2 xamls.

<!-- Colors.xaml -->
<ResourceDictionary [stripped namespaces] >
    <SolidColorBrush x:Key="myColor" Color="Green"/>
</ResourceDictionary>

<!-- Templates.xaml -->
<ResourceDictionary [stripped namespaces]>
    <ControlTemplate x:Key="myTemplate" TargetType="Button">
        <Rectangle Fill="{StaticResource myColor}"/>
    </ControlTemplate>
</ResourceDictionary>

Now in the main project, the MainWindow.xaml looks like this

<Window x:Class="ResourceTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
                <ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Button Template="{StaticResource myTemplate}"/>
    </Grid>
</Window>

That is the desired goal. but unfortunately this crashes because the resource "myColor" cannot be found. I of course know how to fix it, add a mergeddictionary in Templates.xaml and reference Colors.xaml but i always thought, well i never really checked, that resources are looked up depending on the logical tree and the resources of the element. My understanding is; Button is created; try to lookup template .. found; try to lookup color, not found on own resources, walk up and use the windows resources.

It seems that i'm wrong. So i hope someone can shed some light on this for me. We make heavy use of WPF and despite of this we accomplished a lot with it, but because of some wrong learned behaviour in the beginning, our performance is pretty bad just because of the resource lookup. Any help would be greatly appreciated

Thanks in advance Best regards Nico

Algophobia answered 14/7, 2011 at 12:44 Comment(5)
What's wrong with using DynamicResource instead of StaticResource for MyColor? Last I checked, the performance difference was not enough to justify not using Dynamic Resources.Pelag
Well my main concern is performance and every performance tip i read mentions not to use DynamicResource even the MSDN tells that. My sample source code also only contains the minimum example of our problem. So we talk about ~3000-4000 StaticResource calls.Algophobia
Why do you have so many? Usually resources are only loaded into an application once, so you should only have one reference to Colors.xaml or Templates.xaml in your entire application. Usually I load generic resources (colors, templates, styles, etc) into Application.Resources, than specific resources like DataTemplates are loaded into the UserControl that uses them.Pelag
As a side note, I found that Blend is written almost exclusively with DynamicResources, and the code it generates always uses DynamicResource. I don't think they'd do that if there was a significant performance difference between Static and Dynamic resources.Pelag
@Pelag Like i wrote in the first post. I learned it or understood it in the beginning wrong. We have a lot of Custom controls, each custom control has its own xaml with its style. If it is an items control, we also have a xaml for the container. We also split the xamls further, so the designer has a xaml where he can modify only the designer related things, while the main xaml of the control only references the designer stuff. We made that so the designer won't have to deal with bindings and other code relevant things. As it seems this itended clean structure made it even worse.Algophobia
A
55

Well, I don't like answering my own question, but I guess a lot of people might stumble into this and I want to give them our current solution as an option to consider.

Like I said before, we have a lot of XAMLs, around ~300 for all different kinds of things like Shared Resources (Brushes, Colors) but also many XAMLs containing different DataTemplates, Styles for controls and also for Custom Controls. In the beginning, this approach of having a lot of XAMLs was reasonable for us because we do the same with our classes and keep them small and organized. Unfortunately, WPF doesn't like that. The more ResourceDictionaries you have and the more you merge them via MergedDictionaries the worse your performance will get. The best advice I can give you is, use as few ResourceDictionary XAMLs as possible.

We bit the bullet and merged a lot of them into one giant XAML, in fact, we do this now with a pre-compiler keeping the best of both worlds. We can use as many XAMLs as we want, just following a few constraints, and merging them on a compile in a giant XAML. The performance increase we get was remarkable. In my question I wrote "11 million hits on getMergedDictionaries" ... just "precompiling" one of our assemblies, we went down to 2million hits and the performance is in the whole application at all times a lot better.

So in the end. XAML resources should not be considered as source code that gets compiled, instead, it should be understood as an actual resource that, when declared, exists, takes up space and performance.

Well, we had to learn that the hard way. I hope everyone reading this can improve their projects by learning from our mistakes.

Algophobia answered 29/7, 2011 at 10:27 Comment(5)
I don't suppose you'd be willing to share your precompile details, or at least point me in the right direction, would you?Mudcat
Unfortunately not, because its not my code, its the companies i work for. But it wasn't difficult to do. Just parse your xamls for merged dictionaries, bring each file in relation via that information, to know in what order you need to merge them. And then create a new xaml and write each xaml in that order into it. I had to rename all original xamls, so in the end only one real *.xaml exists. I called all original xamls, *.tamls. Of course you have to deal with resources with the same name and the namespaces and aliases. But this can also be easily resolved.Algophobia
Was it a console app you ran as a pre-build step, or was it something integral to VS?Mudcat
No i just build a very simple console app, and set it as a pre build step, like you said. It has multiple modes, because i had to prepare the project the first time, by renaming all xamls, so they won't get compiled by vs, i just converted them to simple resources. After that each compile, took all the so called tamls, parsed them, resolved the namespaces and wrote them in one giant xaml, which then was used by VS when compiling.Algophobia
I had similar problems and created such a pre-build event program. You can find it on GitHub. It includes an example App, and you can see all the XAML files being merged to a single long XAML file. I wrote a full explanation in my blog post . Enjoy :)Becky
M
3

I tend to go the route of using only one ResourceDictionary in an application to avoid any performance issues.

To keep the XAML manageable, I use the XAML regions Visual Studio plugin and wrap each category of resources in a region.

  • Brushes
  • Text Styles
  • etc...

For this scenario, the plugin is an absolute life saver. http://visualstudiogallery.msdn.microsoft.com/3c534623-bb05-417f-afc0-c9e26bf0e177

Mitis answered 5/1, 2012 at 17:8 Comment(1)
I'm already using that. Thanks, though. Organization is not the problem. VS is extremely slow (in regard to accepting key presses, not just showing intellisense windows). I've tried disabling Resharper, and that seems to improve the performance somewhat, but I don't know if I can disable it for just that file (or XAML files), and I prefer not to lose those powerful tools just to edit a text file.Mudcat
H
2

Using SharedResourceDictionary instead of ResourceDictionary completely solved MergedDictionaries performance issue for me: http://www.wpftutorial.net/MergedDictionaryPerformance.html

Hurwit answered 5/8, 2011 at 11:39 Comment(1)
We already use them, and while we didn't gain that much speed, we saved alot of memory. But Sharedresource Dictionaries have a problem with memory leaks, which i also posted hereAlgophobia
S
0

Was any light shed on the resource lookup procedure? Why was "myColor" not found?

By the way, I found a way to make it work - but a strange and unstable way. If Application.xaml has this code, the color should be found:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/> 
        </ResourceDictionary.MergedDictionaries> 
    </ResourceDictionary>
</ResourceDictionary.MergedDictionaries>

If, on the other hand, you include this code into another XAML, which you then include into Application.xaml - it doesn't work, even though the resource structures are identical (verified with Snoop).

Swafford answered 12/7, 2012 at 12:37 Comment(1)
From what i know now is. The resource will look for MyColor, in its current xaml, its current MergedDictionaries, in the Generic.xaml(under some circumstances) and in the app.xaml and its MergedDictionaries. That way it works fine. What we did now was, remove all merged dictionaries, merged alot of xmall xamls into one xaml, and merged them all in the app.xaml. So the app.xaml is more or less the only file allowed to have merged dictionaries. You have to take special attention to the order of "includes" in the app.xaml, but keeping that in mind, its working smoothly.Algophobia

© 2022 - 2024 — McMap. All rights reserved.