SharpDevelop插件系统代码阅读笔记

发布于:2024-04-24 ⋅ 阅读:(24) ⋅ 点赞:(0)

SharpDevelop插件系统代码阅读笔记

1.插件系统实现细节


/// <summary>
///AddIn.cs 通过在插件dll里面查找类名,再创建对象,这个功能MEF已经可以实现了
/// </summary>
/// <param name="className"></param>
/// <returns></returns>
public object CreateObject(string className)
{
    Type t = FindType(className);
    if (t != null)
        return Activator.CreateInstance(t);
    else
        return null;
}

该方法的缺点就是需要传入类名,那么就需要在xml文件里面显示的将类名标记出来,再去解析。但是MEF功能通过Attribute就可以实现,不需要在xml里面显示的标记出来。但是,AddIn的组织方式肯定还是有其独到之处,继续往下看。
那么,获取到对象之后,是怎么组织管理起来的呢,是怎么插入到树节点中去的呢?

2.菜单栏的实现

static object CreateMenuItemFromDescriptor(MenuItemDescriptor descriptor)
{
    Codon codon = descriptor.Codon;
    string type = codon.Properties.Contains("type") ? codon.Properties["type"] : "Command";
    bool createCommand = codon.Properties["loadclasslazy"] == "false";
    
    switch (type) {
        case "Separator":
            return new MenuSeparator(codon, descriptor.Parameter, descriptor.Conditions);
        case "CheckBox":
            return new MenuCheckBox(codon, descriptor.Parameter, descriptor.Conditions);
        case "Item":
        case "Command":
            return new MenuCommand(codon, descriptor.Parameter, descriptor.Conditions);
        case "Menu":
            return new Menu(codon, descriptor.Parameter, ConvertSubItems(descriptor.SubItems), descriptor.Conditions);
        case "Builder":
            return codon.AddIn.CreateObject(codon.Properties["class"]);
        default:
            throw new System.NotSupportedException("unsupported menu item type : " + type);
    }
}

上面这段代码在MenuService.cs里面定义,菜单栏是AddIn应用的地方之一。
思考:如何结合MEF和AddIn框架在一起?
我的现有代码里面的菜单栏是通过大量重写基类实现的,这样做对后续的扩展并不友好。因此,还是想能不能将插件系统引入到我的项目中来。虽然在反射部分有些许区别,但是这点区别可以先忽略不计。
主要功能:
1.xml文件的结构和解析
2.

菜单栏命令所在文件
SharpDevelop\src\Main\Base\Project\Src\Commands\FileCommands.cs

public class OpenFile : AbstractMenuCommand
{
    public override void Run()
    {
        using (OpenFileDialog fdiag  = new OpenFileDialog()) {
            fdiag.AddExtension    = true;
            
            fdiag.Filter = ProjectService.GetAllFilesFilter();
            fdiag.FilterIndex     = 0;
            fdiag.Multiselect     = true;
            fdiag.CheckFileExists = true;
            
            if (fdiag.ShowDialog(SD.WinForms.MainWin32Window) == DialogResult.OK) {
                OpenFiles(Array.ConvertAll(fdiag.FileNames, FileName.Create));
            }
        }
    }
    
    protected virtual void OpenFiles(FileName[] fileNames)
    {
        foreach (var name in fileNames) {
            SD.FileService.OpenFile(name);
        }
    }
}

如上面代码,继承自 AbstractMenuCommand

2. 项目树

public abstract class AbstractProjectBrowserTreeNode : ExtTreeNode, IDisposable

ProjectBrowserPanel 这个里面应该有项目Pad里面的右键菜单

public ProjectBrowserPanel()
		{
			projectBrowserControl      = new ProjectBrowserControl();
			projectBrowserControl.Dock = DockStyle.Fill;
			Controls.Add(projectBrowserControl);
			
			if (SD.AddInTree.GetTreeNode("/SharpDevelop/Pads/ProjectBrowser/ToolBar/Standard", false) != null) {
				toolStrip = SD.WinForms.ToolbarService.CreateToolStrip(this, "/SharpDevelop/Pads/ProjectBrowser/ToolBar/Standard");
				toolStrip.ShowItemToolTips  = true;
				toolStrip.Dock = DockStyle.Top;
				toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
				toolStrip.Stretch   = true;
				standardItems = new ToolStripItem[toolStrip.Items.Count];
				toolStrip.Items.CopyTo(standardItems, 0);
				Controls.Add(toolStrip);
			}
			projectBrowserControl.TreeView.BeforeSelect += TreeViewBeforeSelect;
		}

SD.AddInTree.GetTreeNode()和SD.WinForms.ToolbarService.CreateToolStrip()这两个方法是关键。

WinFormsToolbarService
ToolbarService.CreateToolStrip(object owner, string addInTreePath)
AddInTree.GetTreeNode(addInTreePath)

public static AddInTreeNode GetTreeNode(string path, bool throwOnNotFound = true)
{
    var addInTree = ServiceSingleton.GetRequiredService<IAddInTree>();
    return addInTree.GetTreeNode(path, throwOnNotFound);
}


