当前位置: 移动技术网 > IT编程>开发语言>.net > 转(C# 类似右键菜单弹出窗体)

转(C# 类似右键菜单弹出窗体)

2018年12月13日  | 移动技术网IT编程  | 我要评论

超人力斯,邓朴方外逃,纽欣康左旋肉碱胶囊

文章来自 https://www.cnblogs.com/ahdung/p/floatlayerbase.html

每天进步一点点

新建类  floatlayerbase 继承form,

自己有点小改动public void show(control control, point endpoint) 添加参数 endpoint 避免窗体在最右边或下边时弹出窗体被遮掩。

 

public partial class floatlayerbase : form
    {
        /// <summary>
        /// 鼠标消息筛选器
        /// </summary>
        //由于本窗体为ws_child,所以不会收到在窗体以外点击鼠标的消息
        //该消息筛选器的作用就是让本窗体获知鼠标点击情况,进而根据鼠标是否在本窗体以外的区域点击,做出相应处理
        readonly appmousemessagehandler _mousemsgfilter;

        /// <summary>
        /// 指示本窗体是否已showdialog过
        /// </summary>
        //由于多次showdialog会使onload/onshown重入,故需设置此标记以供重入时判断
        bool _isshowdialogagain;

        //边框相关字段
        borderstyle _bordertype;
        border3dstyle _border3dstyle;
        buttonborderstyle _bordersinglestyle;
        color _bordercolor;

        /// <summary>
        /// 获取或设置边框类型
        /// </summary>
        [description("获取或设置边框类型。")]
        [defaultvalue(borderstyle.fixed3d)]
        public borderstyle bordertype
        {
            get { return _bordertype; }
            set
            {
                if (_bordertype == value) { return; }
                _bordertype = value;
                invalidate();
            }
        }

        /// <summary>
        /// 获取或设置三维边框样式
        /// </summary>
        [description("获取或设置三维边框样式。")]
        [defaultvalue(border3dstyle.raisedinner)]
        public border3dstyle border3dstyle
        {
            get { return _border3dstyle; }
            set
            {
                if (_border3dstyle == value) { return; }
                _border3dstyle = value;
                invalidate();
            }
        }

        /// <summary>
        /// 获取或设置线型边框样式
        /// </summary>
        [description("获取或设置线型边框样式。")]
        [defaultvalue(buttonborderstyle.solid)]
        public buttonborderstyle bordersinglestyle
        {
            get { return _bordersinglestyle; }
            set
            {
                if (_bordersinglestyle == value) { return; }
                _bordersinglestyle = value;
                invalidate();
            }
        }

        /// <summary>
        /// 获取或设置边框颜色(仅当边框类型为线型时有效)
        /// </summary>
        [description("获取或设置边框颜色(仅当边框类型为线型时有效)。")]
        [defaultvalue(typeof(color), "darkgray")]
        public color bordercolor
        {
            get { return _bordercolor; }
            set
            {
                if (_bordercolor == value) { return; }
                _bordercolor = value;
                invalidate();
            }
        }

        protected override sealed createparams createparams
        {
            get
            {
                createparams prms = base.createparams;

                //prms.style = 0;
                //prms.style |= -2147483648;   //ws_popup
                prms.style |= 0x40000000;      //ws_child  重要,只有child窗体才不会抢父窗体焦点
                prms.style |= 0x4000000;       //ws_clipsiblings
                prms.style |= 0x10000;         //ws_tabstop
                prms.style &= ~0x40000;        //ws_sizebox       去除
                prms.style &= ~0x800000;       //ws_border        去除
                prms.style &= ~0x400000;       //ws_dlgframe      去除
                //prms.style &= ~0x20000;      //ws_minimizebox   去除
                //prms.style &= ~0x10000;      //ws_maximizebox   去除

                prms.exstyle = 0;
                //prms.exstyle |= 0x1;         //ws_ex_dlgmodalframe 立体边框
                //prms.exstyle |= 0x8;         //ws_ex_topmost
                prms.exstyle |= 0x10000;       //ws_ex_controlparent
                //prms.exstyle |= 0x80;        //ws_ex_toolwindow
                //prms.exstyle |= 0x100;       //ws_ex_windowedge
                //prms.exstyle |= 0x8000000;   //ws_ex_noactivate
                //prms.exstyle |= 0x4;         //ws_ex_noparentnotify

                return prms;
            }
        }

        public floatlayerbase()
        {
            //初始化消息筛选器。添加和移除在显示/隐藏时负责
            _mousemsgfilter = new appmousemessagehandler(this);
            //initializecomponent();
            initbaseproperties();
            //初始化边框相关
            _bordertype = borderstyle.fixed3d;
            _border3dstyle = system.windows.forms.border3dstyle.raisedinner;
            _bordersinglestyle = buttonborderstyle.solid;
            _bordercolor = color.darkgray;
        }


        protected override void onload(eventargs e)
        {
            //防止重入
            if (_isshowdialogagain) { return; }

            //需得减掉两层边框宽度,运行时尺寸才与设计时完全相符,原因不明
            //确定与controlbox、formborderstyle有关,但具体联系不明
            if (!designmode)
            {
                size size = systeminformation.framebordersize;
                this.size -= size + size;//不可以用clientsize,后者会根据窗口风格重新调整size
            }
            base.onload(e);
        }

        protected override void onshown(eventargs e)
        {
            //防止重入
            if (_isshowdialogagain) { return; }

            //在onshown中为首次showdialog设标记
            if (modal) { _isshowdialogagain = true; }

            if (!designmode)
            {
                //激活首控件
                control firstcontrol;
                if ((firstcontrol = getnextcontrol(this, true)) != null)
                {
                    firstcontrol.focus();
                }
            }
            base.onshown(e);
        }

        protected override void wndproc(ref message m)
        {
            //当本窗体作为showdialog弹出时,在收到wm_showwindow前,owner会被disable
            //故需在收到该消息后立即enable它,不然owner窗体和本窗体都将处于无响应状态
            if (m.msg == 0x18 && m.wparam != intptr.zero && m.lparam == intptr.zero
                && modal && owner != null && !owner.isdisposed)
            {
                if (owner.ismdichild)
                {
                    //当owner是mdi子窗体时,被disable的是mdi主窗体
                    //并且parent也会指向mdi主窗体,故需改回为owner,这样弹出窗体的location才会相对于owner而非mdiparent
                    nativemethods.enablewindow(owner.mdiparent.handle, true);
                    nativemethods.setparent(this.handle, owner.handle);//只能用api设置parent,因为模式窗体是toplevel,.net拒绝为顶级窗体设置parent
                }
                else
                {
                    nativemethods.enablewindow(owner.handle, true);
                }
            }
            base.wndproc(ref m);
        }

        //画边框
        protected override void onpaintbackground(painteventargs e)
        {
            base.onpaintbackground(e);

            if (_bordertype == borderstyle.fixed3d)//绘制3d边框
            {
                controlpaint.drawborder3d(e.graphics, clientrectangle, border3dstyle);
            }
            else if (_bordertype == borderstyle.fixedsingle)//绘制线型边框
            {
                controlpaint.drawborder(e.graphics, clientrectangle, bordercolor, bordersinglestyle);
            }
        }

        //显示后添加鼠标消息筛选器以开始捕捉,隐藏时则移除筛选器。之所以不放dispose中是想尽早移除筛选器
        protected override void onvisiblechanged(eventargs e)
        {
            if (!designmode)
            {
                if (visible) { application.addmessagefilter(_mousemsgfilter); }
                else { application.removemessagefilter(_mousemsgfilter); }
            }
            base.onvisiblechanged(e);
        }

        //实现窗体客户区拖动
        //在wndproc中实现这个较麻烦,所以放到这里做
        protected override void onmousedown(mouseeventargs e)
        {
            //让鼠标点击客户区时达到与点击标题栏一样的效果,以此实现客户区拖动
            nativemethods.releasecapture();
            nativemethods.sendmessage(handle, 0xa1/*wm_nclbuttondown*/, (intptr)2/*caption*/, intptr.zero);

            base.onmousedown(e);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="control">显示在该控件下方</param>
        public dialogresult showdialog(control control, point endpoint)
        {
            return showdialog(control, 0, control.height, endpoint);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="control">触发弹出窗体的控件</param>
        /// <param name="offsetx">相对control水平偏移</param>
        /// <param name="offsety">相对control垂直偏移</param>
        public dialogresult showdialog(control control, int offsetx, int offsety, point endpoint)
        {
            return showdialog(control, new point(offsetx, offsety), endpoint);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="control">触发弹出窗体的控件</param>
        /// <param name="offset">相对control偏移</param>
        public dialogresult showdialog(control control, point offset, point endpoint)
        {
            return this.showdialoginternal(control, offset, endpoint);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="item">显示在该工具栏项的下方</param>
        public dialogresult showdialog(toolstripitem item, point endpoint)
        {
            return showdialog(item, 0, item.height, endpoint);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="item">触发弹出窗体的工具栏项</param>
        /// <param name="offsetx">相对item水平偏移</param>
        /// <param name="offsety">相对item垂直偏移</param>
        public dialogresult showdialog(toolstripitem item, int offsetx, int offsety, point endpoint)
        {
            return showdialog(item, new point(offsetx, offsety), endpoint);
        }

        /// <summary>
        /// 显示为模式窗体
        /// </summary>
        /// <param name="item">触发弹出窗体的工具栏项</param>
        /// <param name="offset">相对item偏移</param>
        public dialogresult showdialog(toolstripitem item, point offset, point endpoint)
        {
            return this.showdialoginternal(item, offset, endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="control">显示在该控件下方</param>
        public void show(control control, point endpoint)
        {
            show(control, 0, control.height,endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="control">触发弹出窗体的控件</param>
        /// <param name="offsetx">相对control水平偏移</param>
        /// <param name="offsety">相对control垂直偏移</param>
        public void show(control control, int offsetx, int offsety, point endpoint)
        {
            show(control, new point(offsetx, offsety), endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="control">触发弹出窗体的控件</param>
        /// <param name="offset">相对control偏移</param>
        public void show(control control, point offset, point endpoint)
        {
            this.showinternal(control, offset, endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="item">显示在该工具栏下方</param>
        public void show(toolstripitem item, point endpoint)
        {
            show(item, 0, item.height, endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="item">触发弹出窗体的工具栏项</param>
        /// <param name="offsetx">相对item水平偏移</param>
        /// <param name="offsety">相对item垂直偏移</param>
        public void show(toolstripitem item, int offsetx, int offsety, point endpoint)
        {
            show(item, new point(offsetx, offsety),endpoint);
        }

        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="item">触发弹出窗体的工具栏项</param>
        /// <param name="offset">相对item偏移</param>
        public void show(toolstripitem item, point offset, point endpoint)
        {
            this.showinternal(item, offset, endpoint);
        }

        /// <summary>
        /// showdialog内部方法
        /// </summary>
        private dialogresult showdialoginternal(component controloritem, point offset, point endpoint)
        {
            //快速连续弹出本窗体将有可能遇到尚未hide的情况下再次弹出,这会引发异常,故需做处理
            if (this.visible) { return system.windows.forms.dialogresult.none; }

            this.setlocationandowner(controloritem, offset, endpoint);
            return base.showdialog();
        }

        /// <summary>
        /// show内部方法
        /// </summary>
        private void showinternal(component controloritem, point offset, point endpoint)
        {
            if (this.visible) { return; }//原因见showdialoginternal

            this.setlocationandowner(controloritem, offset, endpoint);
            base.show();
        }

        /// <summary>
        /// 设置坐标及所有者
        /// </summary>
        /// <param name="controloritem">控件或工具栏项</param>
        /// <param name="offset">相对偏移</param>
        private void setlocationandowner(component controloritem, point offset, point endpoint)
        {
            point pt = point.empty;

            if (controloritem is toolstripitem)
            {
                toolstripitem item = (toolstripitem)controloritem;
                pt.offset(item.bounds.location);
                controloritem = item.owner;
            }

            control c = (control)controloritem;
            pt.offset(getcontrollocationinform(c));
            pt.offset(offset);
            if (pt.x + this.restorebounds.width > endpoint.x)//右边超出界面
            {
                pt.x = pt.x + c.width - this.restorebounds.width;
            }

            this.location = pt;

            //设置owner属性与show[dialog](owner)有不同,当owner是mdichild时,后者会改owner为mdiparent
            this.owner = c.findform();
        }

        /// <summary>
        /// 获取控件在窗体中的坐标
        /// </summary>
        private static point getcontrollocationinform(control c)
        {
            point pt = c.location;
            //control c1 = c.parent;
            while (!((c = c.parent) is form))
            {
                pt.offset(c.location);
            }
            return pt;
        }


        #region 屏蔽对本类影响重大的基类方法和属性

        /// <summary>
        /// 初始化部分基类属性
        /// </summary>
        private void initbaseproperties()
        {
            base.controlbox = false;                           //重要
            //必须得是sizabletoolwindow才能支持调整大小的同时,不受systeminformation.minwindowtracksize的限制
            base.formborderstyle = system.windows.forms.formborderstyle.sizabletoolwindow;
            base.text = string.empty;                          //重要
            base.helpbutton = false;
            base.icon = null;
            base.ismdicontainer = false;
            base.maximizebox = false;
            base.minimizebox = false;
            base.showicon = false;
            base.showintaskbar = false;
            base.startposition = formstartposition.manual;     //重要
            base.topmost = false;
            base.windowstate = formwindowstate.normal;
        }

        //屏蔽原方法
        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("请使用别的重载!", true)]
        public new dialogresult showdialog() { throw new notimplementedexception(); }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("请使用别的重载!", true)]
        public new dialogresult showdialog(iwin32window owner) { throw new notimplementedexception(); }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("请使用别的重载!", true)]
        public new void show() { throw new notimplementedexception(); }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("请使用别的重载!", true)]
        public new void show(iwin32window owner) { throw new notimplementedexception(); }

        //屏蔽原属性
        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool controlbox { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("设置边框请使用border相关属性!", true)]
        public new formborderstyle formborderstyle { get { return system.windows.forms.formborderstyle.sizabletoolwindow; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public override sealed string text { get { return string.empty; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool helpbutton { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new image icon { get { return null; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool ismdicontainer { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool maximizebox { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool minimizebox { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool showicon { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool showintaskbar { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new formstartposition startposition { get { return formstartposition.manual; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new bool topmost { get { return false; } set { } }

        [browsable(false), editorbrowsable(editorbrowsablestate.never)]
        [obsolete("禁用该属性!", true)]
        public new formwindowstate windowstate { get { return formwindowstate.normal; } set { } }

        #endregion

        /// <summary>
        /// 程序鼠标消息筛选器
        /// </summary>
        private class appmousemessagehandler : imessagefilter
        {
            readonly floatlayerbase _layerform;

            public appmousemessagehandler(floatlayerbase layerform)
            {
                _layerform = layerform;
            }

            public bool prefiltermessage(ref message m)
            {
                //如果在本窗体以外点击鼠标,隐藏本窗体
                //若想在点击标题栏、滚动条等非客户区也要让本窗体消失,取消0xa1的注释即可
                //本例是根据坐标判断,亦可以改为根据句柄,但要考虑子孙控件
                //之所以用api而不用form.desktopbounds是因为后者不可靠
                if ((m.msg == 0x201/*|| m.msg==0xa1*/)
                    && _layerform.visible && !nativemethods.getwindowrect(_layerform.handle).contains(mouseposition))
                {
                    _layerform.hide();//之所以不close是考虑应该由调用者负责销毁
                }

                return false;
            }
        }


        /// <summary>
        /// api封装类
        /// </summary>
        private static class nativemethods
        {
            [dllimport("user32.dll")]
            [return: marshalas(unmanagedtype.bool)]
            public static extern bool enablewindow(intptr hwnd, bool benable);

            [dllimport("user32.dll", charset = charset.auto)]
            public static extern intptr sendmessage(intptr hwnd, uint msg, intptr wparam, intptr lparam);

            [dllimport("user32.dll")]
            public static extern bool releasecapture();

            [dllimport("user32.dll", setlasterror = true)]
            public static extern intptr setparent(intptr hwndchild, intptr hwndnewparent);

            [dllimport("user32.dll", setlasterror = true)]
            private static extern bool getwindowrect(intptr hwnd, out rect lprect);

            [structlayout(layoutkind.sequential)]
            private struct rect
            {
                public int left;
                public int top;
                public int right;
                public int bottom;

                public static explicit operator rectangle(rect rect)
                {
                    return new rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
                }
            }

            public static rectangle getwindowrect(intptr hwnd)
            {
                rect rect;
                getwindowrect(hwnd, out rect);
                return (rectangle)rect;
            }

            //[dllimport("user32.dll", exactspelling = true)]
            //public static extern intptr getancestor(intptr hwnd, uint flags);
        }
    }

  

1  添加用户控件usercontrol1继承floatlayerbase

 

2 调用代码

 public floatlayerbase createlayer(type type, out bool isshow)
        {
            //考虑到初始化体验爽滑,不用反射
            floatlayerbase layer = null;
            if (type == typeof(usercontrol1)) { layer = new usercontrol1(); } }

            layerformoption opts = layeroption;
            isshow = opts.isshow;
            layer.bordertype = opts.bordertype;
            layer.border3dstyle = opts.border3dstyle;
            layer.bordersinglestyle = opts.bordersinglestyle;
            layer.bordercolor = opts.bordercolor;
            return layer;
        }

        public void popuplayer(type type, object sender)
        {
            control c = sender as control;
            toolstripitem item = sender as toolstripitem;
            point pt = new point(this.clientrectangle.width, this.clientrectangle.height);

            bool isshow;
            floatlayerbase p = createlayer(type, out isshow);
            if (isshow)
            {
                if (c != null) { p.show(c, pt); }
                else { p.show(item, pt); }
            }
            else
            {
                var result = c != null ? p.showdialog(c, pt) : p.showdialog(item, pt);
                //txbresult.appendtext(result + "\r\n");
            }
        }

private void button1_click(object sender, eventargs e)
{
    popuplayer(typeof(usercontrol1), sender);
}

  

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网