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