posts - 615, comments - 10484, trackbacks - 594, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

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

Posted on 2007-02-01 22:19 webabcd 阅读(...) 评论(...) 编辑 收藏
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.onmousedown 
= 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('onload', 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
{
}

2、新建一个ContextMenu实体类,有六个属性
using System;
using System.Collections.Generic;
using System.Text;

using System.ComponentModel;
using System.Web.UI;

namespace YYControls.SmartGridView
{
    
/// <summary>
    
/// ContextMenu 的摘要说明。
    
/// </summary>

    [ToolboxItem(false)]
    
public class ContextMenu
    
{
        
private string _icon;
        
/// <summary>
        
/// 文字左边的图标的链接
        
/// </summary>

        public string Icon
        
{
            
get return _icon; }
            
set { _icon = value; }
        }


        
private string _text;
        
/// <summary>
        
/// 菜单的文字
        
/// </summary>

        public string Text
        
{
            
get return _text; }
            
set { _text = value; }
        }

       
        
private string _commandButtonId;
        
/// <summary>
        
/// 所调用的命令按钮的ID
        
/// </summary>

        public string CommandButtonId
        
{
            
get return _commandButtonId; }
            
set { _commandButtonId = value; }
        }


        
private string _navigateUrl;
        
/// <summary>
        
/// 链接的url
        
/// </summary>

        public string NavigateUrl
        
{
            
get return _navigateUrl; }
            
set { _navigateUrl = value; }
        }


        
private ItemTypeCollection _itemType;
        
/// <summary>
        
/// 右键菜单的项的类别
        
/// </summary>

        public ItemTypeCollection ItemType
        
{
            
get return _itemType; }
            
set { _itemType = value; }
        }


        
private TargetCollection _target;
        
/// <summary>
        
/// 链接的target
        
/// </summary>

        public TargetCollection Target
        
{
            
get return _target; }
            
set { _target = value; }
        }



        
/// <summary>
        
/// 右键菜单的项的类别
        
/// </summary>

        public enum ItemTypeCollection
        
{
            
/// <summary>
            
/// 链接
            
/// </summary>

            Link,
            
/// <summary>
            
/// 按钮
            
/// </summary>

            Command,
            
/// <summary>
            
/// 分隔线
            
/// </summary>

            Separator
        }


        
/// <summary>
        
/// 链接的target
        
/// </summary>

        public enum TargetCollection
        
{
            
/// <summary>
            
/// 新开窗口
            
/// </summary>

            Blank,
            
/// <summary>
            
/// 当前窗口
            
/// </summary>

            Self,
            
/// <summary>
            
/// 跳出框架
            
/// </summary>

            Top
        }

    }

}


3、新建一个继承自CollectionBase的类ContextMenus
using System.Collections;
using System.ComponentModel;
using System.Web.UI;

namespace YYControls.SmartGridView
{
    
/// <summary>
    
/// ContextMenus 的摘要说明。
    
/// 注意要继承自CollectionBase
    
/// </summary>

    [
    ToolboxItem(
false),
    ParseChildren(
true)
    ]
    
public class ContextMenus : CollectionBase
    
{
        
/// <summary>
        
/// 构造函数
        
/// </summary>

        public ContextMenus()
            : 
base()
        
{
        }


        
/// <summary>
        
/// 实现IList接口
        
/// 获取或设置指定索引处的元素。
        
/// </summary>
        
/// <param name="index">要获得或设置的元素从零开始的索引</param>
        
/// <returns></returns>

        public ContextMenu this[int index]
        
{
            
get
            
{
                
return (ContextMenu)base.List[index];
            }

            
set
            
{
                
base.List[index] = (ContextMenu)value;
            }

        }


        
/// <summary>
        
/// 实现IList接口
        
/// 将某项添加到 System.Collections.IList 中。
        
/// </summary>
        
/// <param name="item">要添加到 System.Collections.IList 的 System.Object。</param>

        public void Add(ContextMenu item)
        
{
            
base.List.Add(item);
        }


        
/// <summary>
        
/// 实现IList接口
        
/// 从 System.Collections.IList 中移除特定对象的第一个匹配项。
        
/// </summary>
        
/// <param name="index">要从 System.Collections.IList 移除的 System.Object</param>

