久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

一步一步玩控件:自定義TabControl

 鴻楓nh 2016-08-03


作者:野比 ([email protected]

時間:May, 2012

封面圖片為野比原創(chuàng),請勿未經(jīng)允許私自引用


#1-1

嗯,,各位,,又是我,生物鐘顛倒的家伙,。

今天我要山寨的是大名鼎鼎的Apple,,傳說中的「被山寨之王」。

沒錯,,都被我山寨好幾次了,。

說起Apple,相信大家對他家的各種產(chǎn)品,,不管他軟還是硬,,都有相當(dāng)?shù)暮酶小?/p>

最近Apple把自家的Web瀏覽器Safari升級到了第5版,并同步推出了Windows版,,支持WinXP開始的全部Windows版本,。

不得不說,這是一個很給力的瀏覽器,,它看起來就像這樣,。

Icon for Safari

其實我并不是蘋果控,我控紅富士要多點,。客觀的評價Safari,,這個軟件界面華麗,,速度快,但在Windows平臺上,,TopSites首頁資源消耗巨大,,操作習(xí)慣和常規(guī)Win瀏覽器有一定區(qū)別,部分網(wǎng)頁不支持或不兼容(WebKit引擎),。

不多說了,,這不是重點。重點在于它的「偏好設(shè)置(Preference)」界面,,就是這個:



看到這個,,你肯定會覺得怎么蘋果的東西會變得這么一般呢?不過就是TabControl上面增加了幾個圖標(biāo)嘛,。

嗯,,朋友,你說的似乎沒錯,。但是,,我曾經(jīng)也算中肯的評價過蘋果的東西,,拋開外觀,蘋果的特點之一就是「悶騷」,,還有「OCD」,,也就是強迫癥。

聽我這么說顯得很干癟,,那么就讓我順著導(dǎo)航標(biāo)簽,,一路點擊過去,看看會發(fā)生什么事,。




沒錯,,這窗口會自動伸縮,而且是動畫的,!這就是apple悶騷的地方,!

為了不讓他一家獨騷,為了不辜負(fù)他被山寨之王的名頭,,我只好勉為其難的山寨一番了,。


山寨前的準(zhǔn)備

山寨其實沒啥好準(zhǔn)備的,但還是需要幾樣重要的東西:

  • 原裝貨:Apple Safari 5
  • 照相機:Snagit 10
  • 生產(chǎn)線:Visual Studio 2005
  • 手冊:MSDN
  • 苦力:野比

分析,,分析

山寨的靈魂在于分析,,首先把剛才拍的高清果照扯過來分解了。



所以,,我把他分解成這幾個部分:

  1. 根據(jù)標(biāo)簽不同修改窗體標(biāo)題
  2. 導(dǎo)航標(biāo)簽
  3. 標(biāo)簽面板
  4. 自動縮放

組件設(shè)計

分析了其中的功能,,那么就要想想怎么來實現(xiàn)。

從功能來看,,這個窗口實際上是由多個子面板切換來實現(xiàn)的,,最多他加了點自動縮放。所以從本質(zhì)來說,,還是一個標(biāo)簽切換的窗口,。

我最早想到的就是大名鼎鼎卻又丑得無以復(fù)加的TabControl。



按照標(biāo)簽切換這個思想,,TabControl完全可以勝任這次的山寨需求,。但是TabControl這么丑,必須要給它整整容才行,。想不到我竟然有整容的才華,。


下手吧,年輕人,!

因為要改動的地方會很多,,所以還是完全自己來繪制標(biāo)簽好了。為了完全自定義TabControl,同時方便循環(huán)利用,,從TabControl派生一個我們自己的標(biāo)簽控件TabControlEx,。

  1. public class TabControlEx : System.Windows.Forms.TabControl  



這就是我們的TabControlEx,看起來和TabControl沒什么兩樣(那是當(dāng)然的),。

為了讓他看起來不太一樣,,在構(gòu)造函數(shù)里加上下面的代碼。

  1. base.SetStyle(  
  2.      ControlStyles.UserPaint |                      // 控件將自行繪制,,而不是通過操作系統(tǒng)來繪制  
  3.      ControlStyles.OptimizedDoubleBuffer |          // 該控件首先在緩沖區(qū)中繪制,,而不是直接繪制到屏幕上,這樣可以減少閃爍  
  4.      ControlStyles.AllPaintingInWmPaint |           // 控件將忽略 WM_ERASEBKGND 窗口消息以減少閃爍  
  5.      ControlStyles.ResizeRedraw |                   // 在調(diào)整控件大小時重繪控件  
  6.      ControlStyles.SupportsTransparentBackColor,    // 控件接受 alpha 組件小于 255 的 BackColor 以模擬透明  
  7.      true);                                         // 設(shè)置以上值為 true  
  8. base.UpdateStyles();  

