static readonly XNamespace xNamespace = @"http://schemas.microsoft.com/winfx/2006/xaml"; // Used for example for "x:Name" attributes. public static IEnumerable <XElement> GetAppDotXamlResources(EnvDTE.Project currentProject, EnvDTE.Solution currentSolution, out string appDotXamlFullPath) { // Attempt to find the file "App.xaml": ProjectItem appDotXaml = FindAppDotXaml(currentProject, currentSolution); if (appDotXaml != null) { // Get the full path: appDotXamlFullPath = appDotXaml.Properties.Item("FullPath").Value.ToString(); // Get the file content: string appDotXamlContent = File.ReadAllText(appDotXamlFullPath); // 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: appDotXamlContent = HtmlPresenterRemover.RemoveHtmlPresenterNodes(appDotXamlContent); // Load the file as XML: XDocument xdoc = XDocument.Parse(appDotXamlContent); // Get the resources: var applicationDotResourcesNode = xdoc.Descendants(DefaultXamlNamespace + "Application.Resources").FirstOrDefault(); if (applicationDotResourcesNode != null) { IEnumerable <XElement> elementsInsideApplicationDotResourcesNode = applicationDotResourcesNode.Elements(); // This is either <ResourceDictionary>...</ResourceDictionary> or it is the list of resources (in case that <ResourceDictionary>...</ResourceDictionary> is implied). // Determine whether the <ResourceDictionary> tag is omitted (implicit) or explicitly specified: bool isTheResourceDictionaryTagExplicitlySpecified = elementsInsideApplicationDotResourcesNode.Any() && elementsInsideApplicationDotResourcesNode.First().Name == (DefaultXamlNamespace + "ResourceDictionary"); // Change all the the resources "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: if (isTheResourceDictionaryTagExplicitlySpecified) { ChangeAllXNameIntoXKey(elementsInsideApplicationDotResourcesNode.First()); } else { ChangeAllXNameIntoXKey(applicationDotResourcesNode); } return(elementsInsideApplicationDotResourcesNode); } } // Failure: appDotXamlFullPath = null; return(null); }
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); }