Ext.Net 1.2.0_在 Web 应用程序中自定义 TreePanel 控件

本文内容

  • 概述
  • 演示自定义 TreePanel 控件
  • 运行结果
  • 说明

概述

当我最开始使用 VS 开发 ASP.NET Web 应用程序时,使用最多的就是用户控件(.ascx)。它的方便在于,可以将界面分割成一个个小的功能模块,也就是利用 ASP.NET 自带的控件组合成一个新的控件,然后在需要的界面“拖”进去就行。

这种方式,不仅对后台方便,对前台也是。比如,对于页面,以及页面的任何一个功能区域,我们都可以分成顶部、底部和中间内容部分,那么就可以对 CSS 做统一设置。

现在我使用 Ext.Net,虽然其 Demo 提供了 SimpleTask Demo,演示了如何自定义、封装一个 Ext.Net 控件,看上去很不错。但一是不熟悉,二是使用用户控件(.ascx)习惯了,而且似乎用户控件足够现在的项目了。

但某天,当我偶然用 Yahoo YSLOW 插件测试我的页面时,突然发现我的用户控件竟然出现了好几个 404 错误。虽然页面在运行时完全正常。我将用户控件需要的脚本,以外部文件方式添加到用户控件,不想将外部脚本在页面里引用,将来不好维护,同时,也不想直接将脚本代码嵌入到用户控件里。毕竟,外部文件可以被浏览器缓存,而嵌入代码则不能。因为,页面和用户控件初始化顺序的问题。

404 错误是个恶心的错误。它能让浏览器做很多无用功,消耗响应时间。我还是改程序吧~~~呵呵,针对目前的这个,用户控件的确存在一些问题。

本文演示如何利用 Ext.Net 封装一个自定义控件。它通常由两个文件、三部分组成:控件类文件和外部脚本文件。其中,控件类文件是一个分部类,分别实现控件的 UI 代码和逻辑代码。UI 代码负责创建控件标记;逻辑代码创建控件的客户端事件和服务器端事件。外部脚本文件用于在客户端操作控件。

演示自定义 TreePanel 控件

先看看封装后,在页面中引用自定义控件,代码如下:

<%@ Page Language="C#" %>
 
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<%@ Register Assembly="ExtNetTreePanleCustomControl" Namespace="ExtNetTreePanleCustomControl.MyCustomControl"
    TagPrefix="MyControl" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />
 
    <script src="resources/myTreePanel.js" type="text/javascript"></script>
 
</head>
<body>
    <form id="form1" runat="server">
    <ext:ResourceManager ID="ResourceManager1" runat="server" InitScriptMode="Linked"
        RemoveViewState="true" IDMode="Explicit" />
    <MyControl:MyTreePanel ID="MyTreePanel1" runat="server" />
    </form>
</body>
</html>

其中,"<MyControl:MyTreePanel ID="MyTreePanel1" runat="server" />" 是自定义的 TreePanel 控件。

  • 自定义控件的分部类 MyTreePanel,文件名分别为 MyTreePanelLogic.cs 和 MyTreePanelUI.cs
using System;
using System.Collections.Generic;
using System.Linq;
 
using Ext.Net.Utilities;
using Ext.Net;
 
namespace ExtNetTreePanleCustomControl.MyCustomControl
{
    [DirectMethodProxyID(IDMode = DirectMethodProxyIDMode.None)]
    public partial class MyTreePanel
    {
        public const string SCOPE = "MyCustomControl.MyTreePanel";
 