        public void Remove(int index)
        
{
            
if (index > -1 && index < base.Count)
            
{
                
base.List.RemoveAt(index);
            }

        }


        
/// <summary>
        
/// ToString()
        
/// </summary>
        
/// <returns></returns>

        public override string ToString()
        
{
            
return "ContextMenus";
        }

    }

}


4、在继承自GridView的类中加一个复杂对象属性,该复杂对象就是第3步创建的那个ContextMenus
        private ContextMenus _contextMenus;
        
/// <summary>
        
/// 行的右键菜单集合
        
/// </summary>

        [
        PersistenceMode(PersistenceMode.InnerProperty),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        Description(
"行的右键菜单"),
        Category(
"扩展")
        ]
        
public virtual ContextMenus ContextMenus
        
{
            
get
            
{
                
if (_contextMenus == null)
                
{
                    _contextMenus 
= new ContextMenus();
                }

                
return _contextMenus;
            }

        }

5、新建一个JavaScriptConstant类,把我们要用到的javascript存在一个常量里
using System;
using System.Collections.Generic;
using System.Text;

namespace YYControls.SmartGridView
{
    
/// <summary>
    
/// javascript
    
/// </summary>

    public class JavaScriptConstant
    
{
        
internal const string jsContextMenu = @"<script type=""text/javascript"">
        //<![CDATA[
        // 数据行的ClientId
        var _rowClientId = '';

        // 以下实现右键菜单,网上找的,不知道原创是谁
        function contextMenu()
        {
            this.items = new Array();
            this.addItem = function (item)
            {
                this.items[this.items.length] = item;
            }

            this.show = function (oDoc)
            {
                var strShow = '';
                var i;

                // 加上word-break: keep-all; 防止菜单项换行
                strShow = ""<div id='rightmenu' style='word-break: keep-all;BACKGROUND-COLOR: #ffffff; BORDER: #000000 1px solid; LEFT: 0px; POSITION: absolute; TOP: 0px; VISIBILITY: hidden; Z-INDEX: 10'>"";
                strShow += ""<table border='0' height='"";
                strShow += this.items.length * 20;
                strShow += ""' cellpadding='0' cellspacing='0'>"";
                strShow += ""<tr height='3'><td bgcolor='#d0d0ce' width='2'></td><td>"";
                strShow += ""<table border='0' width='100%' height='100%' cellpadding=0 cellspacing=0 bgcolor='#ffffff'>"";
                strShow += ""<tr><td bgcolor='#d0d0ce' width='23'></td><td><img src=' ' height='1' border='0'></td></tr></table>"";
                strShow += ""</td><td width='2'></td></tr>"";
                strShow += ""<tr><td bgcolor='#d0d0ce'></td><td>"";
                strShow += ""<table border='0' width='100%' height='100%' cellpadding=3 cellspacing=0 bgcolor='#ffffff'>"";
                
                oDoc.write(strShow);

                for(i=0; i<this.items.length; i++)
                {
                    this.items[i].show(oDoc);
                }
                
                strShow = ""</table></td><td></td></tr>"";
                strShow += ""<tr height='3'><td bgcolor='#d0d0ce'></td><td>"";
                strShow += ""<table border='0' width='100%' height='100%' cellpadding=0 cellspacing=0 bgcolor='#ffffff'>"";
                strShow += ""<tr><td bgcolor='#d0d0ce' width='23'></td><td><img src=' ' height='1' border='0'></td></tr></table>"";
                strShow += ""</td><td></td></tr>"";
                strShow += ""</table></div>\n"";
                
                oDoc.write(strShow);
            }
        }

