GridView既强大又好用。为了让它更强大、更好用,我们来写一个继承自GridView的控件。
[索引页]
[×××]


扩展GridView控件(9) - 给数据行增加右键菜单


作者:webabcd
/*正式版的实现 开始*/
 
介绍
扩展GridView控件:
给数据行增加右键菜单,响应服务端事件或超级链接

使用方法(设置ContextMenus集合属性): 
Text - 菜单的文本内容
BoundCommandName - 需要绑定的CommandName
NavigateUrl - 链接的URL
Target - 链接的目标窗口或框架
SmartGridView的属性ContextMenuCssClass - 右键菜单的级联样式表 CSS 类名(右键菜单的结构div ul li a)


关键代码
js
/*右键菜单 开始*/
yy_sgv_rightMenu = function ()
{
/// <summary>构造函数</summary>

        this._menu = null;
        this._menuItem = null;
        this._handle = null;
        this._target = null;
        this._cssClass = null
};

yy_sgv_rightMenu.prototype =    
{
/// <summary>相关属性和相关方法</summary>

        get_menu: function()    
        {
                return this._menu;
        },
        set_menu: function(value)
        {
                this._menu = value;
        },
        
        get_handle: function()    
        {
                return this._handle;
        },
        set_handle: function(value)
        {
                this._handle = value;
        },
        
        get_target: function()    
        {
                return this._target;
        },
        set_target: function(value)
        {
                this._target = value;
        },
        
        get_cssClass: function()    
        {
                return this._cssClass;
        },
        set_cssClass: function(value)
        {
                this._cssClass = value;
        },
        
        get_menuItem: function()    
        {
                return this._menuItem;
        },
        set_menuItem: function(value)
        {
                this._menuItem = value;
        },


        show:function(e)
        {
                if (this.get_menuItem() == null)    
                {
                        this.hidden();
                        return true;
                }
        
                var rightMenu = this.get_menu();
                if (rightMenu == null)
                {
                        rightMenu = document.createElement("div");
                }

                var menuInnerHTML = ""; // 菜单容器里的HTML内容
                var $items = this.get_menuItem();
                var $handle = this.get_handle();
                var $target = this.get_target();
                
                rightMenu.className = "yy_sgv_rightMenuBase";    
                if (this.get_cssClass() == null || this.get_cssClass() == "")
                        rightMenu.className += " " + "yy_sgv_rightMenu";
                else
                        rightMenu.className += " " + this.get_cssClass();                        
                
                menuInnerHTML += "<ul>";
                
                for (var i in $items)
                {
                        if ($items[i].indexOf("<hr") != -1)
                        {
                                menuInnerHTML += $items[i];
                        }
                        else
                        {
                                if ($target[i] == "")
                                {
                                        $target[i] = "_self";
                                }
                        
                                menuInnerHTML += "<li><a href=\"" + $handle[i] + "\" target=\"" + $target[i] + "\">";
                                menuInnerHTML += $items[i];
                                menuInnerHTML += "</a></li>";        
                        }
                }
                
                menuInnerHTML += "</ul>";
                // alert(menuInnerHTML);
                
                rightMenu.innerHTML = menuInnerHTML;
                
                rightMenu.style.visibility = "visible";
                
                rightMenu. = function(e)
                {
                        e=e||window.event;
                        document.all ? e.cancelBubble = true : e.stopPropagation();
                }
                
                rightMenu.onselectstart = function()
                {
                        return false;
                }
                
                document.body.appendChild(rightMenu);
                this.set_menu(rightMenu); // 方便别的方法引用

                e = e || window.event;
                
                var root = document.documentElement;
                var x = root.scrollLeft + e.clientX;    
                var y = root.scrollTop + e.clientY;
                
                if (this.get_menu().clientWidth+e.clientX > root.clientWidth)
                {
                        x = x - this.get_menu().clientWidth;
                }
                if (this.get_menu().clientHeight+e.clientY > root.clientHeight)
                {
                        y = y - this.get_menu().clientHeight;
                }
                
                this.get_menu().style.left = x + "px";    
                this.get_menu().style.top = y + "px";    
                this.get_menu().style.visibility = "visible";
                
                this.set_handle(null);
                this.set_menuItem(null);
                this.set_target(null);
                
                return false;
        },

        hidden:function()    
        {
                if (this.get_menu() != null)
                {
                        this.get_menu().style.visibility = "hidden";
                }
        }
}

