博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SharpDevelop浅析_2_User Interface
阅读量:4149 次
发布时间:2019-05-25

本文共 8072 字,大约阅读时间需要 26 分钟。

SharpDevelop浅析_2_User Interface

创建易扩展且功能模块松散耦合的应用程序

http://www.cnblogs.com/michael-zhang/articles/629724.html

Demo界面及功能解释
相关概念
Demo代码分析
总结
1、Demo界面及功能解释:
启动界面:
支持打开图片:
支持打开网页:
功能说明:程序有个浮动窗口显示当前磁盘文件,选择文件并双击后在中心窗口中以适当的察看器打开文件;持拖曳打开文件;程序关闭后再次启动时能够“记忆”前一次程序关闭时的窗体大小位置及打开过的文件。

2、相关概念:

主窗体部分:              Workbench
磁盘文件查看窗口:    Pad
文件查看窗口:         ViewContent
说明:
Pad可以使用ICSharpCode.Core的Addin插件机制来扩充Pad,如Visual Studio的资源管理器、类查看器、属性窗口、消息窗口等都属于Pad
ViewContent同样支持插件扩充,根据不同的文件类型使用不同的控件来处理显示,Demo中已实现的文件类型支持:网页文件、图片、普通文本,Visual Studio中的代码窗口、窗体设计器、资源编辑器、对象查看器等都属于ViewContent
Workbench与WorkbenchLayout结合使用来控制主窗口的外观显示,如普通的MDI(PhotoShop?)或Demo使用的WeifenLuo.WinFormsUI.Docking显示方式……

3、Demo代码分析

(注:下面的分析前提是你已经了解)
通 过上面的界面及功能说明,可以确定程序至少应包括接口声明、基本实现两个项目,而接口中又分为:Pad接口、ViewContent接口、 Workbench接口,这样的程序设计重点与难点在哪呢?思考后的结果应该是接口,从接口中能看到程序各部分是如何交互的,以及插件的扩展点,接口的设 计好坏决定着程序的易维护性与易扩展性,所以下面就重点看接口的定义(附介绍相关对象的创建等):

 1//  Core|Interface 定义部分:
 2public interface IPadContent : IDisposable
 3{
 4    System.Windows.Forms.Control Control get; }
 5    
 6    void RedrawContent();
 7}
 8public class PadDescriptor : IDisposable
 9{
10    Codon       codon;
11    IPadContent padContent;
12    bool        padContentCreated;
13    
14    public string Title {
15        get {
16            return codon.Properties["title"];
17        }
18    }
19    public string Icon {
20        get {
21            return codon.Properties["icon"];
22        }
23    }
24    public string Class {
25        get {
26            return codon.Properties["class"];
27        }
28    }
29    public IPadContent PadContent {
30        get {
31            CreatePad();
32            return padContent;
33        }
34    }
35    public void CreatePad()
36    {
37        if (!padContentCreated) {
38            padContentCreated = true;
39            padContent = (IPadContent)codon.AddIn.CreateObject(Class);
40        }
41    }
42    //省略部分方法、属性
43    public PadDescriptor(Codon codon)
44    {
45        this.codon = codon;
46    }
47}
48//  Gui项目中的实现
49class PadContentWrapper : DockContent
50{
51    PadDescriptor padDescriptor;    //通过此对象来获取相关属性
52    //
53}
看到IPadContent接口返回一个WinForm的Control控件,此控件在 实现时被相应的窗体获取并适当地显示,Demo中的磁盘查看器Pad返回的Control是一个UserConrol,使用了TreeView和 ListView组合。注意IPadContent接口中定义返回一个Control而非Form是很聪明的技巧,可以看到实现端的 PadContentWrapper继承自WeifenLuo.WinFormsUI.DockContent,这依赖于实现端的表现方式,而接口可以不 受影响。PadDescriptor是个辅助类,用以返回Pad相关的属性信息,包括返回IPadContent的实例对象。
接下来看Pad的xml声明及客户端调用:
 1//取自Entry项目的SD_UI.addin
 2<Path name = "/SharpDevelop/Workbench/Pads">
 3    <!--
 4    ProjectBrowser
 5    ClassBrowser
 6    SideBar
 7    ErrorList
 8    TaskList
 9    CompilerMessageView
