Ejemplo n.º 1
0
        /// <summary>
        /// Finds the <see cref="FrameworkElement"/> a specific style is based on.
        /// </summary>
        /// <param name="resourceDictionaryUri">The resource dictionary URI.</param>
        /// <param name="styleKey">The style key.</param>
        /// <returns>
        /// <see cref="Type"/> or <c>null</c> if the style is not based on a <see cref="FrameworkElement"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">The <paramref name="resourceDictionaryUri"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">The <paramref name="styleKey"/> is <c>null</c>.</exception>
        /// <remarks>
        /// This method is introduced due to the lack of the ability to use DynamicResource for the BasedOn property when
        /// defining styles inside a derived theme.
        /// Should be used in combination with the <see cref="RecreateDefaultStylesBasedOnTheme"/> method.
        /// </remarks>
        private static Type FindFrameworkElementStyleIsBasedOn(Uri resourceDictionaryUri, string styleKey)
        {
            Argument.IsNotNull("resourceDictionaryUri", resourceDictionaryUri);
            Argument.IsNotNull("styleKey", styleKey);

            return(_styleToFrameworkElementTypeCache.GetFromCacheOrFetch(styleKey, () =>
            {
                try
                {
                    var xmlDocInfo = GetResourceXmlDocument(resourceDictionaryUri);
                    var doc = xmlDocInfo.Item1;
                    var xmlNamespaceManager = xmlDocInfo.Item2;

                    var xpath = string.Format("/ctl:ResourceDictionary/ctl:Style[@x:Key='{0}']/@BasedOn", styleKey);
                    var xmlAttribute = doc.SelectSingleNode(xpath, xmlNamespaceManager) as XmlAttribute;
                    if (xmlAttribute == null)
                    {
                        Log.Warning("Style '{0}' does not have the 'BasedOn' attribute defined", styleKey);
                        return null;
                    }

                    var basedOnValue = xmlAttribute.Value;
                    basedOnValue = basedOnValue.Replace("StaticResource", string.Empty);
                    basedOnValue = basedOnValue.Replace("x:Type", string.Empty).Trim(' ', '{', '}');

                    #region Create xml type mapper
                    var xamlTypeMapper = new XamlTypeMapper(new[] { "PresentationFramework" });
                    foreach (XmlAttribute namespaceAttribute in doc.DocumentElement.Attributes)
                    {
                        var xmlNamespace = namespaceAttribute.Name.Replace("xmlns", string.Empty).TrimStart(':');

                        var value = namespaceAttribute.Value;
                        var clrNamespace = value;
                        var assemblyName = string.Empty;

                        if (clrNamespace.StartsWith("clr-namespace:"))
                        {
                            // We have a hit (formatting is normally one of the 2 below):
                            // * clr-namespace:[NAMESPACE]
                            // * clr-namespace:[NAMESPACE];assembly=[ASSEMBLY]
                            if (clrNamespace.Contains(";"))
                            {
                                clrNamespace = clrNamespace.Split(';')[0];
                            }
                            clrNamespace = clrNamespace.Replace("clr-namespace:", string.Empty);

                            if (value.Contains(";"))
                            {
                                assemblyName = value.Split(';')[1].Replace("assembly:", string.Empty);
                            }

                            xamlTypeMapper.AddMappingProcessingInstruction(xmlNamespace, clrNamespace, assemblyName);
                        }
                    }
                    #endregion

                    var splittedType = basedOnValue.Split(':');
                    var typeNamespace = (splittedType.Length == 2) ? splittedType[0] : "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
                    var typeName = (splittedType.Length == 2) ? splittedType[1] : splittedType[0];
                    var type = xamlTypeMapper.GetType(typeNamespace, typeName);
                    if (type == null)
                    {
                        return null;
                    }

                    Log.Debug("Style '{0}' is based on type '{1}'", styleKey, type);

                    if ((type == typeof(FrameworkElement)) || type.IsSubclassOf(typeof(FrameworkElement)))
                    {
                        return type;
                    }

                    return null;
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Failed to find the framework element where style '{0}' is based on", styleKey);
                    return null;
                }
            }));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Finds the <see cref="FrameworkElement"/> a specific style is based on.
        /// </summary>
        /// <param name="resourceDictionaryUri">The resource dictionary URI.</param>
        /// <param name="styleKey">The style key.</param>
        /// <returns>
        /// <see cref="Type"/> or <c>null</c> if the style is not based on a <see cref="FrameworkElement"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">The <paramref name="resourceDictionaryUri"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentNullException">The <paramref name="styleKey"/> is <c>null</c>.</exception>
        /// <remarks>
        /// This method is introduced due to the lack of the ability to use DynamicResource for the BasedOn property when
        /// defining styles inside a derived theme.
        /// Should be used in combination with the <see cref="RecreateDefaultStylesBasedOnTheme"/> method.
        /// </remarks>
        private static Type FindFrameworkElementStyleIsBasedOn(Uri resourceDictionaryUri, string styleKey)
        {
            Argument.IsNotNull("resourceDictionaryUri", resourceDictionaryUri);
            Argument.IsNotNull("styleKey", styleKey);

            if (_styleToFrameworkElementTypeCache.ContainsKey(styleKey))
            {
                return(_styleToFrameworkElementTypeCache[styleKey]);
            }

            try
            {
                XmlDocument doc;

                if (_resourceDictionaryCache.ContainsKey(resourceDictionaryUri))
                {
                    doc = _resourceDictionaryCache[resourceDictionaryUri];
                }
                else
                {
                    StreamResourceInfo streamResourceInfo = Application.GetResourceStream(resourceDictionaryUri);
                    var reader = new XmlBamlReader(streamResourceInfo.Stream);

                    doc = new XmlDocument();
                    doc.Load(reader);

                    _resourceDictionaryCache.Add(resourceDictionaryUri, doc);
                }

                #region Create xml namespace manager
                // Create namespace manager (all namespaces are required)
                var xmlNamespaceManager = new XmlNamespaceManager(doc.NameTable);
                foreach (XmlAttribute namespaceAttribute in doc.DocumentElement.Attributes)
                {
                    // Clean up namespace (remove xmlns prefix)
                    string xmlNamespace = namespaceAttribute.Name.Replace("xmlns", "").TrimStart(new char[] { ':' });
                    xmlNamespaceManager.AddNamespace(xmlNamespace, namespaceAttribute.Value);
                }

                // Add a dummy node
                xmlNamespaceManager.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
                xmlNamespaceManager.AddNamespace("ctl", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
                #endregion

                string xpath        = string.Format("/ctl:ResourceDictionary/ctl:Style[@x:Key='{0}']/@BasedOn", styleKey);
                var    xmlAttribute = doc.SelectSingleNode(xpath, xmlNamespaceManager) as XmlAttribute;
                if (xmlAttribute == null)
                {
                    Log.Warning("Style '{0}' does not have the 'BasedOn' attribute defined", styleKey);

                    _styleToFrameworkElementTypeCache.Add(styleKey, null);

                    return(null);
                }

                string basedOnValue = xmlAttribute.Value;
                basedOnValue = basedOnValue.Replace("StaticResource", "");
                basedOnValue = basedOnValue.Replace("x:Type", "").Trim(new[] { ' ', '{', '}' });

                #region Create xml type mapper
                var xamlTypeMapper = new XamlTypeMapper(new[] { "PresentationFramework" });
                foreach (XmlAttribute namespaceAttribute in doc.DocumentElement.Attributes)
                {
                    string xmlNamespace = namespaceAttribute.Name.Replace("xmlns", "").TrimStart(new char[] { ':' });

                    string value        = namespaceAttribute.Value;
                    string clrNamespace = value;
                    string assemblyName = string.Empty;

                    if (clrNamespace.StartsWith("clr-namespace:"))
                    {
                        // We have a hit (formatting is normally one of the 2 below):
                        // * clr-namespace:[NAMESPACE]
                        // * clr-namespace:[NAMESPACE];assembly=[ASSEMBLY]
                        if (clrNamespace.Contains(";"))
                        {
                            clrNamespace = clrNamespace.Split(new char[] { ';' })[0];
                        }
                        clrNamespace = clrNamespace.Replace("clr-namespace:", "");

                        if (value.Contains(";"))
                        {
                            assemblyName = value.Split(new char[] { ';' })[1].Replace("assembly:", "");
                        }

                        xamlTypeMapper.AddMappingProcessingInstruction(xmlNamespace, clrNamespace, assemblyName);
                    }
                }
                #endregion

                string[] splittedType  = basedOnValue.Split(new[] { ':' });
                string   typeNamespace = (splittedType.Length == 2) ? splittedType[0] : "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
                string   typeName      = (splittedType.Length == 2) ? splittedType[1] : splittedType[0];
                var      type          = xamlTypeMapper.GetType(typeNamespace, typeName);
                if (type == null)
                {
                    _styleToFrameworkElementTypeCache.Add(styleKey, null);
                    return(null);
                }

                Log.Debug("Style '{0}' is based on type '{1}'", styleKey, type);

                if ((type == typeof(FrameworkElement)) || type.IsSubclassOf(typeof(FrameworkElement)))
                {
                    _styleToFrameworkElementTypeCache.Add(styleKey, type);
                    return(type);
                }


                _styleToFrameworkElementTypeCache.Add(styleKey, null);
                return(null);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Failed to find the framework element where style '{0}' is based on", styleKey);
                return(null);
            }
        }