        private void InitLogic()
        {
            this.Listeners.Render.Fn = MyTreePanel.SCOPE + ".init";
            this.Listeners.Render.Scope = MyTreePanel.SCOPE;
            this.Listeners.CheckChange.Handler = MyTreePanel.SCOPE + ".SelectParentChildNodes(node,checked);";
 
 
            Ext.Net.Button button = new Ext.Net.Button();
            button.ID = "btnGet";
            button.Text = "获得勾选";
            button.Listeners.Click.Handler = MyTreePanel.SCOPE + ".getSelectedNode(#{" + this.ID + "})";
            this.Buttons.Add(button);
            button = new Ext.Net.Button();
            button.ID = "btnClear";
            button.Text = "清除勾选";
            button.Listeners.Click.Handler = MyTreePanel.SCOPE + ".clearSelectedNode(#{" + this.ID + "})";
            this.Buttons.Add(button);
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Ext.Net;
using Newtonsoft.Json;
 
namespace ExtNetTreePanleCustomControl.MyCustomControl
{
    public partial class MyTreePanel : TreePanel
    {
        public MyTreePanel()
        {
            this.ID = "MyTreePanel1";
            this.Title = "MyTreePanel";
            this.Width = System.Web.UI.WebControls.Unit.Pixel(300);
            this.Height = System.Web.UI.WebControls.Unit.Pixel(400);
            this.UseArrows = true;
            this.AutoScroll = true;
            this.Animate = true;
            this.EnableDD = true;
            this.ContainerScroll = true;
            this.RootVisible = false;
 
            this.LoadMask.ShowMask = true;
 
            this.SelectionModel.Add(new Ext.Net.DefaultSelectionModel());
 
            this.BuildTree();
            this.InitLogic();
        }
 
        private void BuildTree()
        {
            Ext.Net.TreeNode root = new Ext.Net.TreeNode("Composers");
            root.Expanded = true;
            this.Root.Add(root);
 
 
            List<Composer> composers = MyData.GetData();
 
            foreach (Composer composer in composers)
            {
                Ext.Net.TreeNode composerNode = new Ext.Net.TreeNode(composer.Name, Icon.UserGray);
                composerNode.Checked = Ext.Net.ThreeStateBool.False;
                composerNode.Leaf = false;
                composerNode.Icon = Ext.Net.Icon.Folder;
                root.Nodes.Add(composerNode);
 
                foreach (Composition composition in composer.Compositions)
                {
                    Ext.Net.TreeNode compositionNode = new Ext.Net.TreeNode(composition.Type.ToString());
                    compositionNode.Checked = Ext.Net.ThreeStateBool.False;
                    compositionNode.Leaf = false;
                    compositionNode.Icon = Ext.Net.Icon.Folder;
                    composerNode.Nodes.Add(compositionNode);
 
                    foreach (Piece piece in composition.Pieces)
                    {
                        Ext.Net.TreeNode pieceNode = new Ext.Net.TreeNode(piece.Title, Icon.Music);
                        pieceNode.Checked = Ext.Net.ThreeStateBool.False;
                        pieceNode.Leaf = true;
                        compositionNode.Nodes.Add(pieceNode);
                    }
                }
            }
        }
    }
}
  • 数据
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
using System.Xml;
using Ext.Net;
 
namespace ExtNetTreePanleCustomControl
{
    public class Composer
    {
        public Composer(string name) { this.Name = name; }
        public string Name { get; set; }
 
        private List<Composition> compositions;
        public List<Composition> Compositions
        {
            get
            {
                if (this.compositions == null)
                {
                    this.compositions = new List<Composition>();
                }
                return this.compositions;
            }
        }
    }
 
    public class Composition
    {
        public Composition() { }
 
        public Composition(CompositionType type)
        {
            this.Type = type;
        }
 
        public CompositionType Type { get; set; }
 
        private List<Piece> pieces;
        public List<Piece> Pieces
        {
            get
            {
                if (this.pieces == null)
                {
                    this.pieces = new List<Piece>();
                }
                return this.pieces;
            }
        }
    }
 
    public class Piece
    {
        public Piece() { }
 
        public Piece(string title)
        {
            this.Title = title;
        }
 
        public string Title { get; set; }
    }
 
    public enum CompositionType
    {
        Concertos,
        Quartets,
        Sonatas,
        Symphonies
    }
 
    public class MyData
    {
        public static List<Composer> GetData()
        {
            Composer beethoven = new Composer("Beethoven");
 
            Composition beethovenConcertos = new Composition(CompositionType.Concertos);
            Composition beethovenQuartets = new Composition(CompositionType.Quartets);
            Composition beethovenSonatas = new Composition(CompositionType.Sonatas);
            Composition beethovenSymphonies = new Composition(CompositionType.Symphonies);
 
            beethovenConcertos.Pieces.AddRange(new List<Piece> { 
            new Piece{ Title = "No. 1 - C" },
            new Piece{ Title = "No. 2 - B-Flat Major" },
            new Piece{ Title = "No. 3 - C Minor" },
            new Piece{ Title = "No. 4 - G Major" },
            new Piece{ Title = "No. 5 - E-Flat Major" }
        });
 
            beethovenQuartets.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Six String Quartets" },
            new Piece{ Title = "Three String Quartets" },
            new Piece{ Title = "Grosse Fugue for String Quartets" }
        });
 
            beethovenSonatas.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Sonata in A Minor" },
            new Piece{ Title = "sonata in F Major" }
        });
 
