WinForm实现类似QQ停靠,显示隐藏过程添加特效效果
這可能是個老題長談的問題了,只是在項目中會用到這個效果,所以今天做個記錄。大家見了別噴我。在項目中的需求是這樣的。
打開程序,在屏幕的右下角會顯示一個窗體,一般情況下該窗體會隱藏停靠在右邊,只露出很小部分,當鼠標移動到這個很小部分時,窗體全部顯示,顯示過程是從右邊滑動到左邊,當鼠標離開窗體時,窗體需要隱藏在右邊,只露出很小部分,隱藏過程是從左邊滑動到右邊。
實現此類效果我碰到的連個難點是:1、如何判斷鼠標離開了窗體?2、窗體顯示隱藏過程中效果如何表現平滑(就是給人一種流暢感覺)?
1、判斷鼠標離開窗體我開始想的是在WndProc方法中來獲取鼠標坐標然后根據窗體的Location來判斷,可能是小弟愚笨,該方法沒有處理好,運行后界面卡住了。然后我只有用個計時器,每隔幾秒獲取一下鼠標坐標在根據窗體的Location來判斷。獲取坐標有個API方法GetCursorPos,有資料表明此方法是最優效率的,那我們就用它好了。
2、顯示隱藏效果開始想的也是改變窗體坐標來實現,但是這個方法做出來的效果比較差,畫面感覺不流暢,后來查到可以用API方法AnimateWindow來實現這個效果,因此我們來認識一下AnimateWindow()方法;
此方法需要傳遞三個參數:
第一個參數:傳入需要顯示特效的窗體的句柄。
第二個參數:完成特效所花時間,單位:毫秒,也就是說你可以指定多少時間內完成指定的特效
第三個參數:指定特效類型,此參數可以指定多個,多個之間用|隔開。這里列舉了一般的9個特效。有這9個基本夠用了。
關于這個方法的詳細資料我就不一一列舉了,大家在網上搜搜,很多資料的。下面進入正題。
1、建一個winform項目,命名:DockFormsApplication,名字大家可以自定義的。
2、建完后項目中會有個默認創建好的窗體Form1,修改Form1的text屬性為:“仿QQ停靠,加特效”
3、添加API方法AnimateWindow()和該方法需要的一些特效參數, 特效參數命名是我自己隨便命名的,大家就不要深究了,至于為什么要這么命名,我自己也不知道,反正能用就行。
注意:AnimateWindow()方法需要引用using System.Runtime.InteropServices;
/// <summary>/// //從左到右/// </summary>public const Int32 AW_HOR_LEFT_RIGHT = 0x00000001;/// <summary>/// 從右到左/// </summary>private const Int32 AW_HOR_RIGHT_LEFT = 0x00000002;/// <summary>/// 從上到下/// </summary>private const Int32 AW_VER_UP_DOWN = 0x00000004;/// <summary>/// 從下到上/// </summary>private const Int32 AW_VER_DOWN_UP = 0x00000008;/// <summary>/// 從中間到四周/// </summary>private const Int32 AW_CENTER = 0x00000010;/// <summary>/// 隱藏窗口/// </summary>private const Int32 AW_HIDE = 0x00010000;/// <summary>/// 顯示窗口/// </summary>private const Int32 AW_ACTIVATE = 0x00020000;/// <summary>/// 使用滑動類型。缺省則為滾動動畫類型。當使用AW_CENTER標志時,這個標志就被忽略/// </summary>private const Int32 AW_SLIDE = 0x00040000;/// <summary>/// 改變透明度/// </summary>private const Int32 AW_BLEND = 0x00080000;/// <summary>/// 特效花費時間 單位:毫秒/// </summary>private int _speed = 500;[DllImport("user32.dll")]public static extern void AnimateWindow(IntPtr hwnd, int stime, int style);//顯示效果3、添加API方法GetCursorPos用于獲取鼠標坐標。此方法需要傳入一個坐標對象。該對象是一個二維結構。存儲坐標的X值和Y值。
/// <summary>/// 鼠標坐標/// </summary>private Point _cursorPoint;//API獲取鼠標坐標[DllImport("user32.dll")]public static extern bool GetCursorPos(out Point pt);4、設置窗體顯示在右下角,并且重寫WndProc方法禁止鼠標拖動和雙擊標題欄最大化
private void Form1_Load(object sender, EventArgs e){//設置窗體顯示位置 右下角int workY = Screen.PrimaryScreen.WorkingArea.Height - Height;int X = Screen.PrimaryScreen.Bounds.Width - Width;this.Location = new Point(X, workY);}//重寫WndProc方法,禁止拖動和雙擊標題欄最大化protected override void WndProc(ref Message m){if (m.Msg == 0x231){this.SuspendLayout();}else if (m.Msg == 0x232){this.ResumeLayout();}else if (m.Msg == 0xA1 && m.WParam.ToInt32() == 2)//禁止拖動 {return;}base.WndProc(ref m);}后來發現,更改窗體屬性:FormBorderStyle值為:FixedSingle也可以達到禁止拖動的效果
5、因為要每隔幾秒獲取鼠標坐標判斷鼠標是否在窗體范圍內,因此需要一個計時器。考慮到性能神馬的,我比較喜歡使用System.Threading.Timer,下面就是計時器嗦必須的幾個變量
??? 注意:這里我需要說明一下,由于AnimateWindow()方法控制窗體特效只能窗體顯示和隱藏兩種狀態,每個特效完成后窗體要么隱藏,要么顯示,如何使特效過后窗體一直顯示,我想了個折中辦法,只要你隱藏了,我就再把你顯示出來,因此在計時器中需要對窗體進行操作,如此則需要跨線程訪問窗體,因此就需要使用委托了,然后Invoke就可以了。
//線程暫停時間 單位:毫秒private int _timespan = 1000;private System.Threading.Timer _timer;private delegate void LoadListDelegate();private LoadListDelegate _loaddelegate;6、程序邏輯需要的幾個變量
/// <summary>/// 窗體是否顯示,true——顯示、false——隱藏/// </summary>private bool _isActive = true;/// <summary>/// 停靠在邊緣時,顯示窗體的寬度/// </summary>private const int _smallX = 5;7、添加兩個方法,顯示窗體和隱藏窗體的兩個方法
/// <summary>/// 隱藏窗體/// </summary>private void SetHide(){if (_isActive){AnimateWindow(this.Handle, _speed, AW_HOR_LEFT_RIGHT | AW_SLIDE | AW_HIDE);int X = Screen.PrimaryScreen.Bounds.Width - _smallX;int Y = this.Location.Y;this.Location = new Point(X, Y);AnimateWindow(this.Handle, 10, AW_BLEND | AW_ACTIVATE);_isActive = false;}}/// <summary>/// 顯示窗體/// </summary>private void SetActivate(){if (!_isActive){AnimateWindow(this.Handle, 10, AW_BLEND | AW_HIDE);int X = Screen.PrimaryScreen.Bounds.Width - Width;int Y = this.Location.Y;this.Location = new Point(X, Y);AnimateWindow(this.Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE | AW_ACTIVATE);_isActive = true;}}8、添加方法,判斷鼠標是否在窗體范圍內,如果在范圍內,則顯示窗體,如果不在范圍內,則停靠在右邊并隱藏窗體
private void LoadControl(){#region 控制窗體顯示和隱藏//獲取當前鼠標坐標GetCursorPos(out _cursorPoint);//根據 窗體當前狀態,判斷窗體接下來是顯示還是隱藏。if (_isActive){//當前窗體為顯示,則接下來是隱藏//如果鼠標坐標不在窗體范圍內,則設置窗體隱藏,否則不處理if (_cursorPoint.X < this.Location.X || _cursorPoint.Y < this.Location.Y){SetHide();}}else{//當前窗體為隱藏,則接下來是顯示//如果鼠標坐標在窗體范圍內,則設置窗體顯示,否則不處理if (_cursorPoint.X >= this.Location.X && _cursorPoint.Y >= this.Location.Y){SetActivate();}}#endregion}9、添加計時器,每隔1秒判斷當前鼠標位置,因為用到委托,因此需要在構造方法中添加一行代碼_loaddelegate = LoadControl;用于指定委托的方法:
private void Form1_Load(object sender, EventArgs e){//設置窗體顯示位置 右下角int workY = Screen.PrimaryScreen.WorkingArea.Height - Height;int X = Screen.PrimaryScreen.Bounds.Width - Width;this.Location = new Point(X, workY);//窗體打開的時候就開始計時器 BeginTimer();}private void BeginTimer(){TimerCallback tcBack = new TimerCallback(InvokTimer);_timer = new System.Threading.Timer(tcBack, null, 5000, _timespan);}private void InvokTimer(object state){if (this.InvokeRequired){this.Invoke(_loaddelegate);}}10、為了窗體一打開和關閉時有特效顯示,需要重寫OnLoad方法和實現Form1_Closing方法
protected override void OnLoad(EventArgs e){base.OnLoad(e);//從右到左滑動AnimateWindow(this.Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE | AW_ACTIVATE);}private void Form1_FormClosing(object sender, FormClosingEventArgs e){//淡出效果AnimateWindow(this.Handle, 1000, AW_BLEND | AW_HIDE);}到此,所有代碼編寫完成,如果想要有更好的體驗,可以設置一下窗體的以下屬性值:
this.MaximizeBox = false;//取消最大化按鈕this.MinimizeBox = false;//取消最小化按鈕this.ShowInTaskbar = false;//任務欄不顯示窗體圖標this.TopMost = false;//設置窗體總是顯示在最前面
完整代碼如下:
轉載于:https://www.cnblogs.com/springSky/archive/2013/02/04/2891345.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的WinForm实现类似QQ停靠,显示隐藏过程添加特效效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lumia 800 7.10.8858.
- 下一篇: 产生不重复的随机牌