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


    文章来自 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 + "
    ");
                }
            }
    
    private void button1_Click(object sender, EventArgs e)
    {
        PopupLayer(typeof(UserControl1), sender);
    }
    

      

  • 相关阅读:
    第五周作业
    1.一维数组:选择排序法、二分查找法; 2.二维数据:定义、引用、初始化,二维数组与矩阵。
    基础作业 本周没上课,但是请大家不要忘记学习。 本周请大家完成上周挑战作业的第一部分:给定一个整数数组(包含正负数),找到一个具有最大和的子数组,返回其最大的子数组的和。 例如:[1, -2, 3, 10, -4, 7, 2, -5]的最大子数组为[3, 10, -4, 7, 2] 输入: 请建立以自己英文名字命名的txt文件,并输入数组元素数值,元素值之间用逗号分隔。 输出 在不删除原有文件内容
    2019年春季学期第二周作业 基础作业 请在第一周作业的基础上,继续完成:找出给定的文件中数组的最大值及其对应的最小下标(下标从0开始)。并将最大值和对应的最小下标数值写入文件。 输入: 请建立以自己英文名字命名的txt文件,并输入数组元素数值,元素值之间用逗号分隔。 输出 在不删除原有文件内容的情况下,将最大值和对应的最小下标数值写入文件
    人生路上对我影响最大的三位老师
    秋季总结
    抓老鼠啊~亏了还是赚了?
    币值转换
    打印沙漏
    第十一周编程总结
  • 原文地址:https://www.cnblogs.com/wangyonglai/p/10112172.html
Copyright © 2020-2023  润新知