1. 程式人生 > >用winform實現的類似於WPF中PopUp控制元件的一段程式碼

用winform實現的類似於WPF中PopUp控制元件的一段程式碼

用winform實現的類似於WPF中PopUp控制元件的一段程式碼

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace xxxxxxx
{
    /// <summary>
/// 浮動層基類 /// </summary> public 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); //初始化基類屬性 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) { return ShowDialog(control, 0, control.Height); } /// <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) { return ShowDialog(control, new Point(offsetX, offsetY)); } /// <summary> /// 顯示為模式窗體 /// </summary> /// <param name="control">觸發彈出窗體的控制元件</param> /// <param name="offset">相對control偏移</param> public DialogResult ShowDialog(Control control, Point offset) { return this.ShowDialogInternal(control, offset); } /// <summary> /// 顯示為模式窗體 /// </summary> /// <param name="item">顯示在該工具欄項的下方</param> public DialogResult ShowDialog(ToolStripItem item) { return ShowDialog(item, 0, item.Height); } /// <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) { return ShowDialog(item, new Point(offsetX, offsetY)); } /// <summary> /// 顯示為模式窗體 /// </summary> /// <param name="item">觸發彈出窗體的工具欄項</param> /// <param name="offset">相對item偏移</param> public DialogResult ShowDialog(ToolStripItem item, Point offset) { return this.ShowDialogInternal(item, offset); } /// <summary> /// 顯示窗體 /// </summary> /// <param name="control">顯示在該控制元件下方</param> public void Show(Control control) { Show(control, 0, control.Height); } /// <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) { Show(control, new Point(offsetX, offsetY)); } /// <summary> /// 顯示窗體 /// </summary> /// <param name="control">觸發彈出窗體的控制元件</param> /// <param name="offset">相對control偏移</param> public void Show(Control control, Point offset) { this.ShowInternal(control, offset); } /// <summary> /// 顯示窗體 /// </summary> /// <param name="item">顯示在該工具欄下方</param> public void Show(ToolStripItem item) { Show(item, 0, item.Height); } /// <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) { Show(item, new Point(offsetX, offsetY)); } /// <summary> /// 顯示窗體 /// </summary> /// <param name="item">觸發彈出窗體的工具欄項</param> /// <param name="offset">相對item偏移</param> public void Show(ToolStripItem item, Point offset) { this.ShowInternal(item, offset); } /// <summary> /// ShowDialog內部方法 /// </summary> private DialogResult ShowDialogInternal(Component controlOrItem, Point offset) { //快速連續彈出本窗體將有可能遇到尚未Hide的情況下再次彈出,這會引發異常,故需做處理 if (this.Visible) { return System.Windows.Forms.DialogResult.None; } this.SetLocationAndOwner(controlOrItem, offset); return base.ShowDialog(); } /// <summary> /// Show內部方法 /// </summary> private void ShowInternal(Component controlOrItem, Point offset) { if (this.Visible) { return; }//原因見ShowDialogInternal this.SetLocationAndOwner(controlOrItem, offset); base.Show(); } /// <summary> /// 設定座標及所有者 /// </summary> /// <param name="controlOrItem">控制元件或工具欄項</param> /// <param name="offset">相對偏移</param> private void SetLocationAndOwner(Component controlOrItem, Point offset) { 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); 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; 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