這段代碼的意思就像注釋里說的,,注意ControlStyles這個枚舉是可以按位組合的,,所以上面要用「或(|)」來進行連接,這樣系統(tǒng)就會完全忽視TabControl這個基類的界面顯示,,而使用我們自己的方式來呈現(xiàn)UI,。

現(xiàn)在TabControlEx看起來是這樣的。


啥米,??。MG,!東西哪去了,??

嗯,,當(dāng)我第一次玩UserPaint的時候,,也被嚇了一跳。其實這就是上面我們設(shè)置的那句ControlStyles.UserPaint,,于是系統(tǒng)就不幫我們畫任何東西了,。

所以從現(xiàn)在開始,一切都要靠自己了,。下面所有的繪制都在OnPaint()方法中繪制。

為了先讓我們找到方向,,在OnPaint()方法中,,我們先把Tab的位置找到,為此我們給每個Tab的邊框都畫出來,。

  1. protected override void OnPaint(PaintEventArgs e)  
  2. {  
  3.     for (int i = 0; i < this.TabCount; i++)  
  4.     {  
  5.         e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));  
  6.     }  
  7. }  

TabControl.GetTabRect(int)的功能是獲得指定index的標(biāo)簽的矩形位置,。畫完后,我們的TabControlEx看起來不那么迷糊了,。


可是,,標(biāo)簽的大小還是不對,我們要的不是普通的那種長條,而是悶騷的蘋果的瘦高型,,要像這樣,。


嗯,好吧,,我們回到構(gòu)造函數(shù),,用下面的語句來設(shè)置大小。

  1. this.SizeMode = TabSizeMode.Fixed;  // 大小模式為固定  
  2. this.ItemSize = new Size(44, 55);   // 設(shè)定每個標(biāo)簽的尺寸  

上面設(shè)置44x55其實只是因為蘋果原版剛好是這么大,,先這么著,,后面如果不合適了,回頭再來改?,F(xiàn)在標(biāo)簽是這樣的了,。


Apple標(biāo)簽的選中狀態(tài)是帶陰影的,看起來很酷,,可是如果我用GDI+來畫的話,,什么漸變什么變換,煩都煩死了,。怎么辦呢,?

請記住,我們正在山寨,。所謂山寨的精神,,就是不問方法、不擇手段,,只要最后「看起來一樣」就行了,。所以,我決定用上摳圖大法,,把apple的背景圖摳出來,。



把這個背景保存為TabBackground.bmp文件,然后添加到項目中,,把它做成「嵌入的資源」,,就像這樣。




然后我們用一個變量來保存背景圖,。因為這張圖隨時會用到,,所以還是做成全局變量(類級別),在構(gòu)造函數(shù)里讀取圖片,。

  1. Image backImage;  
  2. public TabControlEx()  
  3. {  
  4.     // (略)  
  5.     backImage = new Bitmap(this.GetType(), "TabButtonBackground.bmp");   // 從資源文件(嵌入到程序集)里讀取圖片  
  6. }  

現(xiàn)在有了圖標(biāo),,加上去看看吧。在OnPaint()里這樣寫,。

  1. if (this.SelectedIndex == i)  
  2. {  
  3.     e.Graphics.DrawImage(backImage, this.GetTabRect(i));  
  4. }  

只有被選中的標(biāo)簽才會出現(xiàn)這種背景,。于是,,標(biāo)簽變成這樣了。



繪制文字

這會看著還挺單調(diào)的,,所以我們來加點料,。下面來畫文字。說起文字,,我想你應(yīng)該注意到了,,Safari的標(biāo)簽文字,都是帶有陰影的(準(zhǔn)確的說是高光),。


