国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
利用 ASP.NET、JavaScript 和 OLE DB 從頭設(shè)計您自己的網(wǎng)絡(luò)日記應(yīng)用程序
發(fā)布日期: 1/13/2005 | 更新日期: 1/13/2005
Marco Bellinaso
本文假設(shè)您熟悉 Visual Basic .NET 和 JavaScript
下載本文的代碼:Blogging.exe (151KB)
摘要
多數(shù)情況下,ASP.NET 高級模板化控件(如 DataList 和 DataGrid)是用于數(shù)據(jù)表示的最佳選擇。但是,當需要靈活地進行各種各樣的布局時,Repeater 控件就是您所需要的。在本文中,作者將構(gòu)建一個功能齊備的網(wǎng)絡(luò)日記應(yīng)用程序,以舉例說明使用 Repeater 和 DataList 控件來呈現(xiàn)主從關(guān)系中嵌套數(shù)據(jù)的方法。然后,作者將介紹如何通過添加一些使網(wǎng)絡(luò)日記反應(yīng)更迅速且可用性更高的客戶端 JavaScript 代碼,來替代這些控件的默認實現(xiàn)。
如今,似乎每個人都需要網(wǎng)絡(luò)日記,我知道我自己就是這樣的。但是我找不到具有我想要的功能的預(yù)建 ASP.NET 網(wǎng)絡(luò)日記代碼,所以我構(gòu)建了自己的代碼。在構(gòu)建自己的網(wǎng)絡(luò)應(yīng)用程序時,最重要的一點是,要大量用到 ASP.NET 服務(wù)器控件,例如 Repeater、DataList 和 Calendar。網(wǎng)絡(luò)日記應(yīng)用程序乍看上去似乎就是一個簡單的練習,但是實際上,它要求您在一個典型的報告應(yīng)用程序中實現(xiàn)很多需要的功能,如構(gòu)建并呈現(xiàn)主從關(guān)系或編輯和刪除記錄,隱藏或顯示登錄用戶的內(nèi)容和控件,以及管理在同一頁面上多個虛擬窗體的輸入驗證。本文將介紹網(wǎng)絡(luò)日記的設(shè)計和實現(xiàn)細節(jié),并對可輕松應(yīng)用到各種 ASP.NET 項目的技術(shù)進行闡述,而暫且不考慮構(gòu)建這些網(wǎng)絡(luò)日記的目的是出于業(yè)務(wù)需要還是為了娛樂。
在開始編碼工作之前,您應(yīng)該確定想要構(gòu)建的網(wǎng)絡(luò)日記的類型、它應(yīng)具有的功能以及數(shù)據(jù)存儲的設(shè)計方式。有效的網(wǎng)絡(luò)日記包括許多功能。網(wǎng)絡(luò)日記的消息應(yīng)按照從新到舊的順序進行顯示。在同一天內(nèi)可以張貼多條消息,這些消息應(yīng)直觀地分組顯示于表格或框中,但是仍然可以按照張貼的時間順序?qū)ζ溥M行識別。同時,用戶應(yīng)當能夠為她希望閱讀的條目選擇時間間隔。這一點非常重要,因為您并不希望檢索用戶已經(jīng)看過的舊內(nèi)容。
用戶應(yīng)該能夠?qū)θ我庖粭l消息進行評注,并且張貼的評注應(yīng)該能夠直接在其父消息之下進行顯示,從而條理清晰。此外,網(wǎng)絡(luò)日記的所有者應(yīng)該能夠張貼、編輯并刪除消息和評注,而用戶應(yīng)該只能閱讀消息和張貼評注。要根據(jù)用戶身份來決定允許或禁止其進行張貼或編輯操作,需要顯示或隱藏某些控件,并且還需要進行某種形式的身份驗證。
數(shù)據(jù)庫設(shè)計
接下來,必須您必須確定消息和評注的存儲方式。在本項目中,我使用了 SQL Server™ 數(shù)據(jù)庫,但我的示例代碼還包含一個 Microsoft® Access 數(shù)據(jù)庫,以防您選擇這種數(shù)據(jù)儲存方式。該數(shù)據(jù)庫只包含兩個表:一個是消息表,另一個是評注表。消息表儲存唯一的 ID、可選標題或消息摘要、消息文本以及張貼消息的日期和時間。評注表儲存唯一的 ID、評注所對應(yīng)消息的 ID、作者的名稱、作者的電子郵件地址、評注文本以及張貼消息的日期和時間。圖 1 顯示這兩個表的設(shè)計。
圖 1 父表/子表
您可以看到,兩表之間是一對多的關(guān)系,并通過 MessageID 字段進行鏈接,這樣,通過級聯(lián)更新和刪除也加強了引用完整性。請注意,兩表的名稱都以前綴“Blog_”開頭。我一直在自己的 SQL Server 表中使用前綴,這是因為當將這些表按字母順序列出時,它們在 Enterprise Manager 中將分組在一起。此外,Web 宿主計劃通常只提供一個 SQL Server 數(shù)據(jù)庫,您必須在使用的所有 Web 模塊間共享該數(shù)據(jù)庫。如果不使用表前綴,而且某個模塊可能已經(jīng)使用一個名為 Message 的表,那么在部署時無法立即解決此種名稱沖突問題。
另一個要切記的重要細節(jié)是,任何字段都不能是空值,即使用戶將該字段保留為空白。為了防止有未處理的異常出現(xiàn),您應(yīng)該將空字段設(shè)置為空字符串,而不是允許出現(xiàn)空值。如果使用 Web 窗體進行數(shù)據(jù)輸入,則不會有任何問題,這是因為如果沒有內(nèi)容輸入,控件將返回一個空字符串。
業(yè)務(wù)層
在典型的以數(shù)據(jù)庫為中心的應(yīng)用程序中,具有數(shù)據(jù)層、業(yè)務(wù)層和表示層。數(shù)據(jù)層可能由一組存儲過程組成,而業(yè)務(wù)層由一組類(在其各自的單獨程序集中進行選擇性地編譯)組成,這些類包裝存儲過程以確保數(shù)據(jù)的完整性、執(zhí)行驗證以及對其他的業(yè)務(wù)規(guī)則進行強制。但是,在本項目中,我決定不使用存儲過程;我直接將 SQL 查詢和命令硬編碼到主程序集中,以便能更容易地通過 Access 來使用網(wǎng)絡(luò)日記。我也沒有將數(shù)據(jù)層和業(yè)務(wù)層使用的類進行分離,但是,由于只有兩個表和一些簡單的業(yè)務(wù)規(guī)則,因此我創(chuàng)建了一個負責驗證輸入并執(zhí)行正確的 SQL 語句的單個的類。所有代碼以及表示層都將位于同一個程序集內(nèi),因為我不希望用使用業(yè)務(wù)類的程序來更新該類,也不希望用其他類型的應(yīng)用程序或者在多個客戶端之間分布該業(yè)務(wù)類。因此,利用主 ASP.NET 應(yīng)用程序(稱為 WebLogger)對其進行編譯即可。該業(yè)務(wù)類被命名為 Blog,位于名稱為“Business”的命名空間下,所以它不會與代碼隱藏類發(fā)生沖突。以下就是添加一條新消息的方法的實現(xiàn)方式:
Public Sub InsertMessage(ByVal title As String, ByVal message As String)Dim cmd As New OleDbCommand("INSERT INTO Blog_Messages (Title, _Message) VALUES (?, ?)")cmd.Parameters.Add("Title", OleDbType.VarChar).Value = titlecmd.Parameters.Add("Message", OleDbType.LongVarChar).Value = _message.Replace(m_brChar, "<br>")ExecuteCommand(cmd)End Sub
該方法接受添加新記錄所需要的所有值。不需要在消息 ID 中傳值,因為它在數(shù)據(jù)庫表中是自動進行遞增的,并且 AddedDate 將當前日期作為默認值。該方法定義一個 SQL INSERT 命令,并用 ExecuteCommand Helper 方法執(zhí)行該命令。請注意,Message 參數(shù)的值就是作為輸入進行傳遞的消息文本,但是,換行符將被替換為 HTML 的
標記。網(wǎng)絡(luò)管理員可以在進行張貼時使用 HTML 格式,但是,這是因為經(jīng)常會出現(xiàn)新行,所以張貼的內(nèi)容將自動轉(zhuǎn)換為 HTML 格式,從而無需進行鍵入操作。您可能已經(jīng)猜測到了,ExecuteCommand Helper 方法只執(zhí)行作為輸入而接受的命令對象:
Private Sub ExecuteCommand(ByVal cmd As OleDbCommand)cmd.Connection = m_ConnectionTrym_Connection.Open()cmd.ExecuteNonQuery()Finallym_Connection.Close()End TryEnd Sub
ExecuteCommand 方法設(shè)置命令的連接;通過從 web.config 的 appSettings 部分定義的自定義項中檢索連接字符串,在類的構(gòu)造函數(shù)方法中可創(chuàng)建該連接;然后打開這個連接,在 Try 塊中執(zhí)行該命令,并在對應(yīng)的 Finally 塊中關(guān)閉該連接。即使 ExecuteNonQuery 方法引發(fā)異常,該連接也將關(guān)閉??墒菦]有 Catch 塊,因為我不必執(zhí)行任何特定的業(yè)務(wù)操作,如返回交易或記錄一個錯誤。例如,我只想直接在調(diào)用頁中捕獲并處理這樣的異常以顯示一個用戶友好的錯誤消息。因為其他的 Insertxxx、Updatexxx 和 Deletexxx 方法操作類似,所以我將不逐一介紹。但是,研究一下 InsertComment 方法還是很有價值的,因為除了在 Blog_Comments 表中添加新的記錄外,它還發(fā)送一封通知電子郵件,該郵件包含評注文本和一些涉及網(wǎng)絡(luò)日記所有者的消息。
有了這些功能,網(wǎng)絡(luò)日記所有者不必頻繁地加載網(wǎng)絡(luò)日記以及檢查新的電子郵件。當然,如果該網(wǎng)絡(luò)日記非常受歡迎,并獲得很多評注,您就有可能收到過多的電子郵件。因此,應(yīng)用程序的 web.config 應(yīng)該具備一個自定義項,它使管理員能夠決定她是否想要有關(guān)任何新評注的電子郵件通知。然后,需要另一個自定義項來儲存電子郵件的目標地址。這兩項設(shè)置分別命名為 Blog_SendNotifications 和 Blog_AdminEmail,它們在 web.config 的 appSettings 部分(與連接字符串的自定義項一起)內(nèi)的聲明如下:
<add key="Blog_SendNotifications" value="1" /><add key="Blog_AdminEmail" value="mbellinaso@vb2themax.com" />
Blog_SendNotifications 的值為 1 表示通知功能被激活,其他值或該項空則表示該功能沒有被激活。
插入新評注并檢查評注通知是否打開之后,您必須構(gòu)建電子郵件的主體。除了評注文本之外,您還希望包含張貼的父消息的標題、日期和時間,這樣在原始消息和評注間就有了明確的連接。通過執(zhí)行圖 2 中的代碼來檢索父消息的數(shù)據(jù),即利用一條命令從正在討論的單條消息記錄中檢索適當字段。
一旦具備了所有必需的數(shù)據(jù),您就可以構(gòu)建并發(fā)送電子郵件消息了。通過調(diào)用 String.Format 方法來構(gòu)建電子郵件文本,該方法包括一個帶數(shù)字占位符的模板字符串(形式是 {n}),并將模板中的各種占位符用值代替:
‘ build the msg‘s contentDim msg As String = String.Format( _"{0} (email: {1}) has just posted a comment the message ""{2}"" " & _"of your BLOG that you posted at {3}. Here‘s the comment:{4}{4}{5}", _author, email, msgTitle, msgDate, Environment.NewLine, comment)‘ send the mailSystem.Web.Mail.SmtpMail.Send("WebLogger", _ConfigurationSettings.AppSettings("Blog_AdminEmail"), _"New comment notification", msg)End If
業(yè)務(wù)類的最重要的組成元素是 GetData 方法,該方法對指定時間間隔內(nèi)張貼的消息和各自的評注進行檢索。這里您必須選擇是使用 DataReader 還是使用 DataAdapter 以及 DataSet 來檢索和讀取數(shù)據(jù)。在 Web 應(yīng)用程序中,DataReader 通常是最好的選擇,特別是不需要進行本地編輯和緩存數(shù)據(jù)副本時。但是,在這種特定情況下,我選擇 DataSet 是因為它允許存儲多個表并在這些表之間創(chuàng)建父子關(guān)系。這樣一次就可以輕松地檢索所有的父記錄和子記錄,并且無需執(zhí)行單獨的查詢就可從父記錄定位到它們的子數(shù)據(jù),而如果我使用 DataReader 方法則需要執(zhí)行單獨的查詢。這種方法對于程序員來說更加簡單,并節(jié)省了數(shù)據(jù)庫資源和網(wǎng)絡(luò)通信量,這是因為發(fā)送到數(shù)據(jù)庫的 SQL 語句的數(shù)量更少了。
能夠在表間創(chuàng)建關(guān)聯(lián)是另一個絕妙的功能,因為您可以向表中添加計算列,這些列利用關(guān)聯(lián)中對其他表的引用計算表達式的值。我通過 MessageID 字段在 Blog_Messages 和 Blog_Comments 表間創(chuàng)建了關(guān)聯(lián),正如我在圖 1 中為物理數(shù)據(jù)庫創(chuàng)建的關(guān)聯(lián)一樣。我希望其數(shù)據(jù)是來自 Blog_Messages 的 DataTable 中有一個計算列,該列返回對應(yīng)消息記錄的子評注的數(shù)量。如果我使用 DataReader,這將需要單獨的查詢或至少一個子查詢(這在 Access 中是不可能的)。對于 DataSet,可以利用無連接的數(shù)據(jù)副本來實現(xiàn)計數(shù)子記錄的數(shù)量,而無需要求數(shù)據(jù)庫來完成。
我們來看看該方法是如何工作的。它將兩個日期作為輸入,利用 DataAdapter 來檢索在該時間間隔中張貼的消息,然后把它們儲存在名稱為 Messages 的 DataSet 表中。如下所示:
Public Function GetData(ByVal fromDate As Date, ByVal toDate As Date) _As DataSetDim ds As New DataSet()intervalDim da As New OleDbDataAdapter("SELECT * FROM Blog_Messages WHERE" & _"AddedDate BETWEEN ? AND ?" & _"ORDER BY AddedDate DESC", m_Connection)da.SelectCommand.Parameters.Add("FromDate", OleDbType.Date).Value = _fromDateda.SelectCommand.Parameters.Add("ToDate", OleDbType.Date).Value = _toDate.AddDays(1)m_Connection.Open()da.Fill(ds, "Messages")•••
請注意,必須將終止日期增加一天,以便將傳入終止日期那天張貼的消息包括在 resultset 中。如果您只想獲得返回消息的評注,那么可以在 SELECT 語句中使用 IN 篩選器,該篩選器包含由逗號分隔的記錄的所有消息 ID 列表,這些記錄由我剛剛列示的查詢 1 返回(參見圖 3)。
然后,在兩表間創(chuàng)建剛剛介紹過的關(guān)聯(lián):
ds.Relations.Add(New DataRelation("MsgComments", _ds.Tables("Messages").Columns("MessageID"), _ds.Tables("Comments").Columns("MessageID")))
下一步,向 Messages 表中添加計算列,該列返回子評注的數(shù)量:
ds.Tables("Messages").Columns.Add("CommentsCount", _GetType(Integer), "Count(Child(MsgComments).CommentID)")
函數(shù) Child(MsgComments) 返回指定關(guān)聯(lián)的所有子數(shù)據(jù)行,Count 函數(shù)與其在 SQL 中的工作方式相同。
要注意的最后一個細節(jié)是,在返回填充的 DataSet 并終止該函數(shù)之前,如果 Comments 表是空的,則該計算列將表達式計算為 NULL(而不是 0),而以后在 ASP.NET 頁中顯示該值或在其他表達式中使用該值時,將會出現(xiàn)問題。要解決該問題,可以添加不涉及任何消息的假評注:
If ds.Tables("Comments").Rows.Count = 0 ThenDim dr As DataRow = ds.Tables("Comments").NewRow()dr("CommentID") = -1dr("Author") = "none"dr("Email") = "none"dr("Comment") = "none"dr("AddedDate") = Date.Todayds.Tables("Comments").Rows.Add(dr)dr.AcceptChanges()End IfReturn dsEnd Function
這樣,如果消息沒有任何子評注,則表達式計算為 0。
顯示消息和子評注
現(xiàn)在業(yè)務(wù)層已經(jīng)完成了,下一步就是編寫表示層,在該層上可以很好地發(fā)揮 ASP.NET 的功能。大多數(shù)工作是在單個頁(即 Default.aspx)上進行的。該頁將顯示消息和評注,并允許經(jīng)過身份驗證的管理員對評注進行適度調(diào)整并編輯她自己的消息(參見圖 4 中的底部窗格)。
圖 4 管理員模式下的網(wǎng)絡(luò)日記
Default.aspx 顯示了一個按日期分組的消息列表,每條消息可以沒有評注或者有多條評注。最初,評注是隱藏的,但當用戶單擊 View 鏈接時,評注的內(nèi)部列表就會動態(tài)地展開或者折疊。多條消息可以同時展開它們的評注列表,就像一個只有單級子節(jié)點的 TreeView 控件。在開發(fā)該頁時,會面臨一些挑戰(zhàn),包括如何根據(jù)日期排序消息、如何在單獨的 HTML 表中對其進行分組以及如何顯示或隱藏子記錄的內(nèi)部列表。
讓我們從第一個問題開始。假設(shè)您希望在該頁面中顯示 5 條消息,前三條消息在同一天張貼,而其他兩條在另一天張貼。同時,假設(shè)您希望將每一條消息最初都隱藏在自己的 HTML 表中。如果您希望在兩個單獨表格中將前三條消息分為一組,而將剩余的兩條分為一組,則您應(yīng)當從該表中間的消息(不是當天第一條或最后一條消息)中刪除表打開和關(guān)閉標記,從第一個消息的表中刪除關(guān)閉標記,從最后一條消息的表中刪除打開標記。因為我想使用一個模板化數(shù)據(jù)綁定的控件來表示該視圖,并且由于必須要完全控制所有生成的 HTML,因此我選擇使用 Repeater 控件。除了您在 Repeater 的模板中指定,該控件沒有預(yù)定義任何輸出或者布局。圖 5 中的代碼是 Repeater 模板的部分定義。
HeaderTemplate 包括表的打開標記,而 FooterTemplate 用于關(guān)閉該表。打開和關(guān)閉各種按日期分組消息的表的標記的定義,位于 ItemTemplate 部分內(nèi)的 Literal 控件中。通過切換 Literal 控件的可視性,您可以決定是否輸出這些標記,從而決定何時關(guān)閉當前的表并打開一個新表。問題在于,何時隱藏 Literal 以便保持當前表為打開狀態(tài)并且對消息進行分組,以及何時顯示 Literal 從而關(guān)閉該表并打開一個新表。
答案很簡單:只要正在處理中的消息的日期與前一條消息的日期相同,就將這些消息分為一組。在日期更改時,會顯示 Literal 控件并啟動一個新組。另一個問題是,應(yīng)該在何時、何地顯示控件的內(nèi)容。當在 Repeater的 ItemDataBound 事件中處理數(shù)據(jù)項(網(wǎng)絡(luò)日記的消息記錄)并將其綁定到 Repeater 的模板中時,必須對此做出決定。這里您可以讀取當前數(shù)據(jù)項的所有值,并將這些值與前一條消息的數(shù)據(jù)進行比較,這些數(shù)據(jù)已存儲在一個靜態(tài)變量中以便在方法調(diào)用間保留該值。獲得對模板的 Literal 控件的引用后,就可以設(shè)置其相應(yīng)的可視性了。代碼如下:
Private Sub Blog_ItemDataBound(...) Handles Blog.ItemDataBoundStatic prevDayDate As DateIf e.Item.ItemType <> ListItemType.Item AndAlso _e.Item.ItemType <> ListItemType.AlternatingItem Then ReturnDim dayDate As Date = e.Item.DataItem("AddedDate")Dim isNewDay As Boolean = (dayDate.ToShortDateString() <> _prevDayDate.ToShortDateString())prevDayDate = dayDateCType(e.Item.FindControl("DayTitle"), Panel).Visible = isNewDayCType(e.Item.FindControl("DayBox"), Literal).Visible = ( _isNewDay AndAlso e.Item.ItemIndex > 0)End Sub
如您所見,我為 Panel 控件設(shè)置了 Visible 屬性(在同一個模板中也有聲明),該屬性顯示當前表的日期。如果當前消息的日期和前一條消息的日期不同,則顯示該面板;或者如果是綁定的第一條消息,則顯示默認的日期。將 Literal 的可視性設(shè)置為 True 要受另一個條件約束:被綁定的消息必須不是第一條消息,因為在此種情況下,利用在 Repeater 的 HeaderTemplate 部分中聲明的標記可以打開當天的表。要動態(tài)地折疊和展開評注列表而無需回發(fā)給服務(wù)器并且不重新處理頁面,請將評注放置到標記內(nèi),該標記的顯示樣式可以設(shè)置為“none”,或者設(shè)置為一個空字符串來分別隱藏或顯示它。將 DIV 聲明如下,根據(jù)綁定的數(shù)據(jù)項給其分配一個 ID:
<div style="display:‘none‘; margin-left:2.0em; margin-top:.8em; "ID=‘<%# "div" & Container.DataItem("MessageID") %>‘><!-- put here the Comments DataList... --></div>
我這樣進行分配,以便對于每個 DIV 都有唯一的 ID(切記每條消息都有一個)。為了展開或者折疊 DIV,我使用了超級鏈接,該鏈接調(diào)用以 DIV 的 ID 作為輸入的自定義 JavaScript 代碼:
<asp:HyperLink Runat="server"Visible=‘<%# Container.DataItem("CommentsCount") > 0 %>‘NavigateUrl=‘<%# "javascript:ToggleDivState(div" & _Container.DataItem("MessageID") & ");" %>‘>View</asp:HyperLink>
同時還要注意,Visible 屬性與一個表達式綁定,只有在消息有評注顯示(如果其 CommentsCount 計算列的值大于 0)時該表達式才返回 True。ToggleDivState 只是將 DIV 的顯示樣式值取反,從而使其可見或隱藏:
ffunction ToggleDivState(ctrl){div = eval(ctrl);if (div.style.display == "none")div.style.display = "";elsediv.style.display = "none";}
現(xiàn)在我們來看一下評注功能。這次由 DataList 來完成這項工作,因為它的表布局正是我所需的。一般情況下,模板控件或者任何其他數(shù)據(jù)綁定列表控件的 DataSource 屬性都可以通過代碼隱藏(或者服務(wù)器端腳本)以編程方式進行指派。但是,在這種情況下我沒有直接引用 DataList,因為它是由父 Repeater 動態(tài)創(chuàng)建的。雖然和大多數(shù)屬性一樣,它可以有一個在運行時設(shè)置其值的數(shù)據(jù)綁定表達式。如果您使用 DataReader 方法來檢索數(shù)據(jù),可以將 DataSource 屬性綁定到一個自定義方法,該方法接受消息的 ID 并返回 DataTable、DataReader 或者任何其他實現(xiàn) IEnumerable 接口的數(shù)據(jù)類型的子評注。在這里無需這樣做,因為您需要的所有數(shù)據(jù)都已存儲在包含消息的同一 DataSet 中。由于已經(jīng)在消息表和評注表之間創(chuàng)建了關(guān)聯(lián),所以您就可以輕松地利用當前數(shù)據(jù)項的 DataRow 的 GetChildRows 方法來檢索子評注數(shù)組。表達式聲明如下:
<asp:DataList Runat="server" DataSource=‘<%# Container.DataItem.Row.GetChildRows("MsgComments") %>‘>
利用綁定表達式完成 DataList 的 ItemTemplate,以顯示作者名稱和電子郵件地址、消息文本、消息日期以及完整輸出該網(wǎng)絡(luò)日記內(nèi)容的代碼。圖 6 顯示了輸出模塊的完成代碼。
選擇時間間隔并加載網(wǎng)絡(luò)日記
至此我已經(jīng)介紹了 ASPX 文件中頁面內(nèi)容的定義,但是還沒有介紹實際加載網(wǎng)絡(luò)日記內(nèi)容的代碼。為了讓用戶選擇一個時間間隔,我使用了 Calendar 控件,將它的 SelectionMode 屬性設(shè)置為 DayWeekMonth,這樣用戶就可以選擇一天、一周或者整月。例如,如果用戶想選擇最后兩周或者最后的 45 天,提供文本框讓用戶來填寫希望的起始和終止日期是個不錯的主意。圖 7 顯示了向頁面添加的新控件,在 Calendar 中選定了整周。
圖 7 時間間隔
由于回發(fā)而沒有加載該頁面時,必須選擇一個默認的時間間隔,例如上一周。但是,對于頻繁更新的網(wǎng)絡(luò)日記,最好只加載少數(shù)幾天的數(shù)據(jù),對于很少更新的網(wǎng)絡(luò)日記,最好加載上個月整月的數(shù)據(jù)。至于評注通知功能,最好留給網(wǎng)絡(luò)日記管理員用 web.congfig 文件中的自定義項進行選擇,該項允許管理員指定默認的時間間隔天數(shù)。下面的代碼說明了如何從文件中讀取該自定義項、如何將其分析為整數(shù)、如何用它來計算最近 n 天的時間間隔以及如何在日歷中突出顯示該時間間隔:
Private Sub Page_Load(...) Handles MyBase.LoadIf Not IsPostBack ThenDim defPeriod As Integer = Integer.Parse( _ConfigurationSettings.AppSettings("Blog_DefaultPeriod"))Dim fromDate = Date.Today.Subtract(New TimeSpan(defPeriod -_1,0,0,0))BlogCalendar.SelectedDates.SelectRange(fromDate, Date.Today)BindData()End IfEnd Sub
通過從今天的日期中減去 n-1 天,可以計算出起始日期。調(diào)用 BindData 可以加載選定時間間隔的數(shù)據(jù),并將該數(shù)據(jù)綁定到 Repeater 及其內(nèi)部控件。該方法調(diào)用以前開發(fā)的網(wǎng)絡(luò)日記業(yè)務(wù)類的 GetData 方法,并傳入在日歷上選定的起始和終止日期,從 SelectedDates 集合中讀取這些日期:
Private Sub BindData()Dim ds As DataSet = m_BlogManager.GetData( _BlogCalendar.SelectedDates(0), _BlogCalendar.SelectedDates(BlogCalendar.SelectedDates.Count - 1))Blog.DataSource = ds.Tables("Messages").DefaultViewBlog.DataBind()End Sub
如果選擇一天,SelectedDates 將只有一個條目,而且起始和終止日期相同。當用戶單擊日歷時,該頁面被回發(fā),并自動選擇新的時間間隔,處理日歷的 SelectionChanged 事件從而為新的時間間隔再次調(diào)用 BindDatahe。最后,您必須處理 Load 按鈕的 Click 事件以在日歷中選擇指定的自定義時間間隔并加載網(wǎng)絡(luò)日記數(shù)據(jù)。在該事件過程中,我對兩個輸入控件的內(nèi)容進行了分析并獲得了兩個日期。雖然我最終會添加驗證程序來確保在提交窗體前數(shù)據(jù)格式是正確的,但如果數(shù)據(jù)格式無效,這種分析仍會引發(fā)異常。在這種情況下,我采用了今天的日期(參見圖 8)。
請注意,使用日歷的 VisibleDate 來確保終止日期在日歷中可見。這是必要的,因為如果用戶選擇過去的兩個月,則這種選擇在日歷中將無法顯示。日歷將顯示當前月,多數(shù)人對這并不十分清楚。
彈出式日歷
按照當前的情況,如果用戶想選擇的時間間隔不是一天、一周或者一月,那么他們就必須在兩個文本框中手工輸入起始和終止日期,并引用一個外部日歷。另外,他們輸入的日期格式也可能無效。由于這些原因,提供一個彈出式日歷是一個不錯的做法。當單擊某個日期時,該日歷應(yīng)該關(guān)閉并且日期應(yīng)該會出現(xiàn)在主窗口的文本框控件中。使用 Calendar 控件和幾個客戶端 JavaScript,就可在 ASP.NET 中輕松地重現(xiàn)該功能。
我們首先關(guān)注父窗口的 ASPX 代碼。通過調(diào)用下面的 JavaScript 函數(shù),我添加了一個打開彈出式窗口的圖像鏈接:
<a href="javascript:PopupPicker(‘IntervalFrom‘, 200, 200);" ><img src="images/calendar.gif" border="0" ></a>
JavaScript 過程希望接收文本框控件的名稱(該文本框由選定的日期來填充)以及要打開的彈出式日歷窗口的寬度和高度。下面是 JavaScript 代碼,這些代碼位于已在該頁面頂部定義的 <script> 部分中:
function PopupPicker(ctl,w,h){var PopupWindow=null;settings=‘width=‘+ w + ‘,height=‘+ h;PopupWindow=window.open(‘DatePicker.aspx?Ctl=‘ +ctl,‘DatePicker‘,settings);PopupWindow.focus();}
該過程利用 window.open 打開一個指定大小的且不能更改的彈出式窗口,該窗口沒有滾動條、菜單、工具欄或狀態(tài)欄。第一個參數(shù)是要加載到新窗口中的頁的 URL,在剛才顯示的代碼中,它利用查詢字符串中的 ctl 參數(shù)來加載 DatePicker.aspx 頁,該參數(shù)的值作為輸入傳入 PopupPicker 過程。
現(xiàn)在完成了主頁,我必需編寫呈現(xiàn)日歷的 DatePicker.aspx 頁。該頁有一個 Calendar 控件,其 Width 和 Height 屬性設(shè)置為 100%,這樣它可以覆蓋整個頁。ASPX 文件中需要注意的其他重要事項是,客戶端 JavaScript 過程將字符串作為輸入并將它用作父窗體的輸入控件的值,該控件的名稱用查詢字符串進行傳遞。最后,它關(guān)閉彈出式窗體自身。JavaScript 代碼如圖 9 所示。
當用戶單擊 Calendar 控件中的鏈接時,我希望調(diào)用自定義的 JavaScript 過程,而不是正常的處理方法,即提交窗體并選擇單擊的日期。默認情況下,所有 Calendar 控件提供的鏈接都會向服務(wù)器產(chǎn)生一個回發(fā)。而我所希望的是讓它們指向自定義的 SetDate 過程。由于 Calendar 的 DayRender 事件,使得更改包含日期鏈接的表單元格的默認輸出非常簡單,該事件在每次呈現(xiàn)日期時發(fā)生并提供對要創(chuàng)建的表單元格的引用。下面的代碼片段利用我自己的超級鏈接控件來替換默認的單元格內(nèi)容,它們具有相同的文本,只是我的控件指向了 JavaScript 過程:
Private Sub DatePicker_DayRender(...) Handles DatePicker.DayRenderDim hl As New HyperLink()hl.Text = CType(e.Cell.Controls(0), LiteralControl).Texthl.NavigateUrl = "javascript:SetDate(‘" & _e.Day.Date.ToShortDateString() & "‘);"e.Cell.Controls.Clear()e.Cell.Controls.Add(hl)End Sub
傳遞給 JavaScript 過程的值是以短格式(一般是 mm/dd/yy)表示的單擊日的日期。該值將用于父窗體上的輸入控件。圖 10 所示為彈出的窗口。
圖 10 彈出式日歷
正如您所見,可以對 ASP.NET 服務(wù)器控件進行極其靈活的自定義。希望以這種方式使用 DayRender 事件的另一種情況是,您需要重新定向到另一個網(wǎng)頁并以查詢字符串傳遞日期時,而不是在該日期回發(fā)后從服務(wù)器重新定向到第二個網(wǎng)頁。為此,只要用類似下面的代碼來替換設(shè)置超級鏈接的 NavigateUrl 屬性的那一行代碼即可:
hl.NavigateUrl = "SecondPage.aspx?Date=" & e.Day.Date.ToShortDateString()
張貼評注
在圖 4 中,您可以看到每條消息的下面都有一個 "Post your own comment" 的鏈接。當用戶單擊它時,就會出現(xiàn)一個帶輸入控件的框用于張貼評注。您可以猜出它是如何工作的,因為我在構(gòu)建可折疊評注列表時使用了同樣的技術(shù)。該帶輸入控件的 Comments 框在 DIV 中進行聲明,它的顯示樣式最初設(shè)置為 "none",因而它是不可見的。當單擊該鏈接時,顯示樣式被更改,頁面滾動到底部從而使它可見。我在頁面的底部定義了一個單個的評注框(不是一條消息一個)以避免發(fā)送不必要的會使網(wǎng)頁速度變慢的 HTML 代碼。圖 11 顯示了評注框和所需的輸入控件。
圖 11 張貼評注
如何指定是為哪一條消息張貼評注?一個不錯的解決方案是當單擊 "Post your own comment" 鏈接時,將父消息的 ID 存儲到一個隱藏的 ASP.NET 文本框中。隨后,在單擊 Post 按鈕時,可以從 codebehind 中檢索該值。請注意,不能使用 Visible 屬性來隱藏該控件,因為當將 Visible 設(shè)置為 False 時,會隱藏該控件,并且根本不會將 HTML 代碼發(fā)送給客戶端。必須使用與 DIV 所用相同的顯示樣式。DIV 和文本框的聲明如下:
<div id="CommentBox" style="DISPLAY: none"><a name="CommentBoxAnchor"></a><asp:textbox id="ParentMessageID" style="DISPLAY: none"runat="server" />
請注意,我還使用了一個定位點,用它來確保在頁面很長并且用戶想要評注的消息位于頁面頂端時,評注框確實可見。該鏈接聲明如下:
<a href=‘<%# "javascript:ShowCommentBox(" &Container.DataItem("MessageID") & ");" %>‘>Post your own comment</a>
ShowCommentBox JavaScript 例程將接受要評注的消息的 ID,并將它用作剛剛聲明的隱藏文本框控件的值:
function ShowCommentBox(msgID){document.forms[0].ParentMessageID.value = msgID;ShowCommentBox2();}
真正使評注框可見并且向下滾動頁面的代碼是一個獨立的例程(ShowCommentBox2 過程)。當希望顯示該評注框而不設(shè)置隱藏文本框控件的值屬性時,我將再次調(diào)用該過程:
function ShowCommentBox2(){CommentBox.style.display = "";window.location.href = ‘#CommentBoxAnchor‘;}
剩下的所有工作就是處理 Post 按鈕上的單擊,以調(diào)用 Business.Blog 實例的 InsertComment 并再次將更新的數(shù)據(jù)綁定到 Repeater:
Private Sub PostComment_Click(...) Handles PostComment.Clickm_BlogManager.InsertComment(Integer.Parse(ParentMessageID.Text), _Author.Text, Email.Text, Comment.Text)BindData()‘ reset the value of the input controlsParentMessageID.Text = ""‘ reset the other visible textboxes...End Sub
管理網(wǎng)絡(luò)日記
現(xiàn)在,實現(xiàn)用戶任務(wù)的代碼基本上全部完成。用戶可以讀取選擇時間間隔的消息并張貼評注。另一方面,網(wǎng)絡(luò)日記的擁有者必須直接在數(shù)據(jù)存儲上添加、插入和刪除消息與評注。為了訪問該表,下一步的主要工作就是開發(fā)一個登錄頁并修改網(wǎng)絡(luò)日記的主頁,這樣當管理員登錄后,該頁面將顯示用于管理操作的其他控件。登錄頁由用戶名稱與密碼的文本框、“persistent login”選項的復(fù)選框以及一個提交按鈕組成。由于將來只有一個管理員并且不需要采用基于角色的安全策略,將憑據(jù)存儲在 web.config 文件中就足夠了。圖 12 所示為代碼隱藏類。如果指定的憑據(jù)有效,它將對用戶進行身份驗證并重新定向到 Default.aspx 頁。
在 Default.aspx 頁中,我將添加編輯控件,只有用戶經(jīng)過身份驗證時該控件才可見。在該頁的頂部,我聲明了一個帶 "logout" 鏈接的面板,該鏈接利用查詢字符串中的 "action=logout" 參數(shù)指向 Login.aspx,同時還聲明了一個帶文本框的表以指定消息的標題和內(nèi)容。該面板在 Page_Load 中顯示或者隱藏,如下所示:
MessageBox.Visible = User.Identity.IsAuthenticated
當管理員填充文本框并單擊 Post 按鈕時,客戶端就會發(fā)生 Click 事件,在其事件處理程序中,我用 InsertMessage 函數(shù)來向數(shù)據(jù)庫添加新消息,并在 Repeater 中調(diào)用 BlindData 來加載它。
現(xiàn)在是該添加編輯功能的時候了??梢酝ㄟ^將 LinkButton 控件添加到 Repeater 的 ItemTemplate 來實現(xiàn)此目的:
<asp:LinkButton CausesValidation="False" runat="server" Text="Edit"CommandName="Edit" CommandArgument=‘<%# Container.DataItem("MessageID")%>‘Visible=‘<%# User.Identity.IsAuthenticated %>‘/>
只有在用戶經(jīng)過身份驗證后該鏈接才可見(它的工作方式和在 MessageBox 面板中的一樣,但此處通過 ASPX 文件中的數(shù)據(jù)綁定表達式來顯示或隱藏該鏈接)。CommandArgument 屬性包含要編輯消息的 ID,但是還必須將 CommandName 指定為“Edit”,因為需要另一個 LinkButton 來刪除消息,而且您想明確知道單擊的是兩個按鈕中的哪一個。Repeater 的 ItemCommand 事件處理程序的代碼如圖 13 所示。
該代碼首先檢索消息的 ID,該 ID 作為單擊按鈕的 CommandArgument 屬性進行傳遞。然后,管理員根據(jù)按鈕的 CommandName 來決定是否刪除或編輯該消息。當它等于 "Edit" 時,檢索指定消息的當前數(shù)據(jù),并用來填充 MessageBox 的文本框以便管理員會看到當前文本并可以對其進行編輯。當單擊 Post 按鈕時,如果 MessageID 文本框為空,則這意味著管理員正在發(fā)送新消息;否則,該文本框中將包含要編輯消息的 ID。以下是部分 Click 事件處理程序:
Private Sub PostMessage_Click(...) Handles PostMessage.ClickIf Not User.Identity.IsAuthenticated Then _Response.Redirect("Login.aspx", True)If MessageID.Text.Trim().Length > 0 Thenm_BlogManager.UpdateMessage(Integer.Parse(MessageID.Text), _Title.Text, Message.Text)Elsem_BlogManager.InsertMessage(Title.Text, Message.Text)End If‘ reset the textboxes to an empty string, and call BindData...End IfEnd Sub
Add New 和 Edit 功能已經(jīng)全部實現(xiàn)(圖 4 顯示管理員模式下的頁面外觀)。還有一件值得做的事情就是添加 Delete 功能。按照當前的情況,如果管理員誤點了 Delete 鏈接,由于不需要確認,消息就會被立即刪除。增加彈出式確認對話框非常簡單,只需要一些 JavaScript 來響應(yīng)超級鏈接的 onClick 事件即可。這可以通過在創(chuàng)建鏈接(即,當創(chuàng)建 Repeater 的項目時)時向控件的屬性集合添加條目來完成。我只需要為奇、偶項處理 Repeater 的 ItemCreated 事件,獲得對 Delete LinkButton 的引用,并添加 JavaScript 彈出式確認對話框:
Private Sub Blog_ItemCreated(...) Handles Blog.ItemCreatedIf e.Item.ItemType <> ListItemType.AlternatingItem AndAlso _e.Item.ItemType <> ListItemType.Item Then Exit SubDim lnkDelete As LinkButton = CType( _e.Item.FindControl("DeleteMessage"), LinkButton)lnkDelete.Attributes.Add("onclick", _"return confirm(‘Are you sure you want to delete this" & _"message?‘);")End Sub
如果管理員單擊 Cancel,則 JavaScript 返回“假”,不提交該頁面并且不刪除該消息。
編輯和刪除評注的實現(xiàn)方法相同,因此我就不再詳細介紹。但是,您可以在本文的下載代碼中找到完整的實現(xiàn)。有幾個細節(jié)值得在這里介紹一下。在每次必須處理 Repeater 的事件時,我都使用 Visual Basic®.NET Handle 關(guān)鍵字,該關(guān)鍵字使您能夠?qū)⒎椒ㄅc由 WithEvents 聲明的控件實例的事件相關(guān)聯(lián)。評注的內(nèi)部 DataList 不能這樣做,因為它是在運行時動態(tài)創(chuàng)建的并且它沒有 WithEvents 控件變量。但是,您可在控件聲明中直接指定事件處理程序,如下所示:
<asp:DataList Runat="server" OnDeleteCommand="Comments_DeleteCommand"OnItemCreated="Comments_ItemCreated"OnEditCommand="Comments_EditCommand" ...>
其他的小細節(jié)是,當管理員單擊某個評注的 Edit 鏈接時,必須顯示評注框并將頁面滾動到底部以確保其可見。之前我針對“Post your own comment”鏈接這樣做過,但在那樣的情況下,完成這樣操作的 JavaScript 例序直接與鏈接相關(guān)聯(lián),不用和服務(wù)器來回聯(lián)系。這里該頁面首先會回發(fā)給預(yù)先用評注填充的編輯文本框,當將該頁面再次發(fā)送給客戶端瀏覽器時,將調(diào)用 JavaScript 例程。為此,我將一些 JavaScript 發(fā)送給剛好調(diào)用以前編寫的 ShowCommentBox2 例程的客戶端,如下所示:
Sub Comments_EditCommand(...)‘ fill the textboxes with the current data for the clicked comment•••Dim script As String = _"<script language=""JavaScript"">ShowCommentBox2();</script>"Me.RegisterStartupScript("ShowEditCommentBox", script)End Sub
在關(guān)閉頁面的服務(wù)器端 <form> 標記之前,RegisterStartupScript 發(fā)出指定的 JavaScript 塊,確保已經(jīng)創(chuàng)建了 CommentBox (否則,在未找到 CommentBox 容器時會出現(xiàn)引用不正確的錯誤)。
驗證多個虛擬窗體
當有文本框或者其他的輸入控件時,通過添加驗證程序控件來確保提供值并且值的格式和范圍正確,這通常是個不錯的主意。在本應(yīng)用程序中,您必須根據(jù)用戶想要采取的操作來實施不同的驗證。如果用戶按下 Post 按鈕提交評注時,則必須確保他提供了其姓名和評注文本。如果單擊了 Load Blog 按鈕,則必須要檢查起止日期的格式是否有效。不過我沒有添加驗證程序,因為還有另一個問題需要解決。
我有三個帶有輸入控件的“虛擬窗體”:評注框、新消息框,和時間間隔選擇框。在 ASP.NET 頁中,可以只有一個服務(wù)器端窗體。這意味著所有的輸入控件、驗證程序和提交按鈕都位于同一窗體中。一旦用戶正確填充了時間間隔文本框并單擊了提交按鈕,文本框驗證程序就會驗證該輸入。評注框和消息框的驗證程序?qū)⒃谄湮谋究驔]有值或者值的格式不正確的情況下阻止該窗體回發(fā)。
為了解決這個問題,我用一個由 James M. Venglarik 開發(fā)的第二版自定義控件替換了標準的 ASP.NET 按鈕,該控件是他為 MSDN® Magazine 文章“Selectively Enable Form Validation When Using ASP.NET Web Controls”而開發(fā)的。該控件將創(chuàng)建使用某些客戶端 JavaScript 來禁用指定的驗證程序列表的按鈕,從而有可能獲得在提交頁面之前驗證某些輸入控件而不驗證其他控件的按鈕。一旦頁面引用了該控件,就會按照如下聲明 Load Blog 按鈕:
<nfvc:NoFormValButton ID="LoadBlog" Runat=Server Text="Load"NoFormValList="RequireAuthor,RequireComment,ValidateEmailFormat" />
NoFormValList 屬性指定單擊它時禁用的由逗號隔開的驗證程序列表,在此指的是評注框和消息框中的所有文本框驗證程序。
小結(jié)
本文構(gòu)建的應(yīng)用程序至此就算是功能齊全了。您可以將它上載到自己的服務(wù)器上來編寫網(wǎng)絡(luò)日記,或者可以在 http://www.bytecommerce.com/blog 上在線查看。DataSet 的關(guān)聯(lián)功能和創(chuàng)建嵌套 DataLists 和 DataGrids 的能力使得提供主從關(guān)系報告變得非常簡單,而這正是創(chuàng)建該網(wǎng)絡(luò)日記應(yīng)用程序的目的。模板控件的靈活性意味著您幾乎可以創(chuàng)建任何形式的布局。您可以對表中的多個數(shù)據(jù)項進行分組并自定義默認實現(xiàn)的行為,就像在給 Delete 按鈕添加確認彈出框時所看到的那樣。這些控件結(jié)合少許客戶端 JavaScript(用來對這些控件的行為進行腳本編寫并將它們結(jié)合在一起),造就了一個完整且功能豐富的 ASP.NET 報告應(yīng)用程序。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ASP.NET MVC 4 路線圖01-01-01
【ASP.NET】用C#動態(tài)添加非ASP的標準html控件(如添加Script標簽)
子評真詮全文
梅花雨的日歷控件在ASP.NET2.0下不可用的解決方法
javascript獲取TreeView控件選中節(jié)點的Text和Value
DW網(wǎng)頁設(shè)計:驗證用戶注冊
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服