            beethovenSymphonies.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "No. 1 - C Major" },
            new Piece{ Title = "No. 2 - D Major" },
            new Piece{ Title = "No. 3 - E-Flat Major" },
            new Piece{ Title = "No. 4 - B-Flat Major" },
            new Piece{ Title = "No. 5 - C Minor" },
            new Piece{ Title = "No. 6 - F Major" },
            new Piece{ Title = "No. 7 - A Major" },
            new Piece{ Title = "No. 8 - F Major" },
            new Piece{ Title = "No. 9 - D Minor" }
        });
 
            beethoven.Compositions.AddRange(new List<Composition>{
            beethovenConcertos, 
            beethovenQuartets,
            beethovenSonatas,
            beethovenSymphonies 
        });
 
 
            Composer brahms = new Composer("Brahms");
 
            Composition brahmsConcertos = new Composition(CompositionType.Concertos);
            Composition brahmsQuartets = new Composition(CompositionType.Quartets);
            Composition brahmsSonatas = new Composition(CompositionType.Sonatas);
            Composition brahmsSymphonies = new Composition(CompositionType.Symphonies);
 
            brahmsConcertos.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Violin Concerto" },
            new Piece{ Title = "Double Concerto - A Minor" },
            new Piece{ Title = "Piano Concerto No. 1 - D Minor" },
            new Piece{ Title = "Piano Concerto No. 2 - B-Flat Major" }
        });
 
            brahmsQuartets.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Piano Quartet No. 1 - G Minor" },
            new Piece{ Title = "Piano Quartet No. 2 - A Major" },
            new Piece{ Title = "Piano Quartet No. 3 - C Minor" },
            new Piece{ Title = "Piano Quartet No. 3 - B-Flat Minor" }
        });
 
            brahmsSonatas.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Two Sonatas for Clarinet - F Minor" },
            new Piece{ Title = "Two Sonatas for Clarinet - E-Flat Major" }
        });
 
            brahmsSymphonies.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "No. 1 - C Minor" },
            new Piece{ Title = "No. 2 - D Minor" },
            new Piece{ Title = "No. 3 - F Major" },
            new Piece{ Title = "No. 4 - E Minor" }
        });
 
            brahms.Compositions.AddRange(new List<Composition>{
            brahmsConcertos, 
            brahmsQuartets,
            brahmsSonatas,
            brahmsSymphonies 
        });
 
 
            Composer mozart = new Composer("Mozart");
 
            Composition mozartConcertos = new Composition(CompositionType.Concertos);
 
            mozartConcertos.Pieces.AddRange(new List<Piece> {
            new Piece{ Title = "Piano Concerto No. 12" },
            new Piece{ Title = "Piano Concerto No. 17" },
            new Piece{ Title = "Clarinet Concerto" },
            new Piece{ Title = "Violin Concerto No. 5" },
            new Piece{ Title = "Violin Concerto No. 4" }
        });
 
            mozart.Compositions.Add(mozartConcertos);
 
            return new List<Composer> { beethoven, brahms, mozart };
        }
    }
}
  • 外部脚本文件 myTreePanel.js
Ext.ns("MyCustomControl");
 
// ------------------MyTreePanel----------------------------------
MyCustomControl.MyTreePanel = {
    tr: null,
    init: function(tree) {
        this.tr = tree;
    },
    SelectParentChildNodes: function(node, state) {
        var tree = node.getOwnerTree();
        tree.suspendEvents();
        if (node.parentNode != null) {
            // 勾选该节点所有子节点
            node.cascade(function(node) {
                node.attributes.checked = state;
                node.ui.toggleCheck(state);
                return true;
            });
            // 勾选该节点父节点
            var pNode = node.parentNode;
            while (pNode != null) {
                if (state) { //如果选中状态无所谓
                    pNode.ui.toggleCheck(state);
                    pNode.attributes.checked = state;
                    pNode = pNode.parentNode;
                }
                else { //如果未选中状态,需要查看该节点是否所有子节点都未选中
                    var chk = false;
                    pNode.eachChild(function(child) {
                        if (child.attributes.checked || child.getUI().isChecked())
                            chk = true;
                    });
                    pNode.ui.toggleCheck(chk);
                    pNode.attributes.checked = chk;
                    pNode = pNode.parentNode;
                }
            }
        }
        tree.resumeEvents();
    },
    getSelectedNode: function(tree) {
        var msg = [];
        var selNodes = tree.getChecked();
 
        Ext.each(selNodes, function(node) {
            msg.push(node.text);
        });
 
        Ext.Msg.show({
            title: "勾选节点",
            msg: msg.join(','),
            icon: Ext.Msg.INFO,
            minWidth: 200,
            buttons: Ext.Msg.OK
        });
    },
    clearSelectedNode: function(tree) {
        tree.clearChecked();
    }
};

运行结果

未命名

未命名

说明

有几点值得注意:

  1. 标记 <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />Mode="ScriptFiles" 是为了 MyTreePanel 呈现后,保证页面在正确的时机加载,并执行 MyCustomControl.MyTreePanel.init 方法,初始化其 tr 属性,以便在客户端操作该控件;
  2. public const string SCOPE = "MyCustomControl.MyTreePanel"; 语句,配合脚本的 Ext.ns("MyCustomControl"); 以及 MyCustomControl.MyTreePanel = { … },在脚本创建命名空间和 MyTreePanel 类。
  3. 这种方式在很大程度上提高页面性能,便于日后维护该控件。

下载 Demo

原文地址:https://www.cnblogs.com/liuning8023/p/2299990.html