public AddInTreeNode GetTreeNode(string path, bool throwOnNotFound = true)
{
    if (path == null || path.Length == 0) {
        return rootNode;
    }
    string[] splittedPath = path.Split('/');
    AddInTreeNode curPath = rootNode;
    for (int i = 0; i < splittedPath.Length; i++) {
        if (!curPath.ChildNodes.TryGetValue(splittedPath[i], out curPath)) {
            if (throwOnNotFound)
                throw new TreePathNotFoundException(path);
            else
                return null;
        }
    }
    return curPath;
}

//TreeNode是怎么创建的
AddInTreeNode CreatePath(AddInTreeNode localRoot, string path)
{
    if (path == null || path.Length == 0) {
        return localRoot;
    }
    string[] splittedPath = path.Split('/');
    AddInTreeNode curPath = localRoot;
    int i = 0;
    while (i < splittedPath.Length) {
        if (!curPath.ChildNodes.ContainsKey(splittedPath[i])) {
            curPath.ChildNodes[splittedPath[i]] = new AddInTreeNode();//ChildNodes是一个哈希表,key是节点名字,value只是一个实例
        }
        curPath = curPath.ChildNodes[splittedPath[i]];
        ++i;
    }
    
    return curPath;
}

3.1 项目窗口

public static void ShowProjectOptions(IProject project)
{
    if (project == null)
    {
        return;
    }
    foreach (IViewContent viewContent in SD.Workbench.ViewContentCollection)
    {
        ProjectOptionsView projectOptions = viewContent as ProjectOptionsView;
        if (projectOptions != null && projectOptions.Project == project)
        {
            projectOptions.WorkbenchWindow.SelectWindow();
            return;
        }
    }
    try
    {
        AddInTreeNode projectOptionsNode = AddInTree.GetTreeNode("/SharpDevelop/BackendBindings/ProjectOptions/" + project.Language);
        ProjectOptionsView projectOptions = new ProjectOptionsView(projectOptionsNode, project);
        SD.Workbench.ShowView(projectOptions);
    }
    catch (TreePathNotFoundException)
    {
        MessageService.ShowError("${res:Dialog.ProjectOptions.NoPanelsInstalledForProject}");
    }
}

项目树的解析

public ProjectBrowserPanel()
{
	projectBrowserControl      = new ProjectBrowserControl();
	projectBrowserControl.Dock = DockStyle.Fill;
	Controls.Add(projectBrowserControl);
	
	if (SD.AddInTree.GetTreeNode("/SharpDevelop/Pads/ProjectBrowser/ToolBar/Standard", false) != null) {
		toolStrip = SD.WinForms.ToolbarService.CreateToolStrip(this, "/SharpDevelop/Pads/ProjectBrowser/ToolBar/Standard");
		toolStrip.ShowItemToolTips  = true;
		toolStrip.Dock = DockStyle.Top;
		toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
		toolStrip.Stretch   = true;
		standardItems = new ToolStripItem[toolStrip.Items.Count];
		toolStrip.Items.CopyTo(standardItems, 0);
		Controls.Add(toolStrip);
	}
	projectBrowserControl.TreeView.BeforeSelect += TreeViewBeforeSelect;
}
```cpp
/// <summary>
/// 查看解决方案
/// </summary>
/// <param name="solution"></param>
public void ViewSolution(ISolution solution)
{
	AbstractProjectBrowserTreeNode solutionNode = new SolutionNode(solution);
	treeView.Clear();
	solutionNode.AddTo(treeView);

	foreach (var treeObject in solution.Items)//Items里面是数据
	{
		if (treeObject is IProject)
		{
			NodeBuilders.AddProjectNode(solutionNode, (IProject)treeObject);//如果是Project,则加入到Builders里面
		}
		else
		{
			SolutionFolderNode folderNode = new SolutionFolderNode((ISolutionFolder)treeObject);//文件夹节点
			folderNode.InsertSorted(solutionNode);//讲自己插进父亲节点中
		}
	}

	solutionNode.Expand();//展开
}

4 系统里面那么多右键菜单怎么实现

public class ProjectBrowserPanel : System.Windows.Forms.UserControl
{
ToolStrip toolStrip;//工具栏
ProjectBrowserControl projectBrowserControl;//项目浏览器控件

/// <summary>
/// 从一个解决方案(Solution)中构建一个树视图
/// </summary>
/// <param name="solution"></param>
public void ViewSolution(ISolution solution)
{
	AbstractProjectBrowserTreeNode solutionNode = new SolutionNode(solution);
	treeView.Clear();
	solutionNode.AddTo(treeView);//创建一个解决方案节点(solutionNode),并将其添加到树视图中。

	foreach (var treeObject in solution.Items)//Items里面是数据,遍历解决方案中的每个项目和服务器
	{
		if (treeObject is IProject)
		{
			NodeBuilders.AddProjectNode(solutionNode, (IProject)treeObject);//对于每个项目,创建一个项目节点(projectNode)并将其添加到项目节点中。
		}
		else
		{
			SolutionFolderNode folderNode = new SolutionFolderNode((ISolutionFolder)treeObject);//对于每个文件夹,创建一个文件夹节点(folderNode)。
			folderNode.InsertSorted(solutionNode);//并将folderNode插入到父节点中
		}
	}

	solutionNode.Expand();//展开解决方案节点以显示树视图。
}