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);
        }
Exemple #3
0
        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);
        }