问题
I am using Silverlight 4 and trying to share some common styles (colors, brushes). My take was to put them into a "Common.xaml" Resource Dictionary and then use it in all other Resource Dictionaries. Referencing everything like so:
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SampleApp.App"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Styles/Common.xaml"/>
<ResourceDictionary Source="Assets/Styles/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The problem is, that I get an exception on InitializeComponent stating that the common styles cannot be found (Cannot find a Resource with the Name/Key....)
I have to explicitly Reference the "Common.xaml" in every Resource Dictionary where I use it.... And this basically result in multiple Instances of every color, brush, template and whatnot that resides in "Common.xaml".
Isn't there any way to share Resources so the only get instanziated once in Silverlight?
回答1:
The problem is that silverlight appears to streamline loading of resource dictionaries such that multiple dictionaries can be loading in parallel. As a result when one dictionary has a dependency on another that dependency may not be ready in time.
Since ResourceDictionary
doesn't have builtin means to describe inter-dependencies nor an event to indicate when it has loaded the only solution I've been able to come to is to manage the loading of the dictionaries myself.
Here is a function you can add to your App.xaml.cs file to "manually" load a resource dictionary:-
private void LoadResource(Uri uri)
{
var info = Application.GetResourceStream(uri);
string xaml;
using (var reader = new StreamReader(info.Stream))
{
xaml = reader.ReadToEnd();
}
ResourceDictionary result = XamlReader.Load(xaml) as ResourceDictionary;
if (result != null)
{
Resources.MergedDictionaries.Add(result);
}
}
Now in the Application_Startup
before assigning RootVisual
you would use code like:-
LoadResource(new Uri"Assets/Styles/Common.xaml", UriKind.Relative));
LoadResource(new Uri("Assets/Styles/TextBoxStyle.xaml", UriKind.Relative));
It isn't going to be as efficient as using the Source
property but it will work. If you have many such dictionaries and only few "common" dictionaries that contain shared resources then you could use this technique to load only the "common" dictionaries then use:-
Resource.MergedDictionaries.Add(new ResourceDictionary() {Source = new Uri("Assets/Styles/TextBoxStyle.xaml", UriKind.Relative)});
For the other dictionaries that don't have interdependencies on each other.
回答2:
I was able to tweak the solution proposed at http://www.wpftutorial.net/MergedDictionaryPerformance.html to make it work with Silverlight and the VS designer (haven't tried Blend). I have a blog post on it here (http://softnotes.wordpress.com/2011/04/05/shared-resourcedictionary-for-silverlight/)
public class SharedResourceDictionary : ResourceDictionary
{
public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
new Dictionary<Uri, ResourceDictionary>();
private Uri _sourceUri;
public new Uri Source
{
get { return _sourceUri; }
set
{
_sourceUri = value;
if (!_sharedDictionaries.ContainsKey(value))
{
Application.LoadComponent(this, value);
_sharedDictionaries.Add(value, this);
}
else
{
CopyInto(this, _sharedDictionaries[value]);
}
}
}
private static void CopyInto(ResourceDictionary copy, ResourceDictionary original)
{
foreach (var dictionary in original.MergedDictionaries)
{
var mergedCopy = new ResourceDictionary();
CopyInto(mergedCopy, dictionary);
copy.MergedDictionaries.Add(mergedCopy);
}
foreach (DictionaryEntry pair in original)
{
copy.Add(pair.Key, pair.Value);
}
}
}
XAML usage:
<ResourceDictionary.MergedDictionaries>
<ui:SharedResourceDictionary Source="/my_assembly_name;component/Resources/Shared.xaml"/>
</ResourceDictionary.MergedDictionaries>
回答3:
If you get an error loading, ensure the Build Action is set to one of the following:
//In the dll, which is in the xap, marked as Build Action: Resource or Page
LoadResource(new Uri("SilverlightApplication48;component/GlobalAssets.xaml", UriKind.Relative));
//In the xap at the same level as the dll, (not in the dll) marked as Build Action: Content.
LoadResource(new Uri("Dictionary1.xaml", UriKind.Relative));
//In a separate library, marked as Build Action: Resource or Page.
LoadResource(new Uri("StylesLibrary;component/Dictionary2.xaml", UriKind.Relative));
Greg
回答4:
Another interesting note on this thread is that SL only keeps ONE copy of a style if it is found in two different dictionaries. The last one wins. In other words, if you have two different styles both with the same key, the first one is discarded when the second one loads.
来源:https://stackoverflow.com/questions/4152955/silverlight-shared-mergeddictionaries