简体   繁体   English

Treeview与深层嵌套的对象?

[英]Treeview with deeply nested objects?

So, I'm trying to get a bit more into Roslyn and therefore writing an application that helps me analysing my solutions. 所以,我正在尝试更多地了解Roslyn,因此编写了一个帮助我分析解决方案的应用程序。

Should be said, I'm still relatively new to C# and WPF, so I may miss something important or obvious here. 应该说,我对C#和WPF来说还是比较新的,所以我可能会错过一些重要或明显的东西。

I would like to display the structure of my solution in a Treeview. 我想在Treeview中显示我的解决方案的结构。 I'm already able to export the structure of my solution to a textfile with an output like this: 我已经能够将我的解决方案的结构导出到具有如下输出的文本文件:

+ Analysing the following project: Testtool  
|+ Analysing the following document: Converters.cs  
||+ The following namespaces are referenced for the analysed file:  
|||- System  
|||- System.Collections.Generic  
|||- System.Linq  
|||- System.Text  
|||- System.Threading.Tasks  
|||- System.Windows.Data  
||- The file lives in the following namespace: Testtool  
||+ Analysing the following class of the current file: BooleanInverter  
|||+ The following modifiers are used for the class:  
||||- public  
||||- partial  
|||+ The following methods are defined in the currently analysed class:  
||||+ Convert  
|||||+ The following modifiers are used for the method:  
||||||- public  
||||+ ConvertBack  
|||||+ The following modifiers are used for the method:  
||||||- public  
|+ Analysing the following document: LoadingControl.xaml.cs  
||+ The following namespaces are referenced for the analysed file:  
|||- System  
|||- System.Collections.Generic  
|||- System.Linq  
|||- System.Text  
|||- System.Threading.Tasks  
|||- System.Windows  
|||- System.Windows.Controls  
|||- System.Windows.Data  
|||- System.Windows.Documents  
|||- System.Windows.Input  
|||- System.Windows.Media  
|||- System.Windows.Media.Imaging  
|||- System.Windows.Navigation  
|||- System.Windows.Shapes  
|||- System.ComponentModel  
||- The file lives in the following namespace: Testtool  
||+ Analysing the following class of the current file: LoadingControl  
|||+ The following modifiers are used for the class:  
||||- public  
||||- partial  
|||+ The following methods are defined in the currently analysed class:  
||||+ OnPropertyChanged  
|||||+ The following modifiers are used for the method:  
||||||- public  
|||+ The following properties are defined in the currently analysed class:  
||||+ SpinnerText  
|||||+ The following modifiers are used for the Property:  
||||||- public  

Now I'm not sure what a good way would be to display this structure in an object. 现在我不确定在一个对象中显示这个结构有什么好方法。 I mean, if there isn't a better possibility, I would create a corresponding object model, but I feel the need for such deeply nested objects feels wrong. 我的意思是,如果没有更好的可能性,我会创建一个相应的对象模型,但我觉得这种深层嵌套对象的需要感觉不对。 So, maybe someone has a better idea on this? 那么,也许有人对此有更好的想法?

As Dudi already indicates, the Syntax Visualizer also provides a treeview with the entire syntax tree, however, looking at your console output, it seems that you want to show a summary of the "bare syntax tree". 正如Dudi已经指出的那样,语法Visualizer还提供了整个语法树的树视图,但是,查看您的控制台输出,似乎您想要显示“裸语法树”的摘要。 I would go for a corresponding object model, indeed, since your analysis summarises the syntax tree very much, so you shouldn't need that many view models. 实际上,我会选择相应的对象模型,因为您的分析非常总结了语法树,因此您不需要那么多的视图模型。

I don't have any experience using the treeview with WPF, but probably you can just bind to the top level view model, which would represent the Solution , and it's children return the Project view models. 我没有任何使用WPF的树视图的经验,但可能你只能绑定到顶级视图模型,它代表Solution ,它的子项返回Project视图模型。 Using implicit data templates would do the rest. 使用隐式数据模板将完成剩下的工作。

If you want some sample code for this, I'm happy to add that. 如果你想要一些示例代码,我很乐意补充一下。

