ASP.NET可交互式位圖窗體設(shè)計(jì)(9)
發(fā)表時(shí)間:2024-06-12 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]在頁(yè)面和請(qǐng)求之間傳遞狀態(tài) 為使應(yīng)用程序能夠工作,它需要能夠維護(hù)請(qǐng)求之間的狀態(tài)并將狀態(tài)傳遞給繪圖頁(yè)面(如下所示)。 維護(hù)和傳遞狀態(tài)有多種方式。如果應(yīng)用程序是嚴(yán)格的單頁(yè)面應(yīng)用程序(和以前的應(yīng)用程序一樣),則可以使用視圖狀態(tài),其中數(shù)據(jù)被編碼存儲(chǔ)在 Web 頁(yè)的隱藏輸入字段中。 ...
在頁(yè)面和請(qǐng)求之間傳遞狀態(tài)
為使應(yīng)用程序能夠工作,它需要能夠維護(hù)請(qǐng)求之間的狀態(tài)并將狀態(tài)傳遞給繪圖頁(yè)面(如下所示)。
維護(hù)和傳遞狀態(tài)有多種方式。如果應(yīng)用程序是嚴(yán)格的單頁(yè)面應(yīng)用程序(和以前的應(yīng)用程序一樣),則可以使用視圖狀態(tài),其中數(shù)據(jù)被編碼存儲(chǔ)在 Web 頁(yè)的隱藏輸入字段中。
但是我們的圖像控件是在單獨(dú)的頁(yè)面中進(jìn)行繪圖的,因此需要某些更靈活的東西。最好的選擇就是 cookie 和會(huì)話狀態(tài)。會(huì)話狀態(tài)非常靈活,但要求使用服務(wù)器資源。瀏覽器可以保留 cookie,但其大小非常有限。
Page_Load
Page_Load 是在創(chuàng)建頁(yè)面對(duì)象之后以及在運(yùn)行所有事件處理程序之前被調(diào)用的。因此 Page_Load 方法是加載永久數(shù)據(jù)的理想所在。如果找不到數(shù)據(jù),就創(chuàng)建新的數(shù)據(jù)。以下是相關(guān)代碼:
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
randomGen = ViewState("randomGen")
If randomGen Is Nothing Then randomGen = New Random()
' 選項(xiàng)之一:使用會(huì)話狀態(tài)獲得繪圖列表
'(保存在 Page_Unload 中)
'(注意:要求服務(wù)器上的狀態(tài)存儲(chǔ))
drawingList = Session("drawingList")
If drawingList Is Nothing Then drawingList = New DShapeList()
' 選擇之二:從用戶瀏覽器上的 cookie 中
' 檢索繪圖狀態(tài)
'(注意:不需要服務(wù)器存儲(chǔ),但有些用戶會(huì)禁用 cookie)
'(注意之二:cookie 不會(huì)自動(dòng)反序列化!:( )
' 注意之三:使用 cookie 將限制能夠繪制的形狀數(shù)量
'Dim drawingListCookie As HttpCookie
'drawingListCookie = Request.Cookies("drawingList")
'If drawingListCookie Is Nothing Then
' drawingList = New DShapeList()
'Else
' drawingList = _
' SerialHelper.DeserializeFromBase64String( _
' drawingListCookie.Value)
'End If
End Sub
首先,我們嘗試從視圖狀態(tài)加載隨機(jī)數(shù)發(fā)生器狀態(tài)。如果存在,則使用存儲(chǔ)的值。如果不存在,則創(chuàng)建一個(gè)新的 Random 對(duì)象。
接下來(lái),我們嘗試從會(huì)話狀態(tài)加載繪圖列表。同樣,如果不存在繪圖列表,則創(chuàng)建一個(gè)新的空列表。
如果需要,視圖狀態(tài)和會(huì)話狀態(tài)都會(huì)自動(dòng)序列化我們的對(duì)象。視圖狀態(tài)始終被序列化,因此可以表示為瀏覽器隱藏輸入字段中的編碼的字符串。會(huì)話狀態(tài)當(dāng)存儲(chǔ)在數(shù)據(jù)庫(kù)中或者在服務(wù)器間進(jìn)行傳遞時(shí)被序列化,但是如果應(yīng)用程序運(yùn)行在單個(gè)服務(wù)器上(例如在開(kāi)發(fā)機(jī)器上進(jìn)行測(cè)試時(shí)),則不會(huì)將其序列化。
被注釋的代碼試圖從 cookie 加載繪圖列表。請(qǐng)注意,處理 cookie 要比處理視圖或會(huì)話狀態(tài)復(fù)雜得多。首先就是不能自動(dòng)序列化。為序列化為一個(gè)字符串,我們?cè)谝粋(gè)新類當(dāng)中編寫(xiě)了 helper 函數(shù),如下所示:
Public Shared Function DeserializeFromBase64String( _
ByVal base64String As String) As Object
Dim formatter As New BinaryFormatter()
Dim bytes() As Byte = Convert.FromBase64String(base64String)
Dim serialMemoryStream As New MemoryStream(bytes)
Return formatter.Deserialize(serialMemoryStream)
End Function
Dr. GUI 使用了二進(jìn)制格式化程序并轉(zhuǎn)換為可打印的 base 64 字符串,因?yàn)闊o(wú)論是 SOAP 還是 XML 格式化程序都不適用于此應(yīng)用程序。我們必須從純二進(jìn)制表示轉(zhuǎn)換為 base 64 字符串,以避免因簡(jiǎn)單復(fù)制字節(jié)而產(chǎn)生字符串中控制字符的潛在問(wèn)題。base 64 字符串使用一個(gè)字符 A-Z、a-z、0-9、+ 或 /(共 64 個(gè)或 2^6 個(gè)字符)來(lái)表示二進(jìn)制字符串中的每六位,因此四個(gè)字符表示三個(gè)字節(jié) -- 第一個(gè)字符表示第一個(gè)字節(jié)中的六位,第二個(gè)字符表示第一個(gè)字節(jié)的末兩位和第二個(gè)字節(jié)的前四位,以此類推。同樣,使用 base 64 字符串關(guān)鍵在于可以將字符串限制為可打印字符,這樣就避免了任何控制字符出現(xiàn)潛在問(wèn)題。
XML 格式化程序不會(huì)序列化私有數(shù)據(jù) -- 而 Dr. GUI 也不打算為繪圖列表中的私有數(shù)據(jù)添加公開(kāi)訪問(wèn)權(quán)限。SOAP 格式化程序不存在這種限制,但它不會(huì)序列化空列表以便進(jìn)行反序列化。相反,它不為空列表向數(shù)據(jù)流寫(xiě)入任何東西,這樣當(dāng)嘗試反序列化時(shí)就會(huì)引發(fā)一個(gè)異常。(Dr. GUI 認(rèn)為這是一個(gè)錯(cuò)誤。)
Dr. GUI 更喜歡以可讀的 XML 格式進(jìn)行序列化,但由于兩種 XML 序列化格式化程序都無(wú)法完成此項(xiàng)工作,所以最終選擇了二進(jìn)制格式化程序并轉(zhuǎn)換為 base 64 字符串。
Page_Unload
Page_Unload 是在破壞頁(yè)面對(duì)象(包括任何所包含的數(shù)據(jù))之前被調(diào)用的,因此是永久放置重要數(shù)據(jù)的理想位置,這樣我們便可以在將來(lái)從 Page_Load(或者從圖像的 Page_Load)中取出這些數(shù)據(jù)。
因此,我們將數(shù)據(jù)保存在 Page_Unload 中,并從 Page_Load 中檢索數(shù)據(jù)。雖然這有些奇怪,但卻是正確的。
以下是 Page_Unload 的代碼:
Private Sub Page_Unload(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.PreRender
ViewState("randomGen") = randomGen
' 選項(xiàng)之一:編寫(xiě)會(huì)話狀態(tài)
Session("drawingList") = drawingList
' 選項(xiàng)之二:編寫(xiě)一個(gè) cookie。必須編寫(xiě)代碼進(jìn)行序列化。
' 注意:使用 cookie 將限制能夠繪制的形狀數(shù)量!
'Dim drawingListString As String =
' SerialHelper.SerializeToBase64String(drawingList)
'Response.Cookies.Add(New HttpCookie("drawingList", _
' drawingListString))
End Sub
此代碼稍微有些簡(jiǎn)單,因?yàn)槲覀儾槐夭榭礌顟B(tài)是否已經(jīng)存在于視圖或會(huì)話狀態(tài)對(duì)象中,而只需將其無(wú)條件寫(xiě)出。
同樣,視圖狀態(tài)和會(huì)話狀態(tài)可以自動(dòng)對(duì)自身進(jìn)行序列化,而 cookie 則不能,因此我們需要親自執(zhí)行。Dr. GUI 編寫(xiě)了下面的 helper 函數(shù)(在單獨(dú)的類中),代碼如下所示:
Public Shared Function SerializeToBase64String(ByVal o As Object) _
As String
Dim formatter As New BinaryFormatter()
Dim serialMemoryStream As New MemoryStream()
formatter.Serialize(serialMemoryStream, o)
Dim bytes() As Byte = serialMemoryStream.ToArray()
Return Convert.ToBase64String(bytes)
End Function
在單獨(dú)的頁(yè)面中繪圖
正如前面提到的,繪圖是在單獨(dú)的頁(yè)面中進(jìn)行的。以下是該頁(yè)面的代碼:
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim drawingList As DShapeList
' 獲取繪圖列表選項(xiàng)之一:使用會(huì)話狀態(tài)...
drawingList = Session("drawingList")
If drawingList Is Nothing Then drawingList = New DShapeList()
' 獲取繪圖列表選項(xiàng)之二:使用 cookie...
'(請(qǐng)查看主頁(yè)代碼以了解更多注釋)
'Dim drawingListCookie As HttpCookie
'drawingListCookie = Request.Cookies("drawingList")
'If drawingListCookie Is Nothing Then
'drawingList = New DShapeList()
'Else
' drawingList = _
' SerialHelper.DeserializeFromBase64String( _
' drawingListCookie.Value)
'End If
Response.ContentType = "image/gif"
Dim bitMap As New Bitmap(368, 376)
Dim g As Graphics = Graphics.FromImage(bitMap)
Try
g.Clear(Color.White)
drawingList.DrawList(g)
bitMap.Save(Response.OutputStream, ImageFormat.Gif)
Finally
g.Dispose()
bitMap.Dispose()
End Try
End Sub
首先,我們從會(huì)話狀態(tài)或 cookie 中獲取繪圖列表。(這部分代碼與上面的 Page_Load 方法類似。)
然后,我們將正在編寫(xiě)的響應(yīng)流的 ContentType 設(shè)置為一個(gè) GIF 圖像。
接下來(lái),我們要做一些真正美妙的事情:按照所需的大。ū纠凑张c Windows 窗體應(yīng)用程序中相同的大。﹦(chuàng)建一個(gè)位圖。
然后,我們得到一個(gè)與該位圖相關(guān)聯(lián)的 Graphics 對(duì)象,清除該對(duì)象,并在其中繪制我們的列表。
下面的步驟很重要:接下來(lái),我們將 GIF 格式的圖像內(nèi)容寫(xiě)出到響應(yīng)流(即瀏覽器)中。我們?cè)O(shè)置了這種響應(yīng)類型以確保瀏覽器能夠正確解釋圖像,然后發(fā)送圖像的位。(.NET Framework 使該操作變得相當(dāng)簡(jiǎn)單。而在原來(lái)的 Windows GDI 時(shí)代,僅在位圖上進(jìn)行繪制都是非常痛苦的。
另一個(gè)重要步驟就是要記住清理 Graphics 和 Bitmap 對(duì)象 -- 并使用 Try/Finally,以便即使出現(xiàn)異常也會(huì)清理對(duì)象。
嗨!步驟真多。但是為了讓此應(yīng)用程序能夠作為 ASP.NET 應(yīng)用程序運(yùn)行,還是值得的 -- 并且更好的是,這種應(yīng)用程序不需要依賴客戶端腳本。
試一試!
如果您手頭有 .NET,學(xué)習(xí)它的最好方法就是試一試。如果沒(méi)有,就請(qǐng)想辦法得到。如果您每周在 Dr. GUI .NET 上花費(fèi)一個(gè)小時(shí)左右,那么在了解 .NET 之前您將已經(jīng)成為一名 .NET Framework 專家了。
從您開(kāi)始 -- 并邀請(qǐng)您的朋友!
作為第一個(gè)學(xué)習(xí)新技術(shù)的人,感覺(jué)一定不錯(cuò),但如果和朋友們分享則樂(lè)趣更多!為享受更多樂(lè)趣,邀請(qǐng)朋友共同學(xué)習(xí) .NET 吧!
應(yīng)進(jìn)行的嘗試...
首先試一下這里給出的代碼。其中有一些是從大型程序中節(jié)選下來(lái)的,圍繞這些代碼片斷創(chuàng)建程序會(huì)取得不錯(cuò)的效果。(如果必須,也可以使用 Dr. GUI 提供的代碼。)琢磨一下代碼。
向繪圖程序添加一些不同的形狀,包括填充和不填充的形狀。注意:雖然我們沒(méi)有對(duì)其進(jìn)行轉(zhuǎn)換,但所添加的類可能存在于來(lái)自其他文件的不同程序集中(因而是不同的可執(zhí)行文件)。這意味著所添加的類的語(yǔ)言甚至可以和其他類不同。當(dāng)您閱讀并嘗試有關(guān)的必要工作后,會(huì)更加確信這一點(diǎn)。
請(qǐng)向這里的類添加一些方法,并嘗試改動(dòng)用戶界面。自己制作一個(gè)可愛(ài)的 CAD 小程序。
在自己選擇的項(xiàng)目中使用繼承、abstract/MustInherit 類、接口和多態(tài)。當(dāng)類系列具有很多共同部分時(shí),使用繼承的效果最佳。如果類并不具有很多共同部分,但功能相似,這時(shí)使用接口的效果最好。
嘗試用 ASP.NET 編寫(xiě)自己的繪圖應(yīng)用程序。請(qǐng)注意,如果能夠運(yùn)行 .NET Framework,則只需 Microsoft Internet Information Server (IIS) 便可以在自己的計(jì)算機(jī)上運(yùn)行 ASP.NET -- 無(wú)需服務(wù)器!Dr. GUI 認(rèn)為,在便攜式計(jì)算機(jī)上僅使用標(biāo)準(zhǔn)操作系統(tǒng)和免費(fèi)的 .NET Framework 創(chuàng)建和測(cè)試 Web 應(yīng)用程序,感覺(jué)實(shí)在好極了。(但 Dr. GUI 還是傾向于使用 Visual Studio,或者至少 Visual Basic 或 Microsoft Visual C#? 標(biāo)準(zhǔn)版。)看看吧!不需要服務(wù)器!甚至不需要 Internet 連接。ǹ稍 Brand J 的擴(kuò)展版上嘗試…)