1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal
8 using System.Reflection;
10 using System.Windows.Controls;
11 using System.Windows.Markup;
12 using System.Windows.Media.Imaging;
13 using System.Diagnostics;
15 using System.Activities.Presentation;
18 // Helper class that knows how to load up icons that live in assemblies and follow
19 // our extensibility icon naming convention.
21 internal static class ManifestImages
24 private static readonly string[] SupportedExtensions = new string[] {
25 ".png", ".xaml", ".bmp", ".gif", ".jpg", ".jpeg"
29 // ----s open the assembly to which the specified Type belongs and tries
30 // to find and return an image that follows the naming conventions of:
32 // My.Namespace.MyControl.Icon.png
34 // and matches the desired size most closely, if multiple such images are found.
36 // <param name="type">Type to look up</param>
37 // <param name="desiredSize">Desired size (may not be met)</param>
38 // <returns>Null (if no image was found), Image instance (for non-Xaml images),
39 // or object instance (for Xaml-instantiated structures)</returns>
40 // <exception cref="ArgumentNullException">if type is null</exception>
41 public static object GetImage(Type type, Size desiredSize)
45 throw FxTrace.Exception.ArgumentNull("type");
48 Assembly assembly = type.Assembly;
49 string[] resourceNames = assembly.GetManifestResourceNames();
51 if (resourceNames == null || resourceNames.Length == 0)
56 string fullTypeName = type.FullName;
57 string typeName = type.Name;
59 // Do a full namespace match first
60 ImageInfo bestMatch = FindBestMatch(type, assembly, resourceNames, desiredSize, delegate(string extensionlessResourceName)
62 return fullTypeName.Equals(extensionlessResourceName);
65 // Do a partial name match second, if full name didn't give us anything
66 bestMatch = bestMatch ?? FindBestMatch(type, assembly, resourceNames, desiredSize, delegate(string extensionlessResourceName)
68 return extensionlessResourceName != null && extensionlessResourceName.EndsWith(typeName, StringComparison.Ordinal);
71 if (bestMatch != null)
73 return bestMatch.Image;
79 private static ImageInfo FindBestMatch(
82 string[] resourceNames,
84 MatchNameDelegate matchName)
87 Fx.Assert(type != null, "FindBestMatch - type parameter should not be null");
88 Fx.Assert(resourceNames != null && resourceNames.Length > 0, "resourceNames parameter should not be null");
89 Fx.Assert(matchName != null, "matchName parameter should not be null");
91 ImageInfo bestMatch = null;
93 for (int i = 0; i < resourceNames.Length; i++)
96 string extension = Path.GetExtension(resourceNames[i]);
98 if (!IsExtensionSupported(extension))
103 if (!matchName(StripIconExtension(resourceNames[i])))
108 ImageInfo info = ProcessResource(assembly, resourceNames[i]);
114 // Try to match the found resource to the requested size
115 float sizeMatch = info.Match(desiredSize);
117 // Check for exact size match
118 if (sizeMatch < float.Epsilon)
123 // Keep the best image found so far
124 if (bestMatch == null ||
125 bestMatch.LastMatch > sizeMatch)
134 // Tries to load up an image
135 private static ImageInfo ProcessResource(Assembly assembly, string resourceName)
137 Stream stream = assembly.GetManifestResourceStream(resourceName);
143 if (IsXamlContent(resourceName))
145 return new XamlImageInfo(stream);
149 return new BitmapImageInfo(stream);
153 // Checks to see whether the given extension is supported
154 private static bool IsExtensionSupported(string extension)
156 for (int i = 0; i < SupportedExtensions.Length; i++)
158 if (SupportedExtensions[i].Equals(extension, StringComparison.OrdinalIgnoreCase))
167 // Returns true if the passed in resource name ends in ".xaml"
168 private static bool IsXamlContent(string resourceName)
170 return ".xaml".Equals(Path.GetExtension(resourceName), StringComparison.OrdinalIgnoreCase);
173 // Strips ".Icon.ext" from a resource name
174 private static string StripIconExtension(string resourceName)
176 if (resourceName == null)
181 resourceName = Path.GetFileNameWithoutExtension(resourceName);
182 int dotIconIndex = resourceName.LastIndexOf(".Icon", StringComparison.OrdinalIgnoreCase);
183 if (dotIconIndex > 0)
185 return resourceName.Substring(0, dotIconIndex);
191 private delegate bool MatchNameDelegate(string extensionlessResourceName);
193 // Helper class that has information about an image
194 private abstract class ImageInfo
197 private float _lastMatch = 1;
199 protected ImageInfo()
203 public float LastMatch
204 { get { return _lastMatch; } }
205 public abstract object Image
207 protected abstract Size Size
209 protected abstract bool HasFixedSize
212 // gets value range from 0 to 1: 0 == perfect match, 1 == complete opposite
213 public float Match(Size desiredSize)
216 if (!this.HasFixedSize)
222 Size actualSize = this.Size;
224 float desiredAspectRatio = Math.Max(float.Epsilon, GetAspectRatio(desiredSize));
225 float actualAspectRatio = Math.Max(float.Epsilon, GetAspectRatio(actualSize));
227 float desiredArea = Math.Max(float.Epsilon, GetArea(desiredSize));
228 float actualArea = Math.Max(float.Epsilon, GetArea(actualSize));
230 // these values range from 0 to 1, 1 being perfect match, 0 being not so perfect match
231 float ratioDiff = desiredAspectRatio < actualAspectRatio ? desiredAspectRatio / actualAspectRatio : actualAspectRatio / desiredAspectRatio;
232 float areaDiff = desiredArea < actualArea ? desiredArea / actualArea : actualArea / desiredArea;
234 float diff = ratioDiff * areaDiff;
236 _lastMatch = Math.Min(1f, Math.Max(0f, 1f - diff));
242 private static float GetAspectRatio(Size size)
244 if (size.Height < float.Epsilon)
249 return (float)(size.Width / size.Height);
252 private static float GetArea(Size size)
254 return (float)(size.Width * size.Height);
258 // Helper class that knows how to deal with Xaml
259 private class XamlImageInfo : ImageInfo
262 private object _image;
264 public XamlImageInfo(Stream stream)
266 _image = XamlReader.Load(stream);
269 public override object Image
271 get { return _image; }
274 protected override Size Size
276 get { return Size.Empty; }
279 protected override bool HasFixedSize
281 get { return false; }
285 // Helper class that knows how to deal with bitmaps
286 private class BitmapImageInfo : ImageInfo
289 private Image _image;
292 public BitmapImageInfo(Stream stream)
294 BitmapImage bmp = new BitmapImage();
296 bmp.StreamSource = stream;
299 _image = new Image();
302 _size = new Size(bmp.Width, bmp.Height);
305 public override object Image
307 get { return _image; }
310 protected override Size Size
312 get { return _size; }
315 protected override bool HasFixedSize