国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

使用 ASP.NET Community Starter Kit建造網(wǎng)站

2024-07-10 13:04:48
字體:
供稿:網(wǎng)友
  • 本文來源于網(wǎng)頁設(shè)計(jì)愛好者web開發(fā)社區(qū)http://www.html.org.cn收集整理,歡迎訪問。
  • 翻譯:劉海東

    以下內(nèi)容翻譯自building websites with the asp.net community starter kit by k. scott allen and cristian darie for packt publishing,以下內(nèi)容是該書的第8章,詳細(xì)介紹如何擴(kuò)展csk來增加faq功能. 如果你想了解 asp.net community starter kit的詳細(xì)信息,可以到www.asp.net瀏覽和下載,它是一個免費(fèi)的開源項(xiàng)目.假如你想建造一個穩(wěn)健而靈活的asp.net網(wǎng)站,csk將是一個很好的出發(fā)點(diǎn).

    創(chuàng)建一個新模塊
    每一個社區(qū)網(wǎng)站都會有不同的需求要實(shí)現(xiàn)。雖然本身的csk類庫有很大的靈活性,但完全擁有源代碼意味者你可以在這個高品質(zhì)的網(wǎng)站基礎(chǔ)上增加定制的額外功能。在這篇文章里,我們將詳細(xì)介紹如何在已有的框架中增加一個全新的功能faq(frequently asked questions),并如何做到與已有的模塊無縫銜接。

    在真正開始之前,先提醒大家一下,csk是一個不斷修改和升級的項(xiàng)目,所以在動手增加新模塊之前,先到網(wǎng)上查找一下別人是否已經(jīng)實(shí)現(xiàn)了這項(xiàng)功能,或者關(guān)注一下csk是否增加了新的特性。

    模塊設(shè)計(jì)
    在你實(shí)現(xiàn)一個用于csk的模塊之前,首先要明白你要增加的特性到底是什么,然后決定由csk中已有的哪些模塊來實(shí)現(xiàn)這個功能。

    首先讓我們對faq的需求列一個大致的清單:

    l 一個faq由一個問題、一個答案,一個描述或介紹和一些相關(guān)參照的鏈接組成,

    l 社區(qū)成員能夠?qū)δ硞€問題加評注或評級,當(dāng)有新的問題時通過郵件提醒,

    l 如果版主同意,社區(qū)成員可以發(fā)布新的問題主題。

    你當(dāng)然可以只用一個html文件列出所以的問題和答案,但那樣就限制了用戶的交互性操作(評注、評級、郵件等)

    在csk所附帶的數(shù)據(jù)庫中有表community_contentpages,其中包含了社區(qū)頁面的大部分信息如作者、瀏覽計(jì)數(shù)和介紹等。由于要存放與faq相關(guān)的答案、參考鏈接等屬性,我們再增加一個表community_faqs:


    然后我們可以創(chuàng)建保存faq信息的類了。在下面的圖中,faqinfo類繼承自contentinfo,它可以保存一般內(nèi)容信息項(xiàng)的大部分屬性。每一個模塊都會有一個自己的utility類來讀取、添加和編輯內(nèi)容。所以對于faq的模塊,我們還要創(chuàng)建一個faqutility類。


    我們還要創(chuàng)建給code-behind頁面使用的類來顯示和編輯faq。csk中是通過skinnedcommunitycontrol來使不同頁面顯示不同的界面。csk中也包含了其它實(shí)現(xiàn)了常用功能的基類可用于增加(contentaddpage),編輯(contenteditpage)和顯示(contentitempage),下面的圖中顯示了這些類的繼承關(guān)系


    另外,我們還需要創(chuàng)建從webcontrol繼承的類來顯示faq的內(nèi)容。通常,每一個屬性顯示時放在不同的控件中,并且該控件可以用最適合的風(fēng)格來顯示內(nèi)容。下面的圖中顯示了這些最終在這個模塊中會使用的控件,它們最終都是從webcontrol繼承而來。


    模塊創(chuàng)建過程
    構(gòu)建模塊的過程我們將采用自底向上底方式,從數(shù)據(jù)庫建立開始,到表現(xiàn)層的界面和主題設(shè)置結(jié)束。我們將沿用在csk中已經(jīng)約定的命名模式,保持與其它模塊的風(fēng)格一致。如,在書的模塊中要從community_books表中讀取信息,那么相應(yīng)的類就是bookinfo。

    這樣我們就用一個叫做community_faqs的表,對應(yīng)的類叫做faqinfo。當(dāng)然你可能想另外加上唯一標(biāo)示防止將來的csk中包含這個模塊。例如你在abc公司工作,那么這個表名可以叫做community_abcfaqs來減少將來可能出現(xiàn)的名稱沖突。

    我們將使用下面的步驟來構(gòu)建faq模塊。你可以參照這些步驟構(gòu)建你自己的模塊:

    1. 創(chuàng)建一個新的表(community_faqs)來保存新模塊的附加字段信息,

    2. 創(chuàng)建用于添加、編輯、和選擇一個faq的存儲過程,另外還要一個從給定范圍內(nèi)讀取所有faq的存儲過程,

    3. 創(chuàng)建一個維護(hù)的存儲過程通過填充community_pagetypes和community_namedpages表來初始化faq模塊,

    4. 創(chuàng)建一個faqinfo的類保存一條faq的信息,

    5. 創(chuàng)建一個faqutility的類通過訪問數(shù)據(jù)庫來調(diào)用前面創(chuàng)建的與faq相關(guān)的存儲過程,

    6. 創(chuàng)建從webcontrol繼承的控件顯示不同的字段,這些控件叫做faqquestion,faqinro,faqanswer,faqreference和faqeditcontent

    7. 創(chuàng)建從skinnedcommunitycontrol繼承的類來包含下一步創(chuàng)建的顯示頁面背后的邏輯。這些類叫做addfaq,editfaq,faqsection,和faq

    8. 創(chuàng)建新模塊使用的內(nèi)容顯示頁面,包括有faqs_addfaq.ascx,faqs_faqsection.ascx和faq_faq.ascx。我們將使用faqs_addfaq增加和編輯faq。另外你還要在communityes/common/themes/defalult/skins/contentskins目錄下創(chuàng)建默認(rèn)界面文件,例如robotics和professional的主題。

    9. 創(chuàng)建定義該模塊頁面風(fēng)格的css文件和主題相關(guān)的css文件,并放到communities/common/themes/defalult/styles。

    下面將對每一個步驟作詳細(xì)的解釋。

    community_faq表
    大部分模塊共有的信息如標(biāo)題、描述和瀏覽計(jì)數(shù)放在community_contentpages表中。針對該faq模塊的附加信息則需要另加一個表存放。例如,我們可以把faq的問題放在page_title字段中,faq的介紹放在page_description字段.另外,我們還需要存放faq的答案和附加的參考索引等信息,所以還要創(chuàng)建下面的表:

    create table [community_faqs] (

    [faq_contentpageid] [int] not null ,

    [faq_answer] [ntext] not null ,

    [faq_reference] [ntext] null,

    constraint [pk_community_faqs] primary key clustered

    (

    [faq_contentpageid]

    ),

    constraint [fk_community_faqs_community_contentpages]

    foreign key

    (

    [faq_contentpageid]

    ) references [community_contentpages] (

    [contentpage_id]

    ) on delete cascade

    可以看到表的命名方式和數(shù)據(jù)類型與csk中的其它模塊都是一致的,這樣的編程習(xí)慣在國內(nèi)實(shí)在是難以做到。

    我們存儲faq的答案和參考索引的字段是ntext類型,這是為了能支持大數(shù)據(jù)量的文本(可高達(dá)1gb)。還要注意的是faq_answer是一個必要字段而faq_reference可以是空值。我們的主鍵(faq_contentpageid)關(guān)聯(lián)到community_contentpages表中的附加內(nèi)容。另外一個要注意的細(xì)節(jié)是外鍵的約束(級聯(lián)刪除)的使用,保證的數(shù)據(jù)關(guān)聯(lián)的完整性,并且節(jié)省了程序代碼。

    csk使用名為community_contentpagesdeletecontentpage(太長了吧)的存儲過程刪除community_contentpages表中的記錄。當(dāng)這個存儲過程刪除記錄時,服務(wù)器根據(jù)外鍵自動刪除faq表中的對應(yīng)記錄。

    增加存儲過程
    下面我們要做的是建立增加、編輯、讀取單個、讀取多個記錄的存儲過程,在代碼中不會出現(xiàn)sql的代碼。從封裝性和安全性來看,這樣做是一種很好的習(xí)慣。

    community_faqsaddfaq
    下面是增加一條新的faq記錄的存儲過程。我們不需要給兩個表中的每個字段填充數(shù)值。例如,我們不需要填充contentpage_viewcount列(默認(rèn)為0),也不需要在contentpage_datecommented中填寫日期。

    create procedure community_faqsaddfaq
    (
    @communityid int,
    @sectionid int,
    @username nvarchar(50),
    @topicid int,
    @question nvarchar(100),
    @introduction nvarchar(500),
    @metadescription nvarchar(250),
    @metakeys nvarchar(250),
    @moderationstatus int,
    @answer ntext,
    @reference ntext
    )
    as
    declare @contentpageid int
    declare @pagetype int
    set @pagetype = dbo.community_getpagetypefromname('faq')
    declare @userid int
    set @userid = dbo.community_getuserid(@communityid, @username);
    begin tran
    exec @contentpageid = community_addcontentpage
    @communityid,
    @sectionid,
    @userid,
    @question,
    @introduction,
    @metadescription,
    @metakeys,
    @pagetype,
    @moderationstatus,
    @topicid

    insert community_faqs
    (
    faq_contentpageid,
    faq_answer,
    faq_reference
    )

    values
    (
    @contentpageid,
    @answer,
    @reference
    )
    commit tran
    return @contentpageid
    注意這里使用了兩個csk提供的udf(user defined function),第一個udf取回頁面類型,csk中每個模塊都有唯一的頁面類型。第二個存儲過程是根據(jù)community和用戶名取回userid。

    由于我們要在兩個不同表中插入記錄,所以在這里使用了事務(wù)來保證操作的原子性。其中往community_contentpages表中插入記錄是通過調(diào)用communit_addcontentpage這個存儲過程來完成的,把faq的問題作為@title參數(shù)、介紹作為@description參數(shù)。addcontentpage執(zhí)行完后返回新增記錄的主鍵值,該數(shù)值被用于往community_faqs中新增記錄。

    在csk中所有新增記錄的存儲過程必須返回主鍵值作為結(jié)果。

    得到新的contentpageid數(shù)值在系統(tǒng)的上層是很有用的,這一點(diǎn)我們將在后面寫數(shù)據(jù)訪問組件時看到。

    community_faqseditfaq
    這個用于修改已有的faq記錄的存儲過程需要的參數(shù)比前面少了很多。因?yàn)橛行┳侄卧谖覀冊黾佑涗浿缶筒粫俦恍薷模鐓^(qū)域編號等。其代碼如下:

    create procedure community_faqseditfaq
    (
    @communityid int,
    @contentpageid int,
    @username nvarchar(50),
    @topicid int,
    @question nvarchar(100),
    @introduction nvarchar(500),
    @metadescription nvarchar(250),
    @metakeys nvarchar(250),
    @answer text,
    @reference text
    )
    as
    declare @userid int
    set @userid = dbo.community_getuserid(@communityid, @username)
    exec community_editcontentpage
    @contentpageid,
    @userid,
    @question,
    @introduction,
    @metadescription,
    @metakeys,
    @topicid
    update community_faqs set
    faq_answer = @answer,
    faq_reference = @reference
    where faq_contentpageid = @contentpageid
    這里我們又一次使用了csk中的存儲過程community_contentpages,然后用update的sql更新community_faqs。與新增faq的存儲過程相比,這里沒有使用事務(wù)來保證更新操作的原子性。我們遵循了csk中已經(jīng)建立的模式,這里都不使用事務(wù)。也許是設(shè)計(jì)者認(rèn)為更新操作失敗的可能性比新增要小的多,減少不必要的資源鎖定來提高系統(tǒng)數(shù)據(jù)吞吐能力吧。

    community_faqsgetfaqs
    接下來要寫的存儲過程是返回某個社區(qū)內(nèi)給定范圍的所有faq記錄。這個名為community_getpagedsortedcontent的存儲過程,在原來的基礎(chǔ)上增加了針對faq列的參數(shù),并用indexid排序。

    create procedure community_faqsgetfaqs
    (
    @communityid int,
    @username nvarchar(50),
    @sectionid int,
    @pagesize int,
    @pageindex int,
    @sortorder nvarchar(50)
    )
    as
    declare @currentdate datetime
    set @currentdate = getutcdate()
    select
    null faq_answer,
    null faq_reference,
    content.*
    from
    dbo.community_getpagedsortedcontent
    (
    @communityid,
    @username,
    @sectionid,
    @currentdate,
    @sortorder,
    @pagesize,
    @pageindex,
    default
    ) content
    order by
    indexid
    這個存儲過程使用了兩個減少代碼和將來維護(hù)量的技巧。首先,我們使用了content.*作為返回的結(jié)果,這里還使用了csk中的存儲過程。從效率上來說,取回所有的列比讓數(shù)據(jù)庫推算哪些列是需要的在返回要更有效率。而且,在這里設(shè)計(jì)師為了更好的兼顧了維護(hù)性。如果將來community_contentpages的結(jié)構(gòu)有了修改(如增加列),并不需要修改或測試相關(guān)的存儲過程。

    第二個要指出的是結(jié)果集中的兩個空列(faq_answer和faq_reference)。后面我們將寫一個faqinfo組件來保存來自這個存儲過程的多條記錄和后面一個存儲過程的單條記錄。由于我們想使用同一個組件來實(shí)現(xiàn)這兩種操作,所以我們要填充所有列的信息。因?yàn)檫@兩個列可能占用很大空間,而且不會在faq的統(tǒng)計(jì)列表中顯示,所以這里我們就用null值來代替。

    community_faqsgetfaq
    這個存儲過程我們要用來取得一條單獨(dú)的記錄和其它相關(guān)的功能。它還需要增加這個頁面的訪問計(jì)數(shù)和告訴用戶開始讀取頁面的內(nèi)容。這些任務(wù)是通過執(zhí)行csk的community_contnetpagestrackstats過程來完成的,整個過程代碼如下:

    create procedure community_faqsgetfaq
    (
    @communityid int,
    @username nvarchar(50),
    @contentpageid int
    )
    as
    declare @userid int
    set @userid = dbo.community_getuserid(@communityid, @username)
    -- update viewcount and hasread stats
    exec community_contentpagestrackstats @userid, @contentpageid
    declare @currentdate datetime
    set @currentdate = getutcdate()
    select
    faq_answer,
    faq_reference,
    content.*
    from
    dbo.community_getcontentitem(
    @communityid,
    @userid,
    @currentdate) content
    join community_faqs (nolock)
    on contentpage_id = faq_contentpageid
    where
    contentpage_id = @contentpageid
    注意由于要顯示明細(xì),這里我們讀取了faq_answer和faq_reference列的實(shí)際值。這里我們在join連接community_faqs表時使用了nolock的選項(xiàng),這將允許我們執(zhí)行臟讀取而不會有任何警告提示。(和數(shù)據(jù)庫鎖定機(jī)制有關(guān))

    初始化faq模塊
    每一個社區(qū)模塊都有一個維護(hù)用的存儲過程用來填充數(shù)據(jù)庫中與模塊運(yùn)行關(guān)的信息設(shè)置內(nèi)容。譬如我們要通過在community_pagetypes中增加記錄來注冊頁面類型:一條信息是關(guān)于顯示faq列表的頁面,另一條是顯示單個faq詳細(xì)信息的頁面。為了遵循csk一致的命名規(guī)則我們把這個存儲過程叫做community_maintenanceinitializefaqs,其中部分代碼摘錄如下:

    if not exists (select * from community_pagetypes
    where pagetype_name='faq section')
    begin
    insert community_pagetypes
    (
    pagetype_name,
    pagetype_description,
    pagetype_pagecontent,
    pagetype_issectiontype,
    pagetype_serviceselect
    )
    values
    (
    'faq section',
    'contains faqs in a question and answer style format',
    'aspnet.starterkit.communities.faqs.faqsection',
    1,
    'community_faqsserviceselect'
    )
    end
    else
    print 'warning: the faq module has already been registered.'
    由于csk會緩存community_namepages的數(shù)據(jù)所以只會讀取數(shù)據(jù)一次。如果你在這些表中作了修改,需要重新啟動web程序來使修改生效。

    這個維護(hù)的存儲過程還需要注冊一些新模塊重要使用的靜態(tài)顯示頁面,這里包括新增和編輯的頁面,并且你必須使用與你將要創(chuàng)建的aspx文件完全相同的名稱作為注冊信息。

    下面是代碼中的相關(guān)部分:

    if not exists (select * from community_namedpages
    where namedpage_path='/faqs_addfaq.aspx')
    begin
    insert community_namedpages
    (
    namedpage_name,
    namedpage_path,
    namedpage_pagecontent,
    namedpage_title,
    namedpage_description,
    namedpage_sortorder,
    namedpage_isvisible,
    namedpage_menuid
    )
    values
    (
    'addfaq',
    '/faqs_addfaq.aspx',
    'aspnet.starterkit.communities.faqs.addfaq',
    'add faq',
    'enables users to add a new faq',
    0,
    1,
    0
    )
    end
    else
    print 'warning: /faqs_addfaq.aspx has already been registered
    as a namedpage.'
    其中namedpage_pagecontent參數(shù)是csk調(diào)用該靜態(tài)頁面時要使用的code-behind類的完整路徑:aspnet.starterkit.communities.faqs.addfaq。

    faq組件
    faqinfo
    所有faq模塊中的c#代碼都將放在engine/modules/faqs目錄中。首先在一個components的目錄內(nèi)增加helper類。每個csk的模塊都應(yīng)該放在不同的名稱空間,即在aspnet.starterkit.communities后再加上模塊名稱作為限定。

    using system;
    using system.data.sqlclient;
    namespace aspnet.starterkit.communities.faqs
    {
    public class faqinfo : contentinfo
    {
    public faqinfo(sqldatareader dr) : base(dr)
    {
    if(dr["faq_answer"] != dbnull.value)
    {
    _answertext = (string)dr["faq_answer"];
    }
    if(dr["faq_reference"] != dbnull.value)
    {
    _referencetext = (string)dr["faq_reference"];
    }
    }
    public string answertext
    {
    get { return _answertext; }
    set { _answertext = value; }
    }
    public string referencetext
    {
    get { return _referencetext; }
    set { _referencetext = value; }
    }
    public string questiontext
    {
    get { return base.title; }
    set { base.title = value; }
    }
    public string introtext
    {
    get { return base.briefdescription; }
    set { base.briefdescription = value; }
    }
    private string _answertext;
    private string _referencetext;
    }
    }
    faqinfo類初始化時需要一個sqldatareader類的實(shí)例。在我們下一個類中將會有數(shù)據(jù)訪問的代碼來創(chuàng)建一個sqldatareader。

    faqutility
    按照csk中的編碼慣例,我們將把數(shù)據(jù)訪問的過程都放在一個utility類的靜態(tài)方法中。對應(yīng)于每一個與faq相關(guān)的存儲過程都會有一個靜態(tài)方法來調(diào)用。(除了用于系統(tǒng)維護(hù)的存儲過程之外,因?yàn)橹挥性谡军c(diǎn)初始化時才會用到它)。每個靜態(tài)方法都需要與之相對應(yīng)的參數(shù)用于傳遞到存儲過程中去。

    下面是addfaq的方法:

    public static int addfaq(
    string username,
    int sectionid,
    int topicid,
    string question,
    string introduction,
    string answer,
    string reference,
    int moderationstatus)
    {
    sqlconnection conportal = new sqlconnection(
    communityglobals.connectionstring);
    sqlcommand cmdadd = new sqlcommand(
    "community_faqsaddfaq", conportal);
    cmdadd.commandtype = commandtype.storedprocedure;
    cmdadd.parameters.add("@return_value",
    sqldbtype.int).direction =
    parameterdirection.returnvalue;
    cmdadd.parameters.add("@communityid",
    communityglobals.communityid);
    cmdadd.parameters.add("@sectionid", sectionid);
    cmdadd.parameters.add("@username", username);
    cmdadd.parameters.add("@topicid", topicid);
    cmdadd.parameters.add("@question", question);
    cmdadd.parameters.add("@introduction", introduction);
    cmdadd.parameters.add("@metadescription",
    contentpageutility.calculatemetadescription(introduction));
    cmdadd.parameters.add("@metakeys",
    contentpageutility.calculatemetakeys(introduction));
    cmdadd.parameters.add("@moderationstatus", moderationstatus );
    cmdadd.parameters.add("@answer", sqldbtype.ntext);
    cmdadd.parameters.add("@reference", sqldbtype.ntext);
    cmdadd.parameters["@answer"].value = answer;
    cmdadd.parameters["@reference"].value = reference;

    conportal.open();
    cmdadd.executenonquery();
    int result = (int)cmdadd.parameters["@return_value"].value;
    searchutility.addsearchkeys(conportal, sectionid, result,
    question, introduction);
    conportal.close();
    return result;
    }
    請注意addfaq方法內(nèi)部還調(diào)用了searchutility類的方法產(chǎn)生了有關(guān)內(nèi)容的查詢關(guān)鍵字,另外它還返回了新增對象的唯一標(biāo)示符。這里csk需要做的一個改進(jìn)是在代碼中增加 try catch finally來保證執(zhí)行數(shù)據(jù)庫連接的close方法。雖然發(fā)生異常的可能性很小,但對于一個大容量的社區(qū)網(wǎng)站來說,你無法承受浪費(fèi)浪費(fèi)數(shù)據(jù)庫連接可能帶來的性能下降。

    在faqutility中的另外兩個方法是getfaqs和getfaqinfo。getfaqs在sqldatareader中循環(huán)讀取記錄并返回一個faqinfo對象的arraylist,而getfaqinfo只返回由數(shù)據(jù)庫中一條記錄填充的faqinfo對象。

    public static contentinfo getfaqinfo(string username,
    int contentpageid)
    {
    faqinfo faq = null;
    sqlconnection conportal = new sqlconnection(
    communityglobals.connectionstring);
    sqlcommand cmdget = new sqlcommand(
    "community_faqsgetfaq", conportal);
    cmdget.commandtype = commandtype.storedprocedure;
    cmdget.parameters.add(
    "@communityid", communityglobals.communityid);
    cmdget.parameters.add("@username", username);
    cmdget.parameters.add("@contentpageid", contentpageid);
    conportal.open();
    sqldatareader dr = cmdget.executereader();
    if (dr.read())
    faq = new faqinfo(dr);
    conportal.close();
    return faq;
    }
    public static arraylist getfaqs(string username, int sectionid,
    int pagesize, int pageindex,
    string sortorder)
    {
    sqlconnection conportal = new
    sqlconnection(communityglobals.connectionstring);
    sqlcommand cmdget = new sqlcommand( "community_faqsgetfaqs",
    conportal);
    cmdget.commandtype = commandtype.storedprocedure;
    cmdget.parameters.add("@communityid",
    communityglobals.communityid);
    cmdget.parameters.add("@username", username);
    cmdget.parameters.add("@sectionid", sectionid);
    cmdget.parameters.add("@pagesize", pagesize);
    cmdget.parameters.add("@pageindex", pageindex);
    cmdget.parameters.add("@sortorder", sortorder);

    arraylist faqs = new arraylist();
    conportal.open();
    sqldatareader dr = cmdget.executereader();
    while (dr.read())
    faqs.add(new faqinfo(dr));
    conportal.close();
    return faqs;
    }
    這里有比較重要的一點(diǎn)是getfaqinfo使用了以上特定的參數(shù)列表.在類框架中將通過其它的代理類來調(diào)用此方法所以參數(shù)必須一致.在后面我們寫內(nèi)容頁面時你將會看到它是如何工作的。
    我們的數(shù)據(jù)訪問層現(xiàn)在已經(jīng)完成了。如果你按照這個方式創(chuàng)建了模塊,你已經(jīng)可以開始編譯和解決錯誤了。你可以考慮寫一個驅(qū)動頁面來試驗(yàn)以上faqutility 中的4個靜態(tài)方法,并檢查在表community_faqs 和 community_contentpages中的結(jié)果是否正確。
    faq webcontrols
    在csk中把顯示內(nèi)容的功能分成幾個較小的控件。例如在engine/framework/contentpages/controls目錄下,你會看到一個顯示標(biāo)題的控件(文件名title.cs),還要內(nèi)容摘要的控件(briefdescription.cs)。我們需要為faq再增加兩個特定的控件,一個顯示答案和參考,另一個為授權(quán)用戶顯示編輯內(nèi)容的鏈接。

    faqanswer 和 faqreference
    在這一層的所有控件都是從.net framework的webcontrol控件繼承而來。我們只需簡單地設(shè)置控件的cssclass屬性,從當(dāng)前httpcontext中獲取要顯示的文本,然后重載rendercontents方法寫出該文本。

    下面的控件將被創(chuàng)建在engine/module/faqs/controls中,用來顯示faq的答案和參考:

    using system;
    using system.web;
    using system.web.ui;
    using system.web.ui.webcontrols;
    using aspnet.starterkit.communities.faqs;
    using system.componentmodel;
    namespace aspnet.starterkit.communities
    {
    [designer(typeof(aspnet.starterkit.communities.communitydesigner))]
    public class faqanswer : webcontrol
    {
    public faqanswer() : base()
    {
    cssclass = "faqanswertext";
    if(context != null)
    {
    object faqinfo = context.items["contentinfo"];
    if(faqinfo != null)
    {
    _text = ((faqinfo)faqinfo).answertext;
    }
    }
    }
    public string text
    {
    get { return _text; }
    set { _text = value; }
    }
    override protected void rendercontents(
    htmltextwriter writer)
    {
    sectioninfo objsectioninfo =
    (sectioninfo)context.items["sectioninfo"];
    writer.write(
    communityglobals.formattext(
    objsectioninfo.allowhtmlinput,
    objsectioninfo.id, _text));
    }
    private string _text;
    }
    }
    請注意我們前面的控件是在aspnet.starterkit.communities.faqs命名空間中,但是被放在上一層命名空間中。這是因?yàn)閏sk所使用的界面文件都指向aspnet.starterkit.communities。下面這行標(biāo)記就為以上控件指定了界面:

    <community:faqanswer runat="server" id="answer1" name="answer1"/>

    faqrefrence控件看上去與faqanswer非常相似。都重載了rndercontents方法并使用communityglobals類對輸出文本進(jìn)行轉(zhuǎn)換和格式化。

    faqeditcontent
    每一個csk模塊都會使用一個從editcontent繼承而來的控件來為授權(quán)用戶顯示編輯鏈接,使用戶能夠添加、刪除、移動、評注和控制內(nèi)容。我們所要做的只是設(shè)置合適的url屬性。在基類中的邏輯判斷來決定何時顯示正確的鏈接。所有的動作在類的創(chuàng)建過程完成如下:

    public faqeditcontent()
    {
    if (context != null)
    {
    pageinfo pageinfo = (pageinfo)context.items["pageinfo"];
    int contentpageid = pageinfo.id;
    addurl = "faqs_addfaq.aspx";
    editurl = string.format(
    "faqs_editfaq.aspx?id={0}",
    contentpageid);
    deleteurl = string.format(
    "contentpages_deletecontentpage.aspx?id={0}",
    contentpageid);
    moveurl = string.format(
    "contentpages_movecontentpage.aspx?id={0}",
    contentpageid);
    commenturl = string.format(
    "comments_addcomment.aspx?id={0}",
    contentpageid);
    moderateurl = "moderation_moderatesection.aspx";
    }
    }
    content 類
    在傳統(tǒng)的asp.net程序范例中,內(nèi)容類都是code-behind文件。由于csk允許用戶進(jìn)行更高層的定制所以在這方面顯得略有不同,我們將不能用ide來保持窗口界面和代碼保持同步。他們之間存在一個不確定的關(guān)聯(lián),因?yàn)槊總€代碼要支持不同界面的多個窗口文件。所以我們要手動維護(hù)頁面控件和事件綁定。這個任務(wù)雖不困難但需要編程時對控件名稱等細(xì)節(jié)更加注意。

    我們一共有4個內(nèi)容類需要實(shí)現(xiàn)用:

    l faq:顯示一個faq記錄

    l faqsection:顯示一個faq列表

    l addfaq:輸入faq的內(nèi)容

    l editfaq:修改faq的內(nèi)容

    寫一個內(nèi)容類所需要的代碼數(shù)量可能相差很大。如果使用csk中的contentitempage 和contentlistpage類,我們只需要寫很少的代碼就可以顯示faq和faq列表,下面就先看看這兩個類

    faq 和 faqsection
    public class faqsection : contentlistpage
    {
    public faqsection() : base()
    {
    skinfilename = _skinfilename;
    getcontentitems = _getcontentitems;
    }
    string _skinfilename = "faqs_faqsection.ascx";
    getcontentitemsdelegate _getcontentitems =
    new getcontentitemsdelegate(faqutility.getfaqs);
    }
    在這里,我們需要設(shè)定skinfilename屬性來指定實(shí)際的界面文件名。所有從skinnedcommunitycontrol(contentlistpage的基類)繼承的控件都需要這一步驟裝載合適的文件。

    在寫faqutility類的數(shù)據(jù)訪問方法時,我們提到過需要建立一個代理方法由getcontentitemsdelegate調(diào)用。在基類中將使用這一代理方法在指定區(qū)域內(nèi)讀取和顯示faq。按照此模式,在faq類初始化時會指定faqs_faq.ascx為界面文件,并把代理方法指向faqutility.getfaq以供需要時調(diào)用。

    addfaq和editfaq
    這兩個類的實(shí)現(xiàn)有一定的難度。因?yàn)橛脕硇略龊托薷膬?nèi)容的方法在各模塊中都可能用到,并且沒有基類可以繼承以減少工作量。反而是需要我們尋找適合該模塊的控件去讀取和設(shè)置數(shù)值,并響應(yīng)用戶事件觸發(fā)相關(guān)的數(shù)據(jù)訪問過程。

    在實(shí)現(xiàn)這些功能之前,我們先要規(guī)劃頁面上用于輸入和顯示的控件:

    l textbox:用于faq問題

    l textbox:用于faq介紹

    l topicpicker:用于faq列表中選取主題

    l htmltextbox:用于faq答案

    l htmltextbox:用于faq參考

    另外,我們還需要5個對應(yīng)控件用于顯示faq,如前面寫好的faqanswer用來顯示答案。下面是editfaq的創(chuàng)建方法示例:

    public editfaq() : base()
    {
    skinfilename = _skinfilename;
    sectioncontent = _sectioncontent;

    this.skinload += new skinloadeventhandler(skinloadfaq);
    this.preview += new previeweventhandler(previewfaq);
    this.submit += new submiteventhandler(submitfaq);
    }
    在該方法中初始化了用于顯示的界面文件名和sectioncontent屬性(后面將有說明),然后把基類中的事件與響應(yīng)方法建立了關(guān)聯(lián)。

    contenteditpage這一事件響應(yīng)函數(shù)中要包含以下邏輯:裝載界面文件,處理預(yù)覽按鈕的點(diǎn)擊和提交按鈕的點(diǎn)擊。

    void skinloadfaq(object s, skinloadeventargs e)
    {
    txtquestion = (textbox)getcontrol(e.skin, "txtquestion");

    // continue initializing all controls with getcontrol . . .
    }
    正如我們在前面章節(jié)所討論的那樣,csk動態(tài)裝載界面(ascx)文件顯示特定主題。如果你需要通過程序與界面上的控件交互操作,首先要得到該控件實(shí)例的引用,如我們編輯faq時需要得到textbox對象中的內(nèi)容。你可以通過skinnedcomunitycontrol基類中的getcontrol或getoptionalcontrol方法來實(shí)現(xiàn),在這里只有顯示問題的textbox需要被引用:

    protected override void onload(eventargs e)
    {
    if (!page.ispostback)
    {
    contentpageid = int32.parse(
    context.request.querystring["id"]);

    faqinfo faqinfo =
    (faqinfo)faqutility.getfaqinfo(
    objuserinfo.username, contentpageid);

    ensurechildcontrols();
    txtanswer.text = faqinfo.answertext;
    droptopics.selectedtopicid = faqinfo.topicid;
    txtintro.text = faqinfo.introtext;
    txtquestion.text = faqinfo.questiontext;
    txtreference.text = faqinfo.referencetext;
    }
    }
    當(dāng)頁面加載時,我們需要從數(shù)據(jù)庫中獲取已有faq的信息。csk會在查詢參數(shù)中傳遞該內(nèi)容的id,然后我們?nèi)〕鰅d再傳給faqutility類中的getfaqinfo方法。一旦得到了faq對象,就在頁面上顯示其信息。

    void previewfaq(object s, eventargs e)
    {
    if(objsectioninfo.enabletopics)
    topicpreview.name = droptopics.selecteditem.text;
    questionpreview.text = txtquestion.text;
    introductionpreview.text = txtintro.text;
    answerpreview.text = txtanswer.text;
    referencepreview.text = txtreference.text;
    }
    當(dāng)用戶點(diǎn)擊預(yù)覽按鈕后,我們要把所有編輯的內(nèi)容轉(zhuǎn)移到預(yù)覽控件中,在那里編輯內(nèi)容按照定義的風(fēng)格被渲染和顯示。使作者能更有效觀察和設(shè)計(jì)頁面在實(shí)際運(yùn)行時的外觀。contentedit類會保證作者看到的頁面中有預(yù)覽面板并觀察結(jié)果。

    void submitfaq(object s, eventargs e)
    {
    if (page.isvalid)
    {
    // get topic
    int topicid = -1;
    if (objsectioninfo.enabletopics)
    topicid = int32.parse(droptopics.selecteditem.value);

    faqutility.editfaq(
    objuserinfo.username,
    objsectioninfo.id,
    contentpageid,
    topicid,
    txtquestion.text,
    txtintro.text,
    txtanswer.text,
    txtreference.text);

    context.response.redirect(communityglobals.calculatepath(
    string.format("{0}.aspx", contentpageid)));
    }
    }
    sumbitfaq事件響應(yīng)函數(shù)通過faqutility類把內(nèi)容更新到數(shù)據(jù)庫中。一旦更新結(jié)束,我們發(fā)給用戶一個提示并離開編輯頁面,然后重定向到瀏覽頁面查看已更新的faq。

    int contentpageid
    {
    get { return (int)viewstate["contentpageid"]; }
    set { viewstate["contentpageid"] = value; }
    }
    textbox txtquestion;
    topicpicker droptopics;
    textbox txtintro;
    htmltextbox txtanswer;
    htmltextbox txtreference;
    displaytopic topicpreview;
    title questionpreview;
    briefdescription introductionpreview;
    faqanswer answerpreview;
    faqreference referencepreview;
    string _skinfilename = "faqs_addfaq.ascx";
    string _sectioncontent =
    "aspnet.starterkit.communities.faqs.faqsection";
    }
    addfaq頁面與editfaq的頁面基本類似,只是要增加用不同頁面顯示內(nèi)容的選項(xiàng)。

    faq page content skins
    我們的faq模塊需要有3個界面:

    l 顯示單個faq詳細(xì)內(nèi)容的界面

    l 顯示多個faq列表的界面

    l 新增或修改一個faq的界面

    我們至少要在默認(rèn)主題目錄中創(chuàng)建這3個界面文件。如果希望有不同界面還可以繼續(xù)增加其它的主題界面。對于新增或修改faq的頁面可能如下:


    這里要確定界面文件名與內(nèi)容頁面類中skinfilename屬性的賦值吻合。還要注意控件名稱,必須與調(diào)用類中g(shù)etcontrol方法所用參數(shù)名相同。

    最簡便的方法是從一個已有的界面窗口開始創(chuàng)建界面,因?yàn)槟氵€要保持與基類的控件相同。應(yīng)該還記得我們的addfaq類是從contentaddpage繼承而來,他需要界面上有一些特定的控件,如名為pnlformde面板和名為btnadd的按鈕。我們先看以下faq_addfaq的界面:

    <%@ control %>
    <%@ register tagprefix="community"
    namespace="aspnet.starterkit.communities"
    assembly="aspnet.starterkit.communities" %>
    <community:sectiontitle
    cssclass="form_title"
    runat="server"
    id="sectiontitle1"
    name="sectiontitle1"/>
    <p class="form_description">
    use this form to add or edit an faq.
    </p>
    <asp:panel id="pnlform" runat="server">
    <table cellspacing="0" cellpadding="3" width="520"
    class="form_table">
    <tr>
    <td class="form_sectionrow">
    faq form
    </td>
    </tr>
    <tr class="form_labelrow">
    <td >
    <span class="form_labeltext">question:</span>
    <asp:requiredfieldvalidator
    controltovalidate="txtquestion"
    text="(required)"
    runat="server"
    id="requiredfieldvalidator1"
    name="requiredfieldvalidator1"/><br>
    <asp:textbox id="txtquestion"
    cssclass="form_field"
    columns="40"
    runat="server">
    </asp:textbox>
    </td>
    </tr>
    ...
    用于顯示內(nèi)容的界面相對容易構(gòu)建,你只需按照合適的位置排列控件即可。下面是顯示單獨(dú)faq的界面:

    <%@ control %>
    <%@ register
    tagprefix="community"
    namespace="aspnet.starterkit.communities"
    assembly="aspnet.starterkit.communities" %>
    <table width="100%" cellspacing="0" cellpadding="11"
    class="faq_table">
    <tr>
    <td class="faq_introcell">>
    <div align="right">
    <community:displaytopic runat="server"
    id="displaytopic1"
    name="displaytopic1" />
    </div>
    <community:title runat="server" id="title1"
    name="title1" />
    <br>
    <br>
    posted by
    <community:author cssclass="faq_authorlink"
    runat="server" id="author1"
    name="author1" />
    on
    <community:datecreated runat="server"
    id="datecreated1"
    name="datecreated1" />
    <br>
    <br>
    <community:briefdescription runat="server"
    id="introduction1"
    name="introduction1" />
    </td>
    </tr>
    <tr>
    <td class="faq_answercell">
    <community:faqanswer runat="server"
    id="answer1" name="answer1" />
    </td>
    </tr>
    <tr>
    <td class="faq_answercell">
    <community:faqreference runat="server"
    id="reference1"
    name="reference1" />
    </td>
    </tr>
    <tr>
    <td class="faq_bodycell">
    <br>
    <community:rating submittext="rate item"
    runat="server" id="rating1"
    name="rating1" />
    </td>
    </tr>
    </table>

    <table width="100%" cellspacing="0" cellpadding="11">
    <tr>
    <td>
    <div class="content">
    <community:notify
    text="notify me when a new comment is posted"
    runat="server" id="notify1" name="notify1" />
    <p>
    <community:comments runat="server"
    id="comments1"
    name="comments1" />
    <p>
    <community:faqeditcontent
    commenttext="add your comment"
    edittext="edit this faq"
    deletetext="delete this faq"
    runat="server" id="faqeditcontent1"
    name="faqeditcontent1" />
    </div>
    </td>
    </tr>
    </table>
    在這個界面中,我們使用了自己創(chuàng)建的web控件(包括只對特定用戶顯示的faqeditcontent鏈接)。有了這些界面文件,構(gòu)建新模塊就只剩下最后一步了。

    模塊風(fēng)格
    在創(chuàng)建新界面和web控件時,要明確你在代碼中放置的css名稱。在模塊中對界面有新的要求時我們要修改這些css文件。由于csk中不提供默認(rèn)的css設(shè)置,所以你需要對每個模塊進(jìn)行定義。

    系統(tǒng)整合
    現(xiàn)在,你可以啟動和測試新的模塊。運(yùn)行的結(jié)果就像下面的圖形:


    總結(jié)
    在csk中創(chuàng)建新的模塊需要與已有的架構(gòu)進(jìn)行交互,所以首先對csk的整體架構(gòu)進(jìn)行了解。這篇文章對csk的架構(gòu)做了介紹,并詳細(xì)介紹如何增加一個自定義的新模塊,以及實(shí)現(xiàn)過程中所需要遵循的編程模式和命名風(fēng)格。

    發(fā)表評論 共有條評論
    用戶名: 密碼:
    驗(yàn)證碼: 匿名發(fā)表
    主站蜘蛛池模板: 社会| 称多县| 盐山县| 双鸭山市| 尉氏县| 台湾省| 五家渠市| 麻城市| 新巴尔虎右旗| 襄垣县| 达拉特旗| 盘山县| 筠连县| 隆回县| 临汾市| 洪江市| 铁岭县| 邳州市| 宿松县| 本溪| 临武县| 汕尾市| 西平县| 绥德县| 太仆寺旗| 德令哈市| 天祝| 武清区| 盐池县| 紫金县| 祁门县| 开平市| 南陵县| 阿鲁科尔沁旗| 惠来县| 嵩明县| 阿拉善右旗| 丽江市| 县级市| 山西省| 连江县|