/* * static IEnumerable<XElement> GetResourcesUnderApplicationDotResourcesNode(XElement applicationDotResources, out XElement resourceDictionaryNode) * { * // First, check if the "<ResourceDictionary>" is explicitly specified, or if it is implied: * resourceDictionaryNode = applicationDotResources.Elements(DefaultXamlNamespace + "ResourceDictionary").FirstOrDefault(); * bool isResourceDictionarySpecified = (resourceDictionaryNode != null); * * // If no "<ResourceDictionary>" is explicitly specified, it means that it is implicit, so we set it to be the parent node: * if (!isResourceDictionarySpecified) * resourceDictionaryNode = applicationDotResources; * * // Get the children of the ResourceDictionary: * IEnumerable<XElement> childrenOfResourceDictionary = resourceDictionaryNode.Elements(); * * // Get the resources by taking the children of the ResourceDictionary and removing the ones that correspond to ResourceDictionary properties (such as <ResourceDictionary.MergedDictionaries>): * IEnumerable<XElement> resources = from elt in childrenOfResourceDictionary * where !elt.Name.LocalName.Contains(".") * select elt; * * return resources; * } */ public static void ResolveAndMergeTheMergedDictionaries(XElement element, string currentXamlFileNameAndPath, Dictionary <string, Project> assemblyNameToProjectDictionary, HashSet <string> allXamlFilesVisitedDuringRecursion, ResourcesCache resourcesCache) // "allXamlFilesVisitedDuringRecursion" is used to prevent infinite execution during the recursion due to circular references. { // First, let us put the <ResourceDictionary> nodes into a List<> so that we can modify them during the foreach without causing issues with the iterator on a collection being modified: List <XElement> resourceDictionaryNodes = element.Descendants(DefaultXamlNamespace + "ResourceDictionary").ToList(); // Then, iterate through all the <ResourceDictionary> nodes and "in-place expand" the ones that reference another file: foreach (XElement resourceDictionaryNode in resourceDictionaryNodes) { var sourceAttribute = resourceDictionaryNode.Attributes("Source").FirstOrDefault(); if (sourceAttribute != null) { string uri = sourceAttribute.Value.ToString(); // Get the full referenced ResourceDictionary (or read from cache if any): string referencedResourceDictionaryFullPath; XElement resourceDictionary = GetResourceDictionary(uri, currentXamlFileNameAndPath, assemblyNameToProjectDictionary, resourcesCache, out referencedResourceDictionaryFullPath); // Change all the the children "x:Name" into "x:Key" so that we do not get an error if there are two entries with the same "x:Name" in the whole document: ChangeAllXNameIntoXKey(resourceDictionary); // Apply the recursion to resolve the nested resource dictionary references: if (!allXamlFilesVisitedDuringRecursion.Contains(referencedResourceDictionaryFullPath)) { allXamlFilesVisitedDuringRecursion.Add(referencedResourceDictionaryFullPath); ResolveAndMergeTheMergedDictionaries(resourceDictionary, referencedResourceDictionaryFullPath, assemblyNameToProjectDictionary, allXamlFilesVisitedDuringRecursion, resourcesCache); } else { throw new Exception("Circular references in ResourceDictionary files has been detected (file name: " + referencedResourceDictionaryFullPath + ")."); } // Replace the current node with the full ResourceDictionary: resourceDictionaryNode.ReplaceWith(resourceDictionary); } } }
static XElement GetResourceDictionary(string uri, string currentXamlFileNameAndPath, Dictionary <string, Project> assemblyNameToProjectDictionary, ResourcesCache resourcesCache, out string resourceDictionaryFullPath) { // Determine the file absolute path of the file (it includes the file name and path): resourceDictionaryFullPath = GetAbsolutePath(uri, currentXamlFileNameAndPath, assemblyNameToProjectDictionary); // Read from cache if any (this avoids reloading the files at every refresh, to improve performance: XElement resourceDictionaryContent; if (resourcesCache.ResourceDictionaryFileNameToContent.ContainsKey(resourceDictionaryFullPath)) { resourceDictionaryContent = resourcesCache.ResourceDictionaryFileNameToContent[resourceDictionaryFullPath]; } else { //------------------------ // Not found in the cache //------------------------ // Get the file content: string resourceDictionaryXaml = File.ReadAllText(resourceDictionaryFullPath); // Remove the content of all the "HtmlPresenter" nodes, because the content may be not well formatted and may lead to a syntax error when parsing the XDocument: resourceDictionaryXaml = HtmlPresenterRemover.RemoveHtmlPresenterNodes(resourceDictionaryXaml); // Load the file as XML: XDocument xdoc = XDocument.Parse(resourceDictionaryXaml); // Get the content: resourceDictionaryContent = xdoc.Root; // Add to cache: resourcesCache.ResourceDictionaryFileNameToContent.Add(resourceDictionaryFullPath, resourceDictionaryContent); } return(resourceDictionaryContent); }
static bool ProcessXamlForDisplay(string sourceXaml, string sourceXamlFileNameAndPath, EnvDTE.Project currentProject, EnvDTE.Solution currentSolution, Dictionary <string, EnvDTE.Project> assemblyNameToProjectDictionary, ResourcesCache resourcesCache, out string outputXaml, out string errorsIfAny, out HashSet <string> warningsAndTips) { warningsAndTips = new HashSet <string>(); // Default output values: outputXaml = sourceXaml; errorsIfAny = ""; //--------------------------------------- // Remove the content of all the "HtmlPresenter" nodes, because the content may be not well formatted and may lead to a syntax error when parsing the XDocument: //--------------------------------------- sourceXaml = HtmlPresenterRemover.RemoveHtmlPresenterNodes(sourceXaml); //--------------------------------------- // Read the XDocument: //--------------------------------------- System.Xml.Linq.XDocument xdoc; try { xdoc = System.Xml.Linq.XDocument.Parse(sourceXaml); } catch (Exception ex) { errorsIfAny = ex.Message; return(false); } //--------------------------------------- // Remove the "x:Class" attribute if any: //--------------------------------------- if (xdoc.Root != null) { // Remove the "x:Class" attribute if any: XNamespace xNamespace = "http://schemas.microsoft.com/winfx/2006/xaml"; XAttribute classAttributeIfAny = xdoc.Root.Attribute(xNamespace + "Class"); if (classAttributeIfAny != null) { classAttributeIfAny.Remove(); } } //--------------------------------------- // Replace the root control with a UserControl (if it is not already one) and keep only the properties and attributes supported by the UserControl class: //--------------------------------------- ProcessNodeToMakeItCompatibleWithWpf.ReplaceRootWithUserControl(xdoc); //--------------------------------------- // Get the styles and other resources defined in App.xaml //--------------------------------------- IEnumerable <XElement> appDotXamlResources; string appDotXamlFullPath; if (resourcesCache.AppDotXamlResources != null) // This avoids reloading App.xaml at every refresh, to improve performance. { // Read from cache: appDotXamlResources = resourcesCache.AppDotXamlResources; appDotXamlFullPath = resourcesCache.AppDotXamlFullPath; } else { // Attempt to find App.xaml and read it: appDotXamlResources = ResolvingReferencedXamlResources.GetAppDotXamlResources(currentProject, currentSolution, out appDotXamlFullPath); resourcesCache.AppDotXamlResources = appDotXamlResources; resourcesCache.AppDotXamlFullPath = appDotXamlFullPath; } //--------------------------------------- // Resolve all the "Merged Dictionaries", and merge them so that there are no more references to other XAML files: //--------------------------------------- // Process the resources defined in App.xaml: if (appDotXamlResources != null) { try { foreach (XElement element in appDotXamlResources) { ResolvingReferencedXamlResources.ResolveAndMergeTheMergedDictionaries(element, appDotXamlFullPath, assemblyNameToProjectDictionary, new HashSet <string>(), resourcesCache); } } catch (Exception ex) { errorsIfAny = ex.Message; return(false); } } // Process the resources defined in the current file: try { ResolvingReferencedXamlResources.ResolveAndMergeTheMergedDictionaries(xdoc.Root, sourceXamlFileNameAndPath, assemblyNameToProjectDictionary, new HashSet <string>(), resourcesCache); } catch (Exception ex) { errorsIfAny = ex.Message; return(false); } //--------------------------------------- // Surround the document with a control in which we inject all the resources that we found in the end-user's "App.xaml" file: //--------------------------------------- if (appDotXamlResources != null && appDotXamlResources.Any()) { // Put those resouces in a control that will surround the xaml of the current page: var surroundingControlForResources = new XElement(DefaultXamlNamespace + "Border", xdoc.Root); surroundingControlForResources.Add(new XAttribute(XNamespace.Xmlns + "x", @"http://schemas.microsoft.com/winfx/2006/xaml")); // Note: This is required in case the XAML code contains the "x" prefix inside Markup Extensions (such as "{x:Null}"). It is not needed for the "x" prefix in elements and attributes (such as "x:Name"), because those are automatically imported when copying nodes, where as markup extensions are considered as simple strings by "Linq to XML". surroundingControlForResources.Add(new XAttribute("Tag", "AddedByDesignerToInjectAppDotXamlResources")); var resourcesContainer = new XElement(DefaultXamlNamespace + "Border.Resources"); surroundingControlForResources.AddFirst(resourcesContainer); resourcesContainer.Add(appDotXamlResources); // Replace the document with the one surrounded by the new control: xdoc = new XDocument(surroundingControlForResources); } #if Display_The_Processed_XAML // Uncomment to display the processed XAML: errorsIfAny = xdoc.Root.ToString(); outputXaml = xdoc.Root.ToString(); return(false); #endif //--------------------------------------- // Iterate through the document using a Stack<XElement> (pre-order traversal, no recursion) and process the nodes: //--------------------------------------- XElement root = xdoc.Root; Stack <System.Xml.Linq.XElement> stack = new Stack <System.Xml.Linq.XElement>(); stack.Push(root); while (stack.Count > 0) { System.Xml.Linq.XElement current = stack.Pop(); bool elementWasReplacedWithPlaceholder = false; // default value //--------------------------------------- // Process node to make it compatible with WPF (remove events such as PointerPressed, change "Page" to "UserControl", etc.): //--------------------------------------- ProcessNodeToMakeItCompatibleWithWpf.Process(current, ref warningsAndTips); //--------------------------------------- // Remove unknown nodes and attributes, and display "Cannot preview this element" instead: //--------------------------------------- // Verify that the node is not a property: if (!current.Name.LocalName.Contains(".")) { bool isKnownType = false; // Check to see if the element corresponds to a known type (only for elements that are supposed to be in the default namespace): Type type; if (current.Name.NamespaceName == DefaultXamlNamespace && TryGetType(current.Name, out type)) { isKnownType = true; // List all the event handlers of the type: List <string> eventHandlers = new List <string>(); foreach (EventInfo eventInfo in type.GetEvents()) { eventHandlers.Add(eventInfo.Name); } // Duplicate the list of attributes so that when we remove one, it doesn't affect the iteration: List <System.Xml.Linq.XAttribute> attributesListCopy = new List <System.Xml.Linq.XAttribute>(); foreach (System.Xml.Linq.XAttribute attr in current.Attributes()) { attributesListCopy.Add(attr); } // Remove event handlers: foreach (System.Xml.Linq.XAttribute attr in attributesListCopy) { // Check if the attribute is an event handler: //test+= " |||| " + attr.Name.LocalName + " " + attr.Name.NamespaceName + " " + (!attr.Name.LocalName.Contains(".")).ToString() + " " + eventHandlers.Contains(attr.Name.LocalName).ToString(); if (!attr.Name.LocalName.Contains(".") && eventHandlers.Contains(attr.Name.LocalName)) { // Remove the attrbute: attr.Remove(); } } } else if (current.Name.NamespaceName.EndsWith("assembly=mscorlib")) { isKnownType = true; } // If not known type, replace the element with a placeholder that says "Unable to display": if (!isKnownType) { // Create a border with inside a TextBlock that says "Unable to display": System.Xml.Linq.XElement msg = new System.Xml.Linq.XElement(DefaultXamlNamespace + "Border"); System.Xml.Linq.XElement msgTxt = new System.Xml.Linq.XElement(DefaultXamlNamespace + "TextBlock"); // Add the newly created stuff to the XAML: current.ReplaceWith(msg); msg.Add(msgTxt); // Set some attributes: msg.Add(new XAttribute("Background", "DarkRed")); msg.Add(new XAttribute("Opacity", "0.5")); msg.Add(new XAttribute("Tag", "AddedByDesignerAsPlaceholder")); msgTxt.Add(new XAttribute("Text", "Unable to preview this element")); msgTxt.Add(new XAttribute("TextWrapping", "Wrap")); msgTxt.Add(new XAttribute("TextAlignment", "Center")); msgTxt.Add(new XAttribute("HorizontalAlignment", "Stretch")); msgTxt.Add(new XAttribute("VerticalAlignment", "Center")); msgTxt.Add(new XAttribute("FontSize", "12")); msgTxt.Add(new XAttribute("Foreground", "#AAFFFFFF")); msgTxt.Add(new XAttribute("Tag", "AddedByDesignerForRendering")); // Give to that Border the same positioning information as the element that it replaces: MoveAttributeIfAny("Width", current, msg); MoveAttributeIfAny("Height", current, msg); MoveAttributeIfAny("HorizontalAlignment", current, msg); MoveAttributeIfAny("VerticalAlignment", current, msg); MoveAttributeIfAny("Margin", current, msg); MoveAttributeIfAny("Opacity", current, msg); MoveAttributeIfAny("MaxWidth", current, msg); MoveAttributeIfAny("MaxHeight", current, msg); MoveAttributeIfAny("MinWidth", current, msg); MoveAttributeIfAny("MinHeight", current, msg); MoveAttributeIfAny("Visibility", current, msg); MoveAttributeIfAny("Canvas.Left", current, msg); MoveAttributeIfAny("Canvas.Top", current, msg); MoveAttributeIfAny((XNamespace)"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" + "DockPanel.Dock", current, msg); MoveAttributeIfAny((XNamespace)"http://schemas.microsoft.com/winfx/2006/xaml" + "Name", current, msg); MoveAttributeIfAny((XNamespace)"http://schemas.microsoft.com/winfx/2006/xaml" + "Key", current, msg); // Remember that the element was replaced: elementWasReplacedWithPlaceholder = true; } } // Continue with the children of this element: if (!elementWasReplacedWithPlaceholder) { foreach (XElement element in current.Elements()) { stack.Push(element); } } } errorsIfAny = ""; //xdoc.ToString(); outputXaml = root.ToString(); return(true); }