WPF 資源

WPF中,每一個元素都有Resources屬性,同時本身能夠建立ResourcesDictionary,經過 <ResourceDictionary.MergedDictionaries>添加單獨定義的resources供多個xaml之間公用 html


Improve the performance of merged ResourceDictionaries

Each time a control references a ResourceDictionary XAML creates a new instance of it. So if you have a custom control library with 30 controls in it and each control references a common dictionary you create 30 identical resource dictionaries! java

<ResourceDictionary.MergedDictionaries>
   <SharedResourceDictionary Source="/MyControlLibrary;component/Themes/Brushes.xaml"  />
</ResourceDictionary.MergedDictionaries>

To get rid of this problem, I created the SharedResourceDictionary. You can use it the same way as a conventional ResourceDictionary. The only suptile difference is, that if it is instanced multiple times, the resources are loaded only once. web

[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", 
"WPFTutorial.Utils")]
 
/// <summary>
/// The shared resource dictionary is a specialized resource dictionary
/// that loads it content only once. If a second instance with the same source
/// is created, it only merges the resources from the cache.
/// </summary>
public class SharedResourceDictionary : ResourceDictionary
{
    /// <summary>
    /// Internal cache of loaded dictionaries 
    /// </summary>
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();
 
    /// <summary>
    /// Local member of the source uri
    /// </summary>
    private Uri _sourceUri;
 
    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;
 
            if (!_sharedDictionaries.ContainsKey(value))
            {
                // If the dictionary is not yet loaded, load it by setting
                // the source of the base class
                base.Source = value;
 
                // add it to the cache
                _sharedDictionaries.Add(value, this);
            }
            else
            {
                // If the dictionary is already loaded, get it from the cache
                MergedDictionaries.Add(_sharedDictionaries[value]);
            }
        }
    }
}

另一篇文章介紹了應用這個類取得的改進。 c#

http://leecampbell.blogspot.hk/2010/05/mergeddictionaries-performance-problems.html app

MergedDictionaries performance problems in WPF

I don’t normally like to blatantly plagiarise other people’s comments, but this seems to be a little know bug that sounds like it should be shared. ide

A colleague of mine emailed our internal tech list the following email post

I strongly urge everyone working with WPF to use this or at least benchmark it in your own applications if you use ResourceDictionaries.MergedDictionaries. I consider this to be a huge problem in WPF. I’m not sure if it exists in Silverlight, but I would assume it does. this

I was just debugging a very long render delay in some WPF code and I came across this little tidbit: google

http://www.wpftutorial.net/MergedDictionaryPerformance.html spa

The quote of interest is: 「Each time a control references a ResourceDictionary XAML creates a new instance of it. So if you have a custom control library with 30 controls in it and each control references a common dictionary you create 30 identical resource dictionaries!」

Normally that isn’t a huge problem, but when you consider the way that I personally (and have suggested to others) that they organize their resources in Prism projects it gets to be a **serious**problem. For example, let’s say we have this project structure:

/MyProject.Resources
       /Resources
                -Buttons.xaml
                -DataGrid.xaml
                -Global.xaml
                -Brushes.xaml
                -WindowChrome.xaml
                -Icons.xaml
 
/MyProject.Module1
      /Resources
                -Module1Resources.xaml  (References all Dictionaries in /MyProject.Resources/Resources/*)
      /Views
                -View1.xaml
                -View2.xaml
      
/MyProject.Module2
      /Resources
                -Module2Resources.xaml   (References all Dictionaries in /MyProject.Resources/Resources/*)
      /Views
                -View1.xaml
                -View2.xaml
      
/MyProject.Shell
      /Resources
                -ShellResources.xaml   
      /Views
                -MainShell.xaml

If in your views you reference the module-level ResourceDictionary (which helps for maintainability and modularity) then every time you create an instance of View1.xaml for example, you would have to parse all the ResourceDictionaries in /MyProject.Resources/Resources/* every time. This isn’t really a memory concern but it is a huge performance concern. There can potentially be thousands of lines of XAML code to parse and the time really does add up.

I recently switched all of the MergedDictionary references:

<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source=」/SomeDictionary.xaml/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>

To use the attached SharedResourceDictionary which shadows the Source property and keeps a global cache of all ResourceDictionaries parsed:

<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <SharedResourceDictionary Source=」/SomeDictionary.xaml/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>

And I saw a performance increase of almost two orders of magnitude … From almost 6000ms to 200ms. I’ve attached this code; I used the basic sample implementation in the link above so this is considered public information for client purposes.

Cheers,

Charlie

Thanks to Charlie Robbins (Lab49) for expanding on Christian’s blog post and for letting me re-print your email.

相關文章
相關標籤/搜索