所以,,在繪制文字時,先用高光色繪制第一遍,,再用普通文字色(黑)繪制第二遍,。

  1. protected override void OnPaint(PaintEventArgs e)  
  2. {  
  3.     for (int i = 0; i < this.TabCount; i++)  
  4.     {  
  5.         // (略)  
  6.   
  7.         // Calculate text position  
  8.         Rectangle bounds = this.GetTabRect(i);  
  9.         PointF textPoint = new PointF();  
  10.         SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font);  
  11.   
  12.         // 注意要加上每個標(biāo)簽的左偏移量X  
  13.         textPoint.X  
  14.             = bounds.X + (bounds.Width - textSize.Width) / 2;  
  15.         textPoint.Y  
  16.             = bounds.Bottom - textSize.Height - this.Padding.Y;  
  17.   
  18.         // Draw highlights  
  19.         e.Graphics.DrawString(  
  20.             this.TabPages[i].Text,  
  21.             this.Font,  
  22.             SystemBrushes.ControlLightLight,    // 高光顏色  
  23.             textPoint.X,  
  24.             textPoint.Y);  
  25.   
  26.         // 繪制正常文字  
  27.         textPoint.Y--;  
  28.         e.Graphics.DrawString(  
  29.             this.TabPages[i].Text,  
  30.             this.Font,  
  31.             SystemBrushes.ControlText,    // 正常顏色  
  32.             textPoint.X,  
  33.             textPoint.Y);  
  34.   
  35.     }  
  36. }  


繽紛色彩的源泉:圖標(biāo)

文字也有了,那么接下來就輪到圖標(biāo)了,。TabControl是用ImageList控件來存儲自己使用的圖標(biāo)的,,那么添加一個ImageList,然后加入圖標(biāo),。注意這里都要32x32的圖標(biāo),,所以應(yīng)該設(shè)置ImageList.ImageSize為32x32。

  1. // 繪制圖標(biāo)  
  2. if (this.ImageList != null)  
  3. {  
  4.     int index = this.TabPages[i].ImageIndex;  
  5.     string key = this.TabPages[i].ImageKey;  
  6.     Image icon = new Bitmap(1, 1);  
  7.   
  8.     if (index > -1)  
  9.     {  
  10.         icon = this.ImageList.Images[index];  
  11.     }  
  12.     if (!string.IsNullOrEmpty(key))  
  13.     {  
  14.         icon = this.ImageList.Images[key];  
  15.     }  
  16.     e.Graphics.DrawImage(  
  17.         icon,  
  18.         bounds.X + (bounds.Width - icon.Width) / 2,  
  19.         bounds.Top + this.Padding.Y);  
  20. }  


嗯,,現(xiàn)在我們的標(biāo)簽看起來像那么回事了,,接下來就該難看的紅線條退休了。再完善一下,,我們的標(biāo)簽就OK了,。

  


同步滾動演示。上面是山寨,,下面是正品,,正品打開了文字抗鋸齒,我們也可以,,在OnPaint()事件開始加入這樣的代碼,。

  1. e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;  

到此,標(biāo)簽導(dǎo)航部分已經(jīng)完成,,剩下的,,就是窗體的自動縮放功能了。

