.net2.0+ Winform項目實現(xiàn)彈出容器層
適用于:.net2.0+ Winform項目
背景:
有時候我們需要開一個簡單的窗口來做一些事,例如輸入一些東西、點選一個item之類的,可能像這樣:
完了返回原窗體并獲取剛剛的輸入,這樣做并沒有什么問題,但在幾天前我突然產(chǎn)生了一些想法:為什么非得有板有眼的彈出一個窗體給用戶呢,是不是可以在按鈕附近迅速呈現(xiàn)一個層來做這些事呢,類似快捷菜單那樣,用戶高興就在里面做一下該做的事,不高興就在其它地方點一下它就消失,本來很輕便快捷的操作,DUANG~彈出一個窗體來會不會令用戶心里咯噔一下呢,感受層面的事情往往是很微妙的,不管怎樣,我既然起了這個念頭,just try it。
我首先找了一下現(xiàn)成的方案,果然在牛逼的codeproject.com已經(jīng)有牛人做了這樣的事情:
http://www.codeproject.com/Articles/17502/Simple-Popup-Control
簡單體驗了一下,的確是了不起的創(chuàng)造。原理是利用ToolStripControlHost可以承載自定義控件的這一能力,讓下拉式控件ToolStripDropDown將任何自定義控件像右鍵菜單那樣彈出來(別忘了右鍵菜單ContextMenuStrip就是繼承自ToolStripDropDown),這樣就等于把菜單作為一個容器,可以彈出任何或簡單或復雜的控件組合,同時又具有菜單具有的便捷性,召之即來揮之即去。當時了解到這方案的時候真挺開心,正是我想要的效果,感覺這下好了,不用瞎費勁自己造了。
但很快發(fā)現(xiàn)一個在我看來還挺在意的不足,就是ToolStripDropDown只有Show,沒有ShowDialog,就是不能以模式化(Modal,也有叫模態(tài)的,鑒于MSDN都稱模式,我也隨流叫它模式)的方式彈出,這是由ToolStripDropDown的固有能力決定的,該方案既然基于ToolStripDropDown,自然也受限于此,不能模式化彈出。這樣帶來的問題是某些情況下的調(diào)用體驗不好(體驗這種事當然不是用戶才有的專利,俺們碼農(nóng)也是人,也要講體驗的說),比如彈出的控件是讓用戶輸入一些東西,完了用戶點擊某個按鈕什么的返回原窗體,然后在原窗體獲取用戶剛剛的輸入,然后接著做后面的事。由于非模式的Show不會阻塞代碼,所以就不能在Show的下方想當然的獲取值、使用值~這是顯然的。要想獲得值可能就得額外采取一些做法,例如響應(yīng)彈出控件的關(guān)閉事件,或者把原窗體傳入彈出控件完了在后者中做原本應(yīng)該在原窗體中做的事~等等,辦法當然有很多,但這都是因為只能Show帶來的多余的事,有什么比在一個方法中彈出控件、等待返回、繼續(xù)處理來的爽滑的呢,像這樣不是很自然嗎:
所以很遺憾,不得不揮別這個優(yōu)秀的方案,造自己的輪子。不過受該方案的啟發(fā),我想到用ContextMenu來做容器(注意這個菜單類跟上面提到的繼承自ToolStripDropDown的ContextMenuStrip大大的不同,前者是OS原生的菜單,就是在桌面、圖標以及文本框中右鍵彈出的那種菜單,.net是通過調(diào)API的方式來操作這樣的菜單,而后者則完全是.net實現(xiàn),更多信息請參考MSDN,此處不展開),因為ContextMenu的Show是阻塞式的,正合我意。但一番嘗試之后放棄,它的菜單項MenuItem不像ToolStripItem那樣可以通過ToolStripControlHost承載自定義控件,希望是我能力有限,總之我做不到把自定義控件弄到ContextMenu上,也沒見過原生菜單上出現(xiàn)過文本框、復選框等奇怪的東西,如果您知道怎么擴展原生菜單,還望不吝賜教,先行謝過!
我還是打回.net的主意,當中仍然是做了許多不同的嘗試,F(xiàn)orm、Panel、UserControl、ContainerControl、Control等等看起來適合做容器層的東西都試了個遍,甚至重新在ToolStripDropDown上打主意,最后選用Form,改造一番,自我感覺較理想的實現(xiàn)了我要的東西:一個叫做FloatLayerBase的基類,它本身繼承自System.Windows.Forms.Form類,而需要作為浮動層顯示的應(yīng)用則繼承自FloatLayerBase進行實現(xiàn),例如下面這個接受用戶輸入數(shù)值的NumInputDemo實現(xiàn):
樣子和特點:不會令父窗口失去焦點(不會搶焦點的層才是好層):
當然,男人不止一面:
還有其它邊框樣式,有待用戶自行體驗,最后有demo提供。
可以有調(diào)整尺寸的手柄:
可以點住客戶區(qū)拖動:
別的一些應(yīng)用:
這些都只是demo,沒那么好看和強大,重點是有了這個FloatLayerBase,就可以實現(xiàn)自己的浮動應(yīng)用。
使用說明:確保FloatLayerBase類在項目中~廢話。源碼在此:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AhDung.WinForm.Controls
{
/// <summary>
/// 浮動層基類
/// </summary>
public class FloatLayerBase : Form
{
/// <summary>
/// 鼠標消息篩選器
/// </summary>
//由于本窗體為WS_CHILD,所以不會收到在窗體以外點擊鼠標的消息
//該消息篩選器的作用就是讓本窗體獲知鼠標點擊情況,進而根據(jù)鼠標是否在本窗體以外的區(qū)域點擊,做出相應(yīng)處理
readonly AppMouseMessageHandler _mouseMsgFilter;
/// <summary>
/// 指示本窗體是否已ShowDialog過
/// </summary>
//由于多次ShowDialog會使OnLoad/OnShown重入,故需設(shè)置此標記以供重入時判斷
bool _isShowDialogAgain;
//邊框相關(guān)字段
BorderStyle _borderType;
Border3DStyle _border3DStyle;
ButtonBorderStyle _borderSingleStyle;
Color _borderColor;
/// <summary>
/// 獲取或設(shè)置邊框類型
/// </summary>
[Description("獲取或設(shè)置邊框類型。")]
[DefaultValue(BorderStyle.Fixed3D)]
public BorderStyle BorderType
{
get { return _borderType; }
set
{
if (_borderType == value) { return; }
_borderType = value;
Invalidate();
}
}
/// <summary>
/// 獲取或設(shè)置三維邊框樣式
/// </summary>
[Description("獲取或設(shè)置三維邊框樣式。")]
[DefaultValue(Border3DStyle.RaisedInner)]
public Border3DStyle Border3DStyle
{
get { return _border3DStyle; }
set
{
if (_border3DStyle == value) { return; }
_border3DStyle = value;
Invalidate();
}
}
/// <summary>
/// 獲取或設(shè)置線型邊框樣式
/// </summary>
[Description("獲取或設(shè)置線型邊框樣式。")]
[DefaultValue(ButtonBorderStyle.Solid)]
public ButtonBorderStyle BorderSingleStyle
{
get { return _borderSingleStyle; }
set
{
if (_borderSingleStyle == value) { return; }
_borderSingleStyle = value;
Invalidate();
}
}
/// <summary>
/// 獲取或設(shè)置邊框顏色(僅當邊框類型為線型時有效)
/// </summary>
[Description("獲取或設(shè)置邊框顏色(僅當邊框類型為線型時有效)。")]
[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;
}
}
//構(gòu)造函數(shù)
public FloatLayerBase()
{
//初始化消息篩選器。添加和移除在顯示/隱藏時負責
_mouseMsgFilter = new AppMouseMessageHandler(this);
//初始化基類屬性
InitBaseProperties();
//初始化邊框相關(guān)
_borderType = BorderStyle.Fixed3D;
_border3DStyle = System.Windows.Forms.Border3DStyle.RaisedInner;
_borderSingleStyle = ButtonBorderStyle.Solid;
_borderColor = Color.DarkGray;
}
protected override void OnLoad(EventArgs e)
{
//防止重入
if (_isShowDialogAgain) { return; }
//需得減掉兩層邊框?qū)挾龋\行時尺寸才與設(shè)計時完全相符,原因不明
//確定與ControlBox、FormBorderStyle有關(guān),但具體聯(lián)系不明
if (!DesignMode)
{
Size size = SystemInformation.FrameBorderSize;
this.Size -= size + size;//不可以用ClientSize,后者會根據(jù)窗口風格重新調(diào)整Size
}
base.OnLoad(e);
}
protected override void OnShown(EventArgs e)
{
//防止重入
if (_isShowDialogAgain) { return; }
//在OnShown中為首次ShowDialog設(shè)標記
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窗體和本窗體都將處于無響應(yīng)狀態(tài)
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設(shè)置Parent,因為模式窗體是TopLevel,.Net拒絕為頂級窗體設(shè)置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);
}
//實現(xiàn)窗體客戶區(qū)拖動
//在WndProc中實現(xiàn)這個較麻煩,所以放到這里做
protected override void OnMouseDown(MouseEventArgs e)
{
//讓鼠標點擊客戶區(qū)時達到與點擊標題欄一樣的效果,以此實現(xiàn)客戶區(qū)拖動
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">觸發(fā)彈出窗體的控件</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">觸發(fā)彈出窗體的控件</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">觸發(fā)彈出窗體的工具欄項</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">觸發(fā)彈出窗體的工具欄項</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">觸發(fā)彈出窗體的控件</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">觸發(fā)彈出窗體的控件</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">觸發(fā)彈出窗體的工具欄項</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">觸發(fā)彈出窗體的工具欄項</param>
/// <param name="offset">相對item偏移</param>
public void Show(ToolStripItem item, Point offset)
{
this.ShowInternal(item, offset);
}
/// <summary>
/// ShowDialog內(nèi)部方法
/// </summary>
private DialogResult ShowDialogInternal(Component controlOrItem, Point offset)
{
//快速連續(xù)彈出本窗體將有可能遇到尚未Hide的情況下再次彈出,這會引發(fā)異常,故需做處理
if (this.Visible) { return System.Windows.Forms.DialogResult.None; }
this.SetLocationAndOwner(controlOrItem, offset);
return base.ShowDialog();
}
/// <summary>
/// Show內(nèi)部方法
/// </summary>
private void ShowInternal(Component controlOrItem, Point offset)
{
if (this.Visible) { return; }//原因見ShowDialogInternal
this.SetLocationAndOwner(controlOrItem, offset);
base.Show();
}
/// <summary>
/// 設(shè)置坐標及所有者
/// </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;
//設(shè)置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才能支持調(diào)整大小的同時,不受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("設(shè)置邊框請使用Border相關(guān)屬性!", 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)
{
//如果在本窗體以外點擊鼠標,隱藏本窗體
//若想在點擊標題欄、滾動條等非客戶區(qū)也要讓本窗體消失,取消0xA1的注釋即可
//本例是根據(jù)坐標判斷,亦可以改為根據(jù)句柄,但要考慮子孫控件
//之所以用API而不用Form.DesktopBounds是因為后者不可靠
if ((m.Msg == 0x201/*|| m.Msg==0xA1*/)
&& _layerForm.Visible && !NativeMethods.GetWindowRect(_layerForm.Handle).Contains(MousePosition))
{
_layerForm.Hide();//之所以不Close是考慮應(yīng)該由調(diào)用者負責銷毀
}
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;
}
}
}
}
新建繼承窗體,選擇繼承自FloatLayerBase類;也可以新建普通窗體,然后把基類由Form改為FloatLayerBase
在設(shè)計器和源碼中打造浮動應(yīng)用
在需要的地方使用它。關(guān)于使用,先看一下FloatLayerBase的部分公開成員:
//屬性
public BorderStyle BorderType { get; set; }
public Border3DStyle Border3DStyle { get; set; }
public ButtonBorderStyle BorderSingleStyle { get; set; }
public Color BorderColor { get; set; }
//方法
public void Show(Control control);
public void Show(Control control, Point offset);
public void Show(Control control, int offsetX, int offsetY);
public void Show(ToolStripItem item);
public void Show(ToolStripItem item, Point offset);
public void Show(ToolStripItem item, int offsetX, int offsetY);
public DialogResult ShowDialog(Control control);
public DialogResult ShowDialog(Control control, Point offset);
public DialogResult ShowDialog(Control control, int offsetX, int offsetY);
public DialogResult ShowDialog(ToolStripItem item);
public DialogResult ShowDialog(ToolStripItem item, Point offset);
public DialogResult ShowDialog(ToolStripItem item, int offsetX, int offsetY);
上面4個屬性都是跟邊框有關(guān)的,邊框總共有3種形態(tài),三維、線型、無,由BorderType指定;當為三維形態(tài)時,由Border3DStyle指定具體樣式;為線型時,由BorderSingleStyle和BorderColor分別指定具體線型和顏色。原Form.FormBorderStyle屬性已被屏蔽,不允許子類訪問,還有若干原Form的屬性也已屏蔽,原因都在源碼里。另外,原Form.SizeGripStyle照常使用,是否允許調(diào)整浮動層大小就靠它了
方法就說一下Show和ShowDialog,顯然分別是用來非模式化/模式化顯示浮動層的,兩者在調(diào)用角度的重大區(qū)別就是,前者不會阻塞代碼,后者則會,實際應(yīng)用中根據(jù)情況選用。每個方法從參數(shù)又分Control和ToolStripItem兩類,都是代表從什么控件上彈出浮動層的意思,前者接受Button、TextBox等控件(不能傳入Form,后果會不愉快),后者接受工具欄上面的項目,例如ToolStripButton、ToolStripTextBox之類的。重載可以指定相對control或item的偏移位置,默認是在control/item的下方彈出浮動層。最后無論是Show還是ShowDialog彈出來的浮動層,都可以像右鍵菜單那樣通過在其它地方點鼠標使之消失,這里需要說明一下:
鼠標只會點在本程序內(nèi)的窗體中時,讓浮動層消失。點在程序外的窗口、桌面、任務(wù)欄這些則不會。為什么要這樣是因為要做到完全像右鍵菜單那樣對全局鼠標敏感,需要全局鉤子,這會增加代碼量(性能且不說,沒測過不妄言),而且我認為沒必要全局敏感浮動層消失是調(diào)用Hide方法,所以對于模式化打開的浮動層,會返回DialogResult.Cancel,這是.net對模式對話框的設(shè)計使然,模式對話框被Hide或Close時,就是返回Cancel。在此也提醒一下調(diào)用者,在使用模式對話框時,永遠考慮有返回Cancel這種情況,不限于本例,而是所有對話框
原Show()/Show(IWin32Window)和ShowDialog()/ShowDialog(IWin32Window)已被屏蔽,原因見源碼。
其它:
編寫期間一直使用PopupFormBase作為類名,發(fā)布最后時刻才改為現(xiàn)在的FloatLayerBase,所以demo中可能尚有依據(jù)原名起名的子類、方法名等。
Demo下載:
http://pan.baidu.com/s/1mgnGPGc
里面有個Tester供您體驗。
欄 目:C#教程
下一篇:Extjs4如何處理后臺json數(shù)據(jù)中日期和時間
本文標題:.net2.0+ Winform項目實現(xiàn)彈出容器層
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/7030.html
您可能感興趣的文章
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新聞效果的方法
- 01-10C#實現(xiàn)Winform中打開網(wǎng)頁頁面的方法
- 01-10winform實現(xiàn)創(chuàng)建最前端窗體的方法
- 01-10WinForm實現(xiàn)自定義右下角提示效果的方法
- 01-10winform 實現(xiàn)控制輸入法
- 01-10WinForm實現(xiàn)程序一段時間不運行自動關(guān)閉的方法
- 01-10Winform消除button按下出現(xiàn)的虛線簡單實現(xiàn)方法
- 01-10WinForm實現(xiàn)窗體最大化并遮蓋任務(wù)欄的方法


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當前操作系統(tǒng)已
隨機閱讀
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個骰子