        function contextItem(text, icon, cmd, url, target, type)
        {
            this.text = text ? text : '';
            this.icon = icon ? icon : '';
            this.cmd = cmd ? cmd : '';
            this.url = url ? url : '';
            this.target =target ? target : '';
            this.type = type ? type : 'Link';


            this.show = function (oDoc)
            {
                var strShow = '';

                if(this.type == 'Link' || this.type == 'Command')
                {
                    strShow += ""<tr "";
                    strShow += ""onmouseover=\""changeStyle(this, 'on');\"" "";
                    strShow += ""onmouseout=\""changeStyle(this, 'out');\"" "";

                    if (this.type == 'Command')
                    {
                        // 右键菜单是按钮类型,调用所对应的按钮的click事件
                        strShow += ""onclick=\""document.getElementById("";
                        strShow += ""_rowClientId + "";
                        strShow += ""'_"";
                        strShow += this.cmd;
                        strShow += ""').click()"";
                    }
                    else
                    {
                        // 右键菜单是链接类型
                        if (this.target == 'Top') this.target = 'top';
                        if (this.target == 'Self') this.target = 'self';

                        if (this.target == 'top' || this.target == 'self')
                        {
                            strShow += ""onclick=\"""";
                            strShow += this.target;
                            strShow += "".location='"";
                            strShow += this.url;
                            strShow += ""'"";
                        }
                        else
                        {
                            strShow += ""onclick=\""window.open('"";
                            strShow += this.url;
                            strShow += ""')"";
                        }
                    }   
                    strShow += ""\"">"";
                    strShow += ""<td class='ltdexit' width='16'>"";

                    if (this.icon == '')
                    {
                        strShow += '&nbsp;';
                    }
                    else 
                    {
                        strShow += ""<img border='0' src='"";
                        strShow += this.icon;
                        strShow += ""' width='16' height='16' style='POSITION: relative'></img>"";
                    }

                    strShow += ""</td><td class='mtdexit'>"";
                    strShow += this.text;
                    strShow += ""</td><td class='rtdexit' width='5'>&nbsp;</td></tr>"";
                }
                // 右键菜单是分隔线
                else if (this.type == 'Separator')
                {
                    strShow += ""<tr><td class='ltdexit'>&nbsp;</td>"";
                    strShow += ""<td class='mtdexit' colspan='2'><hr color='#000000' size='1'></td></tr>"";
                }

                oDoc.write(strShow);
            }
        }

        function changeStyle(obj, cmd)
        { 
            if(obj)
            {
                try 
                {
                    var imgObj = obj.children(0).children(0);

                    if(cmd == 'on') 
                    {
                        obj.children(0).className = 'ltdfocus';
                        obj.children(1).className = 'mtdfocus';
                        obj.children(2).className = 'rtdfocus';
                        
                        if(imgObj)
                        {
                            if(imgObj.tagName.toUpperCase() == 'IMG')
                            {
                                imgObj.style.left = '-1px';
                                imgObj.style.top = '-1px';
                            }
                        }
                    }
                    else if(cmd == 'out') 
                    {
                        obj.children(0).className = 'ltdexit';
                        obj.children(1).className = 'mtdexit';
                        obj.children(2).className = 'rtdexit';

                        if(imgObj)
                        {
                            if(imgObj.tagName.toUpperCase() == 'IMG')
                            {
                                imgObj.style.left = '0px';
                                imgObj.style.top = '0px';
                            }
                        }
                    }
                }
                catch (e) {}
            }
        }

        function showMenu(rowClientId)
        {
            _rowClientId = rowClientId;

            var x, y, w, h, ox, oy;

            x = event.clientX;
            y = event.clientY;

            var obj = document.getElementById('rightmenu');

            if (obj == null)
                return true;

            ox = document.body.clientWidth;
            oy = document.body.clientHeight;

            if(x > ox || y > oy)
                return false;

            w = obj.offsetWidth;
            h = obj.offsetHeight;

            if((x + w) > ox)
                x = x - w;

            if((y + h) > oy)
                y = y - h;

         
            // obj.style.posLeft = x + document.body.scrollLeft;
            // obj.style.posTop = y + document.body.scrollTop;
            // xhtml不支持上面的了
            // 就是说如果你的页头声明了页是xhtml的话就不能用上面那句了,vs2005创建的aspx会默认加上xhtml声明
            // 此时应该用如下的方法
            obj.style.posLeft = x + document.documentElement.scrollLeft;
            obj.style.posTop = y + document.documentElement.scrollTop;

            obj.style.visibility = 'visible';

            return false;
        }

        function hideMenu()
        {
            if(event.button == 0)
            {
                var obj = document.getElementById('rightmenu');
                if (obj == null)
                    return true;

                obj.style.visibility = 'hidden';
                obj.style.posLeft = 0;
                obj.style.posTop = 0;
            }
        }

        function writeStyle()
        {
            var strStyle = '';

            strStyle += ""<STYLE type='text/css'>"";
            strStyle += ""TABLE {Font-FAMILY: 'Tahoma','Verdana','宋体'; FONT-SIZE: 9pt}"";
            strStyle += "".mtdfocus {BACKGROUND-COLOR: #ccccff; BORDER-BOTTOM: #000000 1px solid; BORDER-TOP: #000000 1px solid; CURSOR: hand}"";
            strStyle += "".mtdexit {BACKGROUND-COLOR: #ffffff; BORDER-BOTTOM: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid}"";
            strStyle += "".ltdfocus {BACKGROUND-COLOR: #ccccff; BORDER-BOTTOM: #000000 1px solid; BORDER-TOP: #000000 1px solid; BORDER-LEFT: #000000 1px solid; CURSOR: hand}"";
            strStyle += "".ltdexit {BACKGROUND-COLOR: #d0d0ce; BORDER-BOTTOM: #d0d0ce 1px solid; BORDER-TOP: #d0d0ce 1px solid; BORDER-LEFT: #d0d0ce 1px solid}"";
            strStyle += "".rtdfocus {BACKGROUND-COLOR: #ccccff; BORDER-BOTTOM: #000000 1px solid; BORDER-TOP: #000000 1px solid; BORDER-RIGHT: #000000 1px solid; CURSOR: hand}"";
            strStyle += "".rtdexit {BACKGROUND-COLOR: #ffffff; BORDER-BOTTOM: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid; BORDER-RIGHT: #ffffff 1px solid}"";
            strStyle += ""</STYLE>"";

            document.write(strStyle);
        }

        function makeMenu()
        {
            var myMenu, item;

            myMenu = new contextMenu();

            // 增加右键菜单项 开始
            // item = new contextItem("", "", "", "", "", "");
            // 1-菜单项的文本
            // 2-图标链接
            // 3-所调用的命令按钮的ID
            // 4-链接地址
            // 5-链接的target
            // 6-右键菜单的项的类别
            // myMenu.addItem(item);

            [$MakeMenu$]
            // 增加右键菜单项 结束

            myMenu.show(this.document);

            delete item;

            delete myMenu;
        }

        function toggleMenu(isEnable)
        {
            if(isEnable)
                document.oncontextmenu = showMenu;
            else
                document.oncontextmenu = new function() {return true;};
        }

        writeStyle();

        makeMenu();

        document.onclick = hideMenu;
        //]]>
        </script>
";
    }

}


6、重写OnPreRender方法,注册上面那段客户端脚本
        /// <summary>
        
/// OnPreRender
        
/// </summary>
        
/// <param name="e"></param>

        protected override void OnPreRender(EventArgs e)
        
{
            
if (ContextMenus.Count > 0)
            
{
                StringBuilder sb 
= new StringBuilder();
                
foreach (ContextMenu cm in ContextMenus)
                
{
                    
// item = new contextItem("", "", "", "", "", "");
                    
// 1-菜单项的文本
                    
// 2-图标链接
                    
// 3-所调用的命令按钮的ID
                    
// 4-链接地址
                    
// 5-链接的target
                    
// 6-右键菜单的项的类别

                    
// 命令按钮
                    if (cm.ItemType == ContextMenu.ItemTypeCollection.Command)
                    
{
                        sb.Append(
"item = new contextItem(\"" + cm.Text +
                            "\", \"" + ResolveUrl(cm.Icon) + "\", \"" +
                            cm.CommandButtonId 
+ "\", \"\", \"\", \"Command\");");
                    }

                    
// 链接
                    else if (cm.ItemType == ContextMenu.ItemTypeCollection.Link)
                    
{
                        sb.Append(
"item = new contextItem(\"" + cm.Text +
                            "\", \"" + ResolveUrl(cm.Icon) + "\", \"\", \"" +
                            cm.NavigateUrl 
+ "\", \"" +
                            cm.Target 
+ "\", \"Link\");");
                    }

                    
// 分隔线
                    else if (cm.ItemType == ContextMenu.ItemTypeCollection.Separator)
                    
{
                        sb.Append(
"item = new contextItem(\"\", \"\", \"\", \"\", \"\", \"Separator\");");
                    }


                    sb.Append(
"myMenu.addItem(item);");
                }


                
// 注册客户端代码
                if (!Page.ClientScript.IsClientScriptBlockRegistered("jsContextMenu"))
                
{
                    Page.ClientScript.RegisterClientScriptBlock(
                        
this.GetType(),
                        
"jsContextMenu", JavaScriptConstant.jsContextMenu.Replace("[$MakeMenu$]", sb.ToString())
                        );
                }

            }


            
base.OnPreRender(e);
        }


7、重写OnRowDataBound给数据行增加客户端代码以调用我们注册的那段javascript,从而实现给GridView的数据行增加右键菜单的功能。
        /// <summary>
        
/// OnRowDataBound
        
/// </summary>
        
/// <param name="e"></param>

        protected override void OnRowDataBound(GridViewRowEventArgs e)
        
{
            
if (e.Row.RowType == DataControlRowType.DataRow)
            
{
                
if (ContextMenus.Count > 0)
                
{
                    
// 给数据行增加客户端代码
                    e.Row.Attributes.Add("oncontextmenu""showMenu('" + e.Row.ClientID + "');return false;");
                }

            }


            
base.OnRowDataBound(e);
        }



控件使用
添加这个控件到工具箱里,然后拖拽到webform上,设置如下属性:ItemType为右键菜单的项的类别(Link,Command,Separator);Icon为文字左边的图标的链接;Text为菜单的文字;CommandButtonId为所调用的命令按钮的ID;NavigateUrl为链接的url;Target为链接的target(Blank,Self,Top)
ObjData.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using System.ComponentModel;

/// <summary>
/// OjbData 的摘要说明
/// </summary>

public class OjbData
{
    
public OjbData()
    
{
        
//
        
// TODO: 在此处添加构造函数逻辑
        
//
    }


    [DataObjectMethod(DataObjectMethodType.Select, 
true)]
    
public DataTable Select()
    
{
        DataTable dt 
= new DataTable();
        dt.Columns.Add(
"no"typeof(string));
        dt.Columns.Add(
"name"typeof(string));

        
for (int i = 0; i < 30; i++)
        
{
            DataRow dr 
= dt.NewRow();
            dr[
0= "no" + i.ToString().PadLeft(2'0');
            dr[
1= "name" + i.ToString().PadLeft(2'0');

            dt.Rows.Add(dr);
        }


        
return dt;
    }

}


Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!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>SmartGridView测试</title>
</head>
<body>
    
<form id="form1" runat="server">
        
<div>
            
<yyc:SmartGridView ID="SmartGridView1" runat="server" DataSourceID="ObjectDataSource1"
                AutoGenerateColumns
="false">
                
<Columns>
                    
<asp:BoundField DataField="no" HeaderText="序号" SortExpression="no" ItemStyle-Width="100px" />
                    
<asp:BoundField DataField="name" HeaderText="名称" SortExpression="name" ItemStyle-Width="100px" />
                    
<asp:BoundField DataField="no" HeaderText="序号" SortExpression="no" ItemStyle-Width="100px" />
                    
<asp:BoundField DataField="name" HeaderText="名称" SortExpression="name" ItemStyle-Width="100px" />
                    
<asp:BoundField DataField="no" HeaderText="序号" SortExpression="no" ItemStyle-Width="100px" />
                    
<asp:BoundField DataField="name" HeaderText="名称" SortExpression="name" ItemStyle-Width="100px" />
                    
<asp:TemplateField>
                        
<footerstyle cssclass="hidden" />
                        
<headerstyle cssclass="hidden" />
                        
<itemstyle cssclass="hidden" />
                        
<itemtemplate>
                    
<asp:Button id="btnRightMenuButton" runat="server" CommandName="RightMenuButton" CommandArgument='<%# Container.DataItemIndex %>' />
                
</itemtemplate>
                    
</asp:TemplateField>
                
</Columns>
                
<ContextMenus>
                    
<yyc:ContextMenu ItemType="Command" Text="右键菜单按钮测试" Icon="~/Images/button.gif" CommandButtonId="btnRightMenuButton" />
                    
<yyc:ContextMenu ItemType="Separator" />
                    
<yyc:ContextMenu ItemType="Link" Text="控件源代码" Icon="~/Images/button.gif" NavigateUrl="http://webabcd.cnblogs.com"
                        Target
="Blank" />
                
</ContextMenus>
            
</yyc:SmartGridView>
            
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="Select"
                TypeName
="OjbData"></asp:ObjectDataSource>
        
</div>
    
</form>
</body>
</html>

注:如果想修改右键菜单的样式,请自行修改javascript,我就不把他们弄出来了。

/*测试版的实现 结束*/


OK
[源码下载]