作者:野比 ([email protected]

時間:May, 2012


#1-2


嗯,,還是我,。

現(xiàn)在繼續(xù)昨天的山寨,。昨天我們分析得到了4條需要山寨的部分,,如下,。

  1. 根據(jù)標(biāo)簽不同修改窗體標(biāo)題
  2. 導(dǎo)航標(biāo)簽
  3. 標(biāo)簽面板
  4. 自動縮放
通過昨天的努力,我們已經(jīng)搞定了第2,、3條,,所以,今天的任務(wù),,就只剩下兩條

  1. 根據(jù)標(biāo)簽不同修改窗體標(biāo)題
  2. 導(dǎo)航標(biāo)簽
  3. 標(biāo)簽面板
  4. 自動縮放


修改窗體標(biāo)題

我們參考下圖,,



我們制作的TabControlEx是作為它所在窗體的子控件存在的,為了獲得包含TabControlEx的窗體(的引用),,可以調(diào)用TabControlEx的FindForm()方法(從Control繼承),。FindForm()可以獲取容納該控件的頂層窗體,在我們的例子里,,就是我們的山寨Safari窗體,。

為了在TabControlEx剛剛加入父控件的時候(也就是窗體初始化的時候)就能夠順利「劫持」到窗體的引用,并修改它的標(biāo)題(否則顯示Tab0的時候會發(fā)現(xiàn)窗體的標(biāo)題還未改變),,我們重寫一下TabControlEx的ParentChanged事件,。

  1. // 對父窗體的引用  
  2. Form oldman;  
  3. protected override void OnParentChanged(EventArgs e)  
  4. {  
  5.     // 如果沒有劫持到,,則搜索  
  6.     if (oldman == null)  
  7.         oldman = this.FindForm();  
  8.   
  9.     oldman.Text = this.TabPages[0].Text;  
  10. }  

這樣,,我們就可以在啟動時就修改父窗體標(biāo)題了。我們最終的目的是每次切換標(biāo)簽時都改變父窗體標(biāo)題,,現(xiàn)在我們拿到了窗體的引用,,只需要重寫TabControlEx的Selected事件,。

  1. protected override void OnSelected(TabControlEventArgs e)  
  2. {  
  3.     parent.Text = e.TabPage.Text;  
  4. }  

下面是完成之后的效果



自動調(diào)整窗體大小

完成了雜項工作,現(xiàn)在要進入今天的重點:自動調(diào)整大小,。在開始之前,,先來回顧一下這個悶騷的功能。



下面來好好分析一下到底發(fā)生了什么事,。

注意,,大家發(fā)現(xiàn)右下角那個問號沒有,?根據(jù)觀察,,那個問號始終是保持在窗體右下角的,這就好辦了,,直接Anchor到Right和Bottom就行,。因此下面的分析中直接無視它了。

從本質(zhì)上來看,,因為切換的標(biāo)簽內(nèi)容高度不同,,所以窗體高度也發(fā)生了改變。但不管怎么變,,窗體的底部到最下面一個控件的距離Δ沒有變化,,參考分析圖,。





所以,,動畫就是在H1-H2這段距離內(nèi)發(fā)生的,。另外,,值得注意的是,,Safari是在窗體動畫完成,調(diào)整大小到位以后,,才顯示新標(biāo)簽的控件,,這樣做可以顯得很有動感,而且留下了足夠的時間加載控件,。所以,,動畫應(yīng)該在標(biāo)簽的Selecting事件里解決,而顯示控件留到Selected事件,。

下面來分析大小調(diào)整的算法,。


山寨算法:從不追求精確還原

通過慢鏡頭分析,可以看到在相同時間差內(nèi)窗體大小的運動距離是不同的,,這說明窗體大小不是勻速改變的。



為了不讓算法影響我們的設(shè)計進度,,將算法寫在單獨的方法里(最正規(guī)的應(yīng)該是寫成委托,,直接傳遞方法,但你認(rèn)為一個山寨貨有必要嗎),。

  1. private double getHeight(double time)  
  2. {  
  3.     // (略)  
  4. }  

既然這樣,,那么算法的問題我們稍后再來討論,現(xiàn)在研究怎樣讓窗體動起來,。

由于動畫過程較長,,將近1秒,那么我們實現(xiàn)的時候應(yīng)當(dāng)盡量以不影響主線程為前提,。除了動不動就多線程這種有點大炮打蚊子太2的方法外,,我們還可以用系統(tǒng)自帶的Timer。在每個Timer.Tick事件里挪一步,,合起來就成了動畫,。

  1. // Δ常量  
  2. int FORM_DELTA = 20;  
  3. // 動畫用Timer  
  4. Timer timer;  
  5. // 經(jīng)歷時間計數(shù)器  
  6. int elapsed = 0;  
  7.   
  8. // 構(gòu)造函數(shù)  
  9. public TabControlEx()  
  10. {  
  11.     // (略)  
  12.   
  13.     // 初始化Timer  
  14.     timer = new Timer();  
  15.     timer.Interval = 100;  
  16.     timer.Enabled = false;  
  17.     timer.Tick += new EventHandler(timer_Tick);  
  18. }  
  19.   
  20. // Timer tickle  
  21. void timer_Tick(object sender, EventArgs e)  
  22. {  
  23.     if (parent == null)  
  24.         return;  
  25.   
  26.     elapsed++;  
  27.     parent.Height = getHeight(elapsed, FORM_DELTA);  
  28. }  


現(xiàn)在我們可以填寫剛才分析的Selecting和Selected事件了。



算法嘗試

目前流行的加減速函數(shù)有很多,,最簡單的從1次函數(shù)(勻速),、2次函數(shù)(勻加速)到3,、4甚至5次函數(shù)都有人在用。這類指數(shù)型的加速函數(shù)使用簡單方便,,用得很多。下面是在Mahematica里繪制的幾種函數(shù)曲線,,從上倒下分別為:g=10的自由落體函數(shù),,y=x^2,y=x^3,,y=x^4和y=x直線(注意:為了讓大家看清函數(shù)細(xì)節(jié),,x和y軸不是1:1的)。

看起來要實現(xiàn)又加速又減速還真是麻煩,,看來只有去掉減速了,。反正山寨嘛,只要「看起來像」就行了,。沒辦法,,我們是搞山寨的,手藝當(dāng)然不行了,,所以到底用那種,,還真的不知道。山寨大法告訴我們,,不知道的東西,,「試,就對了」,。那么就選3個版本的getHeight()來試試,。

作者:野比 ([email protected]

時間:May, 2012



(未完待續(xù))

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多