at first thanks a lot for your answers. 起初非常感谢你的回答。 They both helped me and brought up some good thoughts. 他们都帮助了我并提出了一些好的想法。 That said, there were still several things I greatly disliked about the „pure object model“ approach. 也就是说,对于“纯对象模型”方法,我仍然有很多不喜欢的事情。 But after some searching, thinking I have a solution that fits my needs while still is good to maintain in my opinion. 但经过一番搜索后,我认为我有一个符合我需求的解决方案,同时在我看来仍然很好。 The source code of the Syntax Visualizer from Roslyn brought me up to some of it. Roslyn的Syntax Visualizer的源代码让我了解了它的一些内容。 Key points: 关键点:

  • Defining a single “SyntaxTreeNode” property. 定义单个“SyntaxTreeNode”属性。 This property can contain a list of other SyntaxTreeNode objects. 此属性可以包含其他SyntaxTreeNode对象的列表。
  • Iterating through the solution with a given way, extract the informations I want. 以给定方式迭代解决方案,提取我想要的信息。
  • Implementing a way to specify the “parentNode” a new Node should be added to. 实现指定“parentNode”的方法应该添加一个新节点。 Check the kind of the currently examined SyntaxNode/Token. 检查当前检查的SyntaxNode / Token的类型。
  • Depending on the kind set the parameters of the node accordingly (here's still some good room for improvement/refactoring). 根据类型设置相应节点的参数(这里仍然有一些改进/重构的良好空间)。
  • Once a subnode is build, add it to the determined parent. 构建子节点后,将其添加到确定的父节点。
  • Define the property as ObservableCollection and then bind the TreeViews ItemSource to it. 将属性定义为ObservableCollection,然后将TreeViews ItemSource绑定到它。

Note that I'm using Syncfusions sfTreeGrid, so the way for handling the columns may differ. 请注意,我使用的是Syncfusions sfTreeGrid,因此处理列的方式可能会有所不同。 But it should get the point. 但它应该得到重点。

SyntaxTreeNode definition SyntaxTreeNode定义

public class SyntaxTreeNode 
{
    // A node can contain other nodes
    public ObservableCollection<SyntaxTreeNode> SyntaxTreeNodes { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
}

In the class that examines the solution 在检查解决方案的类中

    // The property used for binding the ItemsSource of the sfTreeView
    private ObservableCollection<SyntaxTreeNode> _TreeViewNodes;
    public ObservableCollection<SyntaxTreeNode> TreeViewNodes
    {
        get { return _TreeViewNodes; }
        set
        {
            _TreeViewNodes = value;
        }
    }

Central method for getting the hierarchical object. 获取分层对象的中心方法。 Not properly refactored yet. 尚未正确重构。 public void displaySyntaxTree() { // Init the property TreeViewNodes = new ObservableCollection(); public void displaySyntaxTree(){//初始化属性TreeViewNodes = new ObservableCollection();

        // Open the solution from the supplied path
        string solutionPath = @"Path to your solution";
        MSBuildWorkspace msWorkspace = MSBuildWorkspace.Create();
        Solution solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;

        // Analyze each project.
        foreach (var project in solution.Projects)
        {
            // Set project node
            SyntaxTreeNode projectNode = new SyntaxTreeNode();
            projectNode.Name = project.Name;
            projectNode.Type = "Project";
            projectNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>();

            // Add the defined node to the collection (first tree level).
            TreeViewNodes.Add(projectNode);

            // Set the recently added node as a “parentNode”. This will be used later to add the prebuild subnode (or a list of subnodes).
            SyntaxTreeNode parentProjectNode = CommonDataAccess.TreeViewNodes[CommonDataAccess.TreeViewNodes.Count - 1];

            // Get the list of referenced assemblies for the project. Subsequently create a new node that will then show them (a “root node” for the references if you want to say so). At this point the node does NOT yet get added to the collection that’s bound to the TreeView.
            IReadOnlyList<MetadataReference> projectReferences = project.MetadataReferences;
            SyntaxTreeNode referenceNode = new SyntaxTreeNode();
            referenceNode.Name = "Referenced assemblies";
            referenceNode.Type = "Reference list";
            referenceNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>();

            // Create a new node for each reference entry under the reference “root node” defined above.
            foreach (MetadataReference reference in projectReferences)
            {
                SyntaxTreeNode refNode = new SyntaxTreeNode();
                int refNameStart = reference.Display.LastIndexOf("\\") + 1;
                int refNameLength = reference.Display.Length - refNameStart;
                refNode.Name = reference.Display.Substring(refNameStart, refNameLength);
                refNode.Type = "Reference";
                referenceNode.SyntaxTreeNodes.Add(refNode);
            }

            // Now add the prebuild reference “root node” with the single reference nodes to the earlier defined project node (therefore creating a structure with two sublevels).
            parentProjectNode.SyntaxTreeNodes.Add(referenceNode);

            // Now go through the documents for the current project.
            foreach (Document document in project.Documents)
            {
                // Add a subnode to the current project for the document.
                SyntaxTreeNode TreeNode = new SyntaxTreeNode();
                TreeNode.Name = document.Name;
                TreeNode.Type = "File";

                parentProjectNode.SyntaxTreeNodes.Add(TreeNode);

                string path = document.FilePath;
                FileStream stream = File.OpenRead(path);

                SyntaxTree tree = CSharpSyntaxTree.ParseText(SourceText.From(stream), path: path);

                CompilationUnitSyntax root = (CompilationUnitSyntax)tree.GetRoot();

                // Similar to the project level, determine the node for each document now and set it as “parentNode”. So this is actually now on a sublevel (most likely the first).
                SyntaxTreeNode actNode = parentProjectNode.SyntaxTreeNodes[parentProjectNode.SyntaxTreeNodes.Count - 1];

                // Call the method for adding the subnodes for the current document. Passing with ref so no need to return a node.
                getSubNodes(root, ref actNode);
            }
        }
    }

    // Simple method for getting the childs of the SyntaxTree, init the sublevel collection and adding prepared subnodes to the parent node.
    public void getSubNodes(CompilationUnitSyntax parentTree, ref SyntaxTreeNode parentNode)
    {
        ChildSyntaxList childs = parentTree.ChildNodesAndTokens();
        List<SyntaxTreeNode> nodesToAdd = iterateSubNodes(childs);
        foreach (SyntaxTreeNode node in nodesToAdd)
        {
            if (parentNode.SyntaxTreeNodes == null)
                parentNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>();
            parentNode.SyntaxTreeNodes.Add(node);
        }
    }

    // Method for examing the kind of the current node. If no proper kind was found “recursively” searching through the nodes. Has to be improved, expanded and refactored still (dictionary could be suitable for this).
    public List<SyntaxTreeNode> iterateSubNodes (ChildSyntaxList syntaxList)
    {
        List<SyntaxTreeNode> nodesToReturn = new List<SyntaxTreeNode>();

        foreach (SyntaxNodeOrToken nodeOrToken in syntaxList)
        {
            SyntaxKind childKind = nodeOrToken.Kind();
            if (childKind == SyntaxKind.UsingDirective)
            {
                SyntaxTreeNode tokenToAdd = new SyntaxTreeNode();
                UsingDirectiveSyntax usingNode = (UsingDirectiveSyntax)nodeOrToken;
                tokenToAdd = addUsing(usingNode);
                nodesToReturn.Add(tokenToAdd);
            }
            else if (childKind == SyntaxKind.NamespaceDeclaration)
            {
                SyntaxTreeNode tokenToAdd = new SyntaxTreeNode();
                NamespaceDeclarationSyntax namespaceNode = (NamespaceDeclarationSyntax)nodeOrToken;
                tokenToAdd = addNamespace(namespaceNode);
                nodesToReturn.Add(tokenToAdd);
            }
            else
            {
                iterateSubNodes(nodeOrToken.ChildNodesAndTokens());
            }
        }

        // Return the node (or a list of them) for further processing.
        return nodesToReturn;
    }

    // Method for defining the parameters for a NameSpaceDeclaration.
    private SyntaxTreeNode addNamespace(NamespaceDeclarationSyntax namespaceSyntax)
    {
        SyntaxTreeNode nodeToReturn = new SyntaxTreeNode();
        nodeToReturn.Name = namespaceSyntax.Name.ToString();
        nodeToReturn.Type = "Namespace Definition";

        return nodeToReturn;
    }

    // The same as for Namespace, here just for usingDirectives.
    private SyntaxTreeNode addUsing(UsingDirectiveSyntax usingSyntax)
    {
        SyntaxTreeNode nodeToReturn = new SyntaxTreeNode();
        nodeToReturn.Name = usingSyntax.Name.ToString();
        nodeToReturn.Type = usingSyntax.Kind().ToString();

        return nodeToReturn;
    }

The code above gives me a view like this in the end: 上面的代码最终给了我这样的观点: 在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM