一般情況下的標(biāo)注式提示窗口 屏幕邊緣的標(biāo)注式提示窗口 |
一、技術(shù)要點(diǎn)
就像本文開頭所說(shuō)的"標(biāo)注式消息提示窗口"其實(shí)就是一個(gè)具有不規(guī)則外形的窗體,但卻具備了更加復(fù)雜的屬性和行為。標(biāo)注的箭頭會(huì)根據(jù)不同控件指向不同的位置,當(dāng)需要標(biāo)注的控件過(guò)于接近屏幕的邊緣時(shí),標(biāo)注窗口還會(huì)自動(dòng)調(diào)整顯示位置以及箭頭的長(zhǎng)短和大小。
我們?yōu)樾聞?chuàng)建的窗體取名為InfoWindow。在類的頭部定義intArc和intArrowHeight兩個(gè)私有變量,可以適當(dāng)調(diào)整它們的值來(lái)微調(diào)提示窗口的位置和箭頭的大小與位置。
提示窗口的箭頭位置無(wú)非具有左上、右上、左下和右下四個(gè)可能性,我們?yōu)榇硕x了枚舉類型的變量ArrowLocation,根據(jù)提示窗口位于屏幕的不同位置,GetArrowLocation可以計(jì)算提示窗口的位置并且返回適當(dāng)?shù)腁rrowLocation,定義如下:
…… public enum ArrowLocation { TopLeft, TopRight, BottomLeft, BottomRight } |
SetInfoWindowRegion函數(shù)非常重要,它在Form.Load事件即裝載和顯示提示窗體時(shí)被調(diào)用,當(dāng)計(jì)算出新的提示窗口的位置和箭頭顯示位置后,調(diào)用SetBounds將更新后的位置和大小應(yīng)用到提示窗口,gPath是GraphicsPath類型的私有變量,它表示標(biāo)注式窗口的不規(guī)則圖形路徑,該圖行路徑也是根據(jù)提示窗口的位置和箭頭顯示的位置來(lái)創(chuàng)建,gPath.AddArc方法用來(lái)繪制提示窗口四個(gè)邊角的弧度部分,和AddLine方法一起描繪出提示窗口包括箭頭的輪廓,一切就緒后我們就用這個(gè)gPath對(duì)象傳遞給Region對(duì)象,當(dāng)將這個(gè)Region對(duì)象賦給Form窗體的Region屬性后,窗體就具備了標(biāo)注式提示窗口樣式的不規(guī)則外形了,部分代碼如下:
private void SetInfoWindowRegion() { if (!this.IsHandleCreated) return; System.Drawing.Size windowSize = this.Size; Point[] ArrowPoints = new Point[3]; Point topLeftPoint = Point.Empty; Point bottomRightPoint = (Point)windowSize; switch (this.GetArrowLocation) { case ArrowLocation.TopLeft: …… case ArrowLocation.TopRight: …… case ArrowLocation.BottomLeft: …… case ArrowLocation.BottomRight: …… } …… …… if ((this.GetArrowLocation == ArrowLocation.TopLeft) || (this.GetArrowLocation == ArrowLocation.TopRight)) { gPath.AddArc(topLeftPoint.X, rectY2 - arcRadius, arcDia, arcDia, 90, 90); gPath.AddLine(topLeftPoint.X, rectY2, topLeftPoint.X, rectY1); gPath.AddArc(topLeftPoint.X, topLeftPoint.Y, arcDia, arcDia, 180, 90); gPath.AddLine(rectX1, topLeftPoint.Y, ArrowPoints[0].X, topLeftPoint.Y); gPath.AddLines(ArrowPoints); gPath.AddLine(ArrowPoints[2].X, topLeftPoint.Y, rectX2, topLeftPoint.Y); gPath.AddArc(rectX2 - arcRadius, topLeftPoint.Y, arcDia, arcDia, 270, 90); gPath.AddLine(bottomRightPoint.X, rectY1, bottomRightPoint.X, rectY2); gPath.AddArc(rectX2 - arcRadius, rectY2 - arcRadius, arcDia, arcDia, 0, 90); gPath.AddLine(rectX2, bottomRightPoint.Y, rectX1, bottomRightPoint.Y); } else { gPath.AddLine(rectX1, topLeftPoint.Y, rectX2, topLeftPoint.Y); gPath.AddArc(rectX2 - arcRadius, topLeftPoint.Y, arcDia, arcDia, 270, 90); gPath.AddLine(bottomRightPoint.X, rectY1, bottomRightPoint.X, rectY2); gPath.AddArc(rectX2 - arcRadius, rectY2 - arcRadius, arcDia, arcDia, 0, 90); gPath.AddLine(rectX2, bottomRightPoint.Y, ArrowPoints[0].X, bottomRightPoint.Y); gPath.AddLines(ArrowPoints); gPath.AddLine(ArrowPoints[2].X, bottomRightPoint.Y, rectX1, bottomRightPoint.Y); gPath.AddArc(topLeftPoint.X, rectY2 - arcRadius, arcDia, arcDia, 90, 90); gPath.AddLine(topLeftPoint.X, rectY2, topLeftPoint.X, rectY1); gPath.AddArc(topLeftPoint.X, topLeftPoint.Y, arcDia, arcDia, 180, 90); } gPath.CloseFigure(); this.Region = new Region(this.gPath); } |
ShowInfoWindow函數(shù)用來(lái)將提示窗口顯示出來(lái),該函數(shù)需要將提示窗口附著的控件和需要顯示的文本傳遞過(guò)來(lái)。然后,AnchorPointFromControl根據(jù)控件的位置返回提示窗口的箭頭應(yīng)該顯示的坐標(biāo),代碼如下:
public static Point AnchorPointFromControl(Control anchorControl) { if (anchorControl == null) throw new ArgumentException(); Point controlLocation = anchorControl.Location; System.Drawing.Size controlSize = anchorControl.Size; if (anchorControl.Parent != null) controlLocation = anchorControl.Parent.PointToScreen(controlLocation); return controlLocation + new Size(controlSize.Width / 2, controlSize.Height / 2); } |
PointToScreen表明將工作區(qū)點(diǎn)的位置映射成屏幕坐標(biāo)統(tǒng)一進(jìn)行計(jì)算。上述代碼最后以行說(shuō)明提示窗口的箭頭顯示在附著控件的中點(diǎn)。
將提示窗口的背景顏色設(shè)置成Info,外觀如下圖:
我們發(fā)現(xiàn)這樣的外觀有點(diǎn)別扭,沒(méi)錯(cuò)!因?yàn)樘崾敬翱谌鄙俸谏吙颍∷,還需要在窗體的OnPaint事件中添加代碼,如下:
protected override void OnPaint(PaintEventArgs e) { Pen p = new Pen(Color.Black , 2); e.Graphics.DrawPath(p, gPath); base.OnPaint(e); } |
二、程序?qū)崿F(xiàn)
啟動(dòng)Visual Studio 2005,新建Visual C#的Windows 應(yīng)用程序項(xiàng)目,并取名為ShowInfoWindow,添加4個(gè)Button組件、1個(gè)Label組件、1個(gè)textBox組件和3個(gè)Panel組件,其中3個(gè)Button用來(lái)顯示標(biāo)注式消息提示窗口并分別附著在三個(gè)組件之上,代碼如下:
…… private InfoWindow iw; …… private void button1_Click(object sender, EventArgs e) { iw = new InfoWindow(); iw.ShowInfoWindow(label1, "關(guān)于標(biāo)簽組件的提示說(shuō)明。"); } private void button3_Click(object sender, EventArgs e) { iw = new InfoWindow(); iw.ShowInfoWindow(button2, "關(guān)于按鈕組件的提示說(shuō)明。"); } private void button4_Click(object sender, EventArgs e) { iw = new InfoWindow(); iw.ShowInfoWindow(textBox1, "關(guān)于文本框組件的提示說(shuō)明。"); } |
然后,我們?cè)陧?xiàng)目中添加新Windows窗體,取名為InfoWindow,將InfoWindow的BackColor設(shè)為Info,F(xiàn)ormBorderStyle設(shè)為None,將ShowIcon和ShowInTaskbar都設(shè)為False,在窗體上放置1個(gè)Label組件和1個(gè)Button組件,分別用來(lái)顯示消息內(nèi)容和關(guān)閉提示窗口的操作。具體實(shí)現(xiàn)請(qǐng)參見文章附帶的源碼,這里不再詳述。
三、總結(jié)
本文演示了標(biāo)注式消息提示窗口的創(chuàng)建和顯示,利用GraphicsPath對(duì)象、Region對(duì)象以及屏幕坐標(biāo)映射等方法有效的實(shí)現(xiàn)了提示窗口的外觀和樣式,提示窗口可以自動(dòng)附著在相應(yīng)控件之上,并且根據(jù)附著控件在屏幕上的位置自動(dòng)調(diào)整提示窗口箭頭的位置和大小。演示程序在Windows XP SP2以及.Net 框架 2.0環(huán)境下運(yùn)行通過(guò)。