明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

ASP.NET可交互式位圖窗體設(shè)計(9)

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