if (document.all)
{
        window.attachEvent(' yy_sgv_rightMenu = new yy_sgv_rightMenu())
}
else
{
        window.addEventListener('load', yy_sgv_rightMenu = new yy_sgv_rightMenu(), false);
}

function yy_sgv_setRightMenu(handle, menuItem, target, cssClass)
{
/// <summary>设置需要显示的右键菜单</summary>

        yy_sgv_rightMenu.set_handle(handle);
        yy_sgv_rightMenu.set_menuItem(menuItem);
        yy_sgv_rightMenu.set_target(target);
        yy_sgv_rightMenu.set_cssClass(cssClass);
}
/*右键菜单 结束*/
 
css
/*右键菜单必须要具有的样式*/
.yy_sgv_rightMenuBase
{
        visibility
: hidden;
        position
: absolute;
}
/*右键菜单的示例样式 开始*/
.yy_sgv_rightMenu
{
        border-right
: 2px outset;
        border-top
: 2px outset;
        border-left
: 2px outset;
        border-bottom
: 2px outset;
        background-color
: buttonface;
}
.yy_sgv_rightMenu hr
{
        width
: 300px;
}
.yy_sgv_rightMenu ul
{
        list-style
: none; margin:0; padding:0;
}
.yy_sgv_rightMenu ul li
{
        vertical-align
: bottom;
}
.yy_sgv_rightMenu A
{ color: MenuText; text-decoration: none; display: block; width: 300px; text-align:center; line-height:20px }    
.yy_sgv_rightMenu A:link
{ color: MenuText; text-decoration: none; }    
.yy_sgv_rightMenu A:active
{ color: MenuText; text-decoration: none; }    
.yy_sgv_rightMenu A:visited
{ color: MenuText; text-decoration: none; }    
.yy_sgv_rightMenu A:hover
{ color: HighlightText; background-color: Highlight; }
/*右键菜单的示例样式 结束*/
 
c#
using System;
using System.Collections.Generic;
using System.Text;

using System.Web.UI.WebControls;
using System.Web.UI;

namespace YYControls.SmartGridViewFunction
{
        /// <summary>
        /// 扩展功能:给数据行增加右键菜单
        /// </summary>
        public class ContextMenuFunction : ExtendFunction
        {
                List<string> _rowRightClickButtonUniqueIdList = new List<string>();

                private string _menuItem;
                private string _target;

                /// <summary>
                /// 构造函数
                /// </summary>
                public ContextMenuFunction()
                        : base()
                {

                }

                /// <summary>
                /// 构造函数
                /// </summary>
                /// <param name="sgv">SmartGridView对象</param>
                public ContextMenuFunction(SmartGridView sgv)
                        : base(sgv)
                {

                }

                /// <summary>
                /// 扩展功能的实现
                /// </summary>
                protected override void Execute()
                {
                        this._sgv.RowDataBoundDataRow += new SmartGridView.RowDataBoundDataRowHandler(_sgv_RowDataBoundDataRow);
                        this._sgv.PreRender += new EventHandler(_sgv_PreRender);
                        this._sgv.RenderBegin += new SmartGridView.RenderBeginHandler(_sgv_RenderBegin);

                        foreach (ContextMenu cm in this._sgv.ContextMenus)
                        {
                                string text = cm.Text == null ? "" : cm.Text;
                                string target = cm.Target == null ? "" : cm.Target;

                                this._menuItem += String.Format(",\"{0}\"", text.Replace(",", ","));
                                this._target += String.Format(",\"{0}\"", target.Replace(",", ","));
                        }

                        this._menuItem = String.Format("new Array({0})", this._menuItem.TrimStart(','));
                        this._target = String.Format("new Array({0})", this._target.TrimStart(','));
                }

                void _sgv_RowDataBoundDataRow(object sender, GridViewRowEventArgs e)
                {
                        string handle = "";

                        // 从用户定义的ContextMenus集合中分解出ContextMenu
                        foreach (ContextMenu cm in this._sgv.ContextMenus)
                        {
                                if (!String.IsNullOrEmpty(cm.NavigateUrl))
                                {
                                        handle += String.Format(",\"{0}\"", cm.NavigateUrl);
                                        continue;
                                }
                                else if (String.IsNullOrEmpty(cm.BoundCommandName))
                                {
                                        handle += String.Format(",\"{0}\"", "#");
                                        continue;
                                }

                                foreach (TableCell tc in e.Row.Cells)
                                {
                                        bool bln = false;

                                        foreach (Control c in tc.Controls)
                                        {
                                                // 如果控件继承自接口IButtonControl
                                                if (c is IButtonControl
                                                        && ((IButtonControl)c).CommandName == cm.BoundCommandName)
                                                {
                                                        handle += String.Format(",\"{0}\"", this._sgv.Page.ClientScript.GetPostBackClientHyperlink(c, ""));
                                                        _rowRightClickButtonUniqueIdList.Add(c.UniqueID);

                                                        bln = true;
                                                        break;
                                                }
                                        }

                                        if (bln)
                                        {
                                                break;
                                        }
                                }
                        }

                        handle = String.Format("new Array({0})", handle.TrimStart(','));

                        string oncontextmenuValue =
                                String.Format
                                (
                                        "yy_sgv_setRightMenu({0},{1}_rightMenuItem,{1}_rightMenuTarget, {2})",
                                        handle,
                                        this._sgv.ClientID,
                                        String.IsNullOrEmpty(this._sgv.ContextMenuCssClass) ? "null" : "'" + this._sgv.ContextMenuCssClass + "'"
                                );

                        // 设置按钮的客户端属性
                        YYControls.Helper.Common.SetAttribute(
                                e.Row,
                                "oncontextmenu",
                                oncontextmenuValue,
                                AttributeValuePosition.Last);
                }

                /// <summary>
                /// SmartGridView的PreRender事件
                /// </summary>
                /// <param name="sender"></param>
                /// <param name="e"></param>
                void _sgv_PreRender(object sender, EventArgs e)
                {
                        // 构造所需脚本
                        string scriptString = "";
                        scriptString += "document.oncontextmenu=function(evt){return yy_sgv_rightMenu.show(evt);};";
                        scriptString += "document.onclick=function(){yy_sgv_rightMenu.hidden();};";

                        // 注册所需脚本
                        if (!this._sgv.Page.ClientScript.IsClientScriptBlockRegistered("yy_sgv_rightMenu"))
                        {
                                this._sgv.Page.ClientScript.RegisterClientScriptBlock
                                (
                                        this._sgv.GetType(),
                                        "yy_sgv_rightMenu",
                                        scriptString,
                                        true
                                );
                        }

                        // 为每个SmartGridView注册与右键菜单相关的变量
                        if (!this._sgv.Page.ClientScript.IsClientScriptBlockRegistered(String.Format("yy_sgv_rightMenu_{0}", this._sgv.ClientID)))
                        {
                                this._sgv.Page.ClientScript.RegisterClientScriptBlock
                                (
                                        this._sgv.GetType(),
                                        String.Format("yy_sgv_rightMenu_{0}", this._sgv.ClientID),
                                        String.Format(
                                        "var {0}_rightMenuItem={1};var {0}_rightMenuTarget={2};",
                                                this._sgv.ClientID,
                                                this._menuItem,
                                                this._target),
                                        true
                                );
                        }

                }

                /// <summary>
                /// RenderBegin
                /// </summary>
                /// <param name="sender"></param>
                /// <param name="writer"></param>
                void _sgv_RenderBegin(object sender, HtmlTextWriter writer)
                {
                        foreach (string uniqueId in this._rowRightClickButtonUniqueIdList)
                        {
                                // 注册回发或回调数据以进行验证
                                this._sgv.Page.ClientScript.RegisterForEventValidation(uniqueId);
                        }
                }

        }
}
 
/*正式版的实现 结束*/
 
/*测试版的实现 开始*/
 
介绍
给GridView的数据行增加右键菜单可以增加用户体验,不过实现起来挺麻烦的,现在我们扩展一下GridView控件以实现这样的功能。


控件开发
1、新建一个继承自GridView的类。
/// <summary>
/// 继承自GridView
/// </summary>
[ToolboxData(@"<{0}:SmartGridView runat='server'></{0}:SmartGridView>")]
public class SmartGridView : GridView
{
}