10    PropertyPad
11    SearchResults
12    Bookmarks
13    DefinitionView
14    
15    -->
16    <Pad id       = "FileScout"
17         category = "Tools"
18         title    = "${res:MainWindow.Windows.FileScoutLabel}"
19         icon     = "PadIcons.FileBrowser"
20         shortcut = "Control|Alt|F"
21         class    = "SDUserInterface.GUI.Pad.FileScout"/>
22</Path>
23// 取自Gui项目的DefaultWorkbench.cs
24void InitializeWorkspace()
25{
26    //
27    ArrayList contents = AddInTree.GetTreeNode("/SharpDevelop/Workbench/Pads").BuildChildItems(this);
28    foreach (PadDescriptor content in contents)
29    {
30        if (content != null)
31        {
32            ShowPad(content);
33        }
34    }
35    //
36}

配置文件中的class指定了实现IPadContent的一个类型(全称限定名),使用时通过ICSharpCode.Core的AddInTree对象构建相关PadDescriptor集合……

下面来看ViewContent的定义:

 1// Core|Interface 定义部分:
 2public interface IViewContent : IDisposable
 3{
 4    Control Control getset; }
 5
 6    IWorkbenchWindow WorkbenchWindow getset; }
 7
 8    string TitleName getset; }
 9
10    string FileName getset; }
11
12    bool IsReadOnly get; }
13
14    void Load(string fileName);
15
16    event EventHandler TitleNameChanged;
17}
18public class DisplayBindingDescriptor
19{
20    object binding = null;
21    Codon codon;
22    
23    public IDisplayBinding Binding {
24        get {
25            if (binding == null{
26                binding = codon.AddIn.CreateObject(codon.Properties["class"]);
27            }
28            return binding as IDisplayBinding;
29        }
30    }
31    
32    public Codon Codon {
33        get {
34            return codon;
35        }
36    }
37    
38    public DisplayBindingDescriptor(Codon codon)
39    {
40        this.codon = codon;
41    }
42    
43    public bool CanAttachToFile(string fileName)
44    {
45        string fileNameRegex = codon.Properties["fileNamePattern"];
46        if (fileNameRegex == null || fileNameRegex.Length == 0// no regex specified
47            return true;
48        return Regex.IsMatch(fileName, fileNameRegex, RegexOptions.IgnoreCase);
49    }
50}
51public interface IDisplayBinding
52{
53    bool CanCreateContentForFile(string fileName);
54    
55    IViewContent CreateContentForFile(string fileName);
56}
57// Gui项目中的实现:
58public class SdiWorkspaceWindow : DockContent, IWorkbenchWindow
59{
60    IViewContent content;
61    //
62}

可以看到IViewContent同样是返回一个Conrol,供使用端(SdiWorkspaceWindow)根据需要封装组合; DisplayBindingDescriptor同样是个辅助类,除了返回ViewContent的相关属性信息外,提供了bool CanAttachToFile(string fileName)方法,用以判断当前显示插件是否可以显示相关类型的文件,这里的判断是通过配置文件中的fileNamePattern属性作正则判断 (注:文件名称符合一定规则的可能会用到此属性,一般不常用),注意到该辅助类返回了一个IDisplayBinding接口,查看该接口的方法可以看到 使用它来更进一步地判断当前文件是否是可支持类型(通过文件扩展名或试读取等方式),如果属于该插件支持类型的文件则创建并返回 IViewContenet接口。ViewContent插件的声明如下:

 1<Path name = "/SharpDevelop/Workbench/DisplayBindings">
 2    <DisplayBinding id               = "Browser"
 3                    supportedformats = "Web Pages"
 4                    class            = "SDUserInterface.GUI.ViewContent.BrowserDisplayBinding"/>
 5    <DisplayBinding id    = "Text"
 6                        insertafter = "Browser"
 7                        supportedformats = "Text Files,Source Files"
 8                        class = "SDUserInterface.GUI.ViewContent.TextViewDisplayBinding" />
 9    <DisplayBinding id    = "Image"
10                        insertbefore = "Text"
11                        supportedformats = "图片"
12                        class = "SDUserInterface.GUI.ViewContent.ImageDisplayBinding" />
13</Path>

值得注意的是insertbefore, insertafter 属性,此属性指明获取所有DisplayBindingDescriptor后的先后顺序,如:一个.rtf文件可以由Office-Word和记事本打开,一般要优先选择使用Word打开。

获取ViewContent对象的过程如下:双击磁盘文件Pad中的一个文件项时,调用FileService中的OpenFile(string fileName)方法,相关代码如下:

 1// 取自Gui项目中的Common/FileService.cs
 2public static IWorkbenchWindow OpenFile(string fileName)
 3{
 4    //
 5    IDisplayBinding binding = DisplayBindingService.GetBindingPerFileName(fileName);
 6    
 7    if (binding != null{
 8        binding.CreateContentForFile(fileName);
 9        WorkbenchSingleton.Workbench.ShowView(newContent);
10        //
11    }
 else {
12        throw new ApplicationException("Can't open " + fileName + ", no display codon found.");
13    }
14    return GetOpenFile(fileName);
15}
16// 取自Gui项目中的Common/DisplayBindingService.cs
17static DisplayBindingDescriptor GetCodonPerFileName(string filename)
18{
19    foreach (DisplayBindingDescriptor binding in bindings) {
20        if (binding.CanAttachToFile(filename)) {
21            if (binding.Binding != null && binding.Binding.CanCreateContentForFile(filename)) {
22                return binding;
23            }
24        }
25    }
26    return null;
27}

接下来看主窗体的定义:

 1public interface IWorkbench
 2{
 3    string Title  getset; }
 4
 5    List<IViewContent> ViewContentCollection get; }
 6
 7    List<PadDescriptor> PadContentCollection get; }
 8
 9    IWorkbenchWindow ActiveWorkbenchWindow get; }
10    
11    object ActiveContent get; }
12    
13    IWorkbenchLayout WorkbenchLayout getset; }
14    
15    void ShowView(IViewContent content);
16
17    void CloseAllViews();
18    
19    void CloseView(IViewContent content);
20    
21    void ShowPad(PadDescriptor content);
22    
23    PadDescriptor GetPad(Type type);
24
25    void RedrawAllComponents();
26}
27public interface IWorkbenchLayout
28{
29    bool FullScreen getset; }
30
31    IWorkbenchWindow ActiveWorkbenchwindow get; }
32
33    object ActiveContent get; }
34    
35    void Attach(IWorkbench workbench);
36    
37    void Detach();
38    
39    void ShowPad(PadDescriptor content);
40
41    void ShowPad(PadDescriptor content,bool bActivateIt);
42    
43    IWorkbenchWindow ShowView(IViewContent content);
44
45    void RedrawAllComponents();
46
47    void LoadConfiguration();
48    void StoreConfiguration();
49}

IWorkbench定义主窗体,IWorkbenchLayout定义窗体布局,可以看到IWorkbench的两个重要属性是Pad和 ViewContent的对象集合(维护已打开的窗体记录,避免重复打开等作用),其实现类的ShowPad()/ShowView()方法执行的操作即 向对应的集合添加成员,然后调用IWorkbenchLayout的ShowPad()/ShowView()。IWorkbenchLayout的 LoadConfiguration()和StoreConfiguration()方法用以在窗体加载或关闭时执行加载或保存子窗口布局的操作。

至此Demo的核心已分析完了,我们可以根据定义的接口扩展程序,如增加项目管理/查看Pad, 增加.swf等文件查看的ViewContent, 或更换主界面显示为普通MDI方式……

4、总结:

此Demo更说明了可以应用到很多的方面:
a, 界面组成部分Pad
b, 界面中不同类型的文件查看器ViewContent
c, 磁盘文件查看Pad的下侧文件列表针对不同文件显示相关图标(详见SD_UI.addin文件的"/Workspace/Icons"层次下的定义)
...
SharpDevelop的这种界面设计方案使得应用程序很容易扩展和进一步开发,而不与已开发的模块冲突;从中得到的启发是应用程序要重点设计接口(契约)以及处理对象间的关系……
说明:Demo代码基本来源于,删改了部分代码以使Demo精简和突出重点。

你可能感兴趣的文章
[LeetCode]Search Insert Position
查看>>
[LeetCode]Set Matrix Zeroes
查看>>
[LeetCode]Simplify Path
查看>>
[LeetCode]Sort Colors
查看>>
[LeetCode]Spiral Matrix
查看>>
[LeetCode]Spiral Matrix II
查看>>
[LeetCode]Sqrt(x)
查看>>
[LeetCode]String to Integer (atoi)
查看>>
[LeetCode]Subsets
查看>>
[LeetCode]Subsets II
查看>>
[LeetCode]Substring with Concatenation of All Words
查看>>
[LeetCode]Sudoku Solver
查看>>
[LeetCode]Sum Root to Leaf Numbers
查看>>
[LeetCode]Surrounded Regions
查看>>
[LeetCode]Swap Nodes in Pairs
查看>>
[LeetCode]Symmetric Tree
查看>>
[LeetCode]Text Justification
查看>>
[LeetCode]Trapping Rain Water
查看>>
[LeetCode]Triangle
查看>>
[LeetCode]Two Sum
查看>>