現在網上有很多提供主頁空間的站點,但是大部分低價位的站點只提供靜態HTML文檔的功能,不支持ASP、PHP等動態網頁技術。如果您像在您的主頁上放上您喜歡的文章,那么需要一個一個鏈接的做。如果您刪除了某篇文章或者想添加某篇文章,那么需要做很多改動,牽一發而動全身。
凌科文章動態發布系統就是為你解決這個問題而開發的軟件。使用本軟件后您可以輕易生成下面效果的網頁。
///////////////////////////////////
Delphi的使用幫助文件
最后一篇情書
上一頁
//////////////////////////////////
凌科文章動態發布預覽
凌科文章動態新技術發
下一頁
/////////////////////////////////
點擊相應的文章標題(如"最后一篇情書 ")就會彈出一個窗口顯示這篇文章。
軟件的界面如下:

這款軟件有兩項關鍵性技術:瘦客戶端的數據庫應用和網頁分頁顯示技術。其中的瘦客戶端的數據庫應用在我的另一款軟件《凌科網頁精靈》的“程序自動升級”功能中也有所應用。
一、瘦客戶端的數據庫應用
本軟件的一個基本要求就是用戶可是隨時在工程添加、刪除文章,并能將工程保存到文件中,并可以在以后打開工程文件后繼續編輯工程。實現這個功能可以有很多中解決方案:
(1) 定義一個結構體TariticleItem(因為要將文章保存到ListBox的Item屬性中,所以要用類來模擬結構體):
TariticleItem = class
Public
Title: string;
Ariticle: string;
End;
將用戶輸入的標題和文章正文存儲在一個TariticleItem,然后再調用ListBox.Item.Add()將此TariticleItem保存到ListBox 中,在保存工程的時候遍歷ListBox中每個項目然后保存到文件。在加載工程文件時也類似的遍歷項目。
但是這樣會使工作量變得很大,況且很有可能出現內存問題(我猜測的,沒有實踐),而且保存的工程文件格式也會很難確定。
(2)用Access的MDB數據庫存儲文章,通過ADO操作數據庫。之所以選擇ADO訪問Access數據庫而不使用BDE的原因就是BDE的配置十分麻煩,在這種小規模的程序中使用BDE有點恐怖。而Access的ADO驅動在Win98以上操作系統中一般都有安裝,這樣就免去了配置的麻煩。而且使用TADOTable或TADOQuery的SaveToFile和LoadFromFile方法也可以輕易實現數據導出/導入文件。但是為什么我們不使用它呢?因為“Win98以上操作系統” “一般”都有安裝,這就說明Win95的用戶就不能使用本軟件了(現在還是有很多Win95用戶的),而且即使是Win98以上的系統也不能保證一定裝有ADO驅動。
(3)使用Delphi中的TclientDataSet 控件。TClientDataSet控件繼承自TDataSet,具有所有數據集組件的基本功能,而且還有自己的很多很有用的功能,它是一款基于文件型數據存儲和操作的控件,數據可以導出到文件中(支持xml、cds兩種文件格式)。該控件封裝了對數據進行操作處理的接口和功能,具有ADO的上述優點,基本上能滿足單機"瘦"數據庫應用程序的需要。Delphi5的用戶在發布軟件時只要同時發布Midas.dll就可以了,Delphi6、Delphi7的用戶只要在工程文件中包含MidasLib單元,Midas.dll的功能就集成在應用程序中了(會使程序增大200K左右),成為真正的零配置軟件。況且使用TclientDataSet控件,我們就可以使用數據敏感組件,我們又免去了處理將保持用戶輸入數據更新的問題。基于以上原因,我決定使用TclientDataSet控件。

如上圖,在窗體中放入一個TclientDataSet控件提供數據、一個TdataSource控件、一個TDBGrid控件用來顯示文章標題列表,一個TDBEdit控件顯示文章標題,一個TDBMemo控件顯示文章內容,并設定它們之間的關系。
在TclientDataSet組件上單擊右鍵,選擇“Fields Editor”,在彈出的字段編輯器中單擊右鍵,選擇“New Field”,建一個名為Title的String類型字段和名為Article的Memo類型字段。再在TclientDataSet組件上單擊右鍵,選擇“Create DataBase”。至此一個模擬數據庫就建好了,您可以調用TclientDataSet的Delete,Edit,Insert等方法實現對數據庫操作了,還可以調用SaveToFile、LoadFromFile方法保存、加載數據文件了。
二、網頁分頁顯示技術
網頁分頁顯示技術比較簡單,我現在也說得口干舌躁了,先給出代碼,你先自己分析一下吧!我將網頁分頁顯示封裝成了一個TarticlePublisher類,核心方法就是MakePublish方法,下面給出類的定義和MakePublish方法的代碼。
TArticlePublisher = class(TObject)
private
FTitleWord: string;
FIndexWord: string;
FNumPerPage: Integer;
FPageCount: Integer;
FDataSet: TDataSet;
FOutPutDir: string;
FLinkFont: TFont;
FUseDefFont: Boolean;
FIndexTemplete: string;
FLinkCSS: string;
procedure FSetNumPerPage(AValue: Integer);
procedure FSetLinkFont(AFont: TFont);
procedure FSetUseDefFont(AValue: Boolean);
procedure FSetIndexTemplete(AValue: string);
procedure FSetLinkCSS(AValue: string);
protected
function AddFontToText(AText: string; AFont: TFont): string;virtual;
public
//輸出文章文件的文件名的開始幾個字符,默認為'art'
property TitleWord: string read FTitleWord write FTitleWord;
//輸出索引文件的文件名的開始幾個字符,默認為'ind'
property IndexWord: string read FIndexWord write FIndexWord;
//每頁的條目數,默認為30
property NumPerPage: Integer read FNumPerPage write FSetNumPerPage;
//總頁數,只讀
property PageCount: Integer read FPageCount;
property DataSet: TDataSet read FDataSet write FDataSet;
//輸出路徑
property OutPutDir: string read FOutPutDir write FOutPutDir;
//超鏈接字體
property LinkFont: TFont read FLinkFont write FSetLinkFont;
//使用默認字體
property UseDefFont: Boolean read FUseDefFont write FSetUseDefFont;
//索引頁模板
property IndexTemplete: string read FIndexTemplete write FSetIndexTemplete;
//超鏈接CSS樣式
property LinkCSS: string read FLinkCSS write FSetLinkCSS;
constructor Create;
procedure MakePublish;//開始轉化
end;
procedure TArticlePublisher.MakePublish;
var
LCount: Integer;
i, j: Integer;
LAriticleSL: TStringList;//保存文章用
LStrList: TStringList;//保存動態頁面用
LTempStr: string;
begin
LCount := DataSet.RecordCount;
DataSet.First;
LAriticleSL := TStringList.Create;
for i := 0 to LCount - 1 do
begin
LAriticleSL.Clear;
LAriticleSL.Add(DataSet.FieldByName('Article').AsString);
LAriticleSL.SaveToFile(Format('%s%s%d.htm',[OutPutDir,TitleWord,i]));
DataSet.Next;
end;
LAriticleSL.Free;
if LCount mod NumPerPage = 0 then
FPageCount := LCount div NumPerPage
else
FPageCount := (LCount div NumPerPage) + 1;
LStrList := TStringList.Create;
DataSet.First;
for i := 1 to PageCount do
begin
LStrList.Clear;
for j := 0 to NumPerPage - 1 do
begin
if DataSet.Eof then break;
LStrList.Add('<p>');
if LinkCSS = '' then
LStrList.Add('<a href='+QuotedStr(TitleWord+IntToStr((i-1)*NumPerPage+j)+'.htm')+
' target=_blank>'+AddFontToText(DataSet.FieldByName('Title').AsString,LinkFont)+'</a>')
else
LStrList.Add('<a href='+QuotedStr(TitleWord+IntToStr((i-1)*NumPerPage+j)+'.htm')+
' target=_blank style="'+LinkCSS+'">'+
AddFontToText(DataSet.FieldByName('Title').AsString,LinkFont)+'</a>');
LStrList.Add('</p>');
DataSet.Next;
end;
if i <> 1 then
LStrList.Add('<a href='+QuotedStr(IndexWord+IntToStr(i-1)+'.htm')+
'>'+AddFontToText('上一頁', LinkFont)+'</a>');
if i <> PageCount then
LStrList.Add('<a href='+QuotedStr(IndexWord+IntToStr(i+1)+'.htm')+
'>'+AddFontToText('下一頁', LinkFont)+'</a>');
if IndexTemplete <> '' then
begin
LTempStr := AnsiReplaceText(IndexTemplete, INDEXTEMPLETESIGN, LStrList.Text);
LStrList.Text := LTempStr;
end;
LStrList.SaveToFile(Format('%s%s%d.htm',[OutPutDir,IndexWord,i]));
end;
LStrList.Free;
end;
二、其他經驗
1、將字體Tfont保存到Ini文件的方法。
因為vcl的TFont只是對windows FONT的封裝,它內部所存儲的數據只是為了使用戶方便操作windows的font而定義的一些私有狀態數據,可以說這些數據與真正windows font中的數據風馬牛不相及,你保存、恢復這些數據根本沒用(即使正確地恢復了這些數據也只會得到一個錯誤的TFont對象)
要保存TFont的內容并能正確恢復最簡單的方法是保存TFont.Handle所對應font的具體結構,恢復時根據這個結構創建一個HFONT然后賦值給TFont.Handle即可。
保存字體:
var
LFont: TFont;
LLF: TLogFont;
LMS: TMemoryStream;
…………
begin
…………
LFont := Edit1.Font;
GetObject(LFont.Handle, SizeOf(LLF), @LLF);
LMS.Write(LLF, SizeOf(LLF));
LMS.Position := 0;//不能丟了這句
LIni.WriteBinaryStream('Config', 'LinkFont', LMS);
…………
end;
加載字體:
var
LFont: TFont;
LLF: TLogFont;
LMS: TMemoryStream;
Begin
…………
LIni.ReadBinaryStream('Config', 'LinkFont', LMS);
LMS.Position := 0;//不能丟了這句
LMS.Read(LLF, SizeOf(LLF));
LFont.Handle := CreateFontIndirect(LLF);
…………
End;
但是這樣還有一個問題就是字體中的字體名、字體大小等屬性都能正確的保存,但是字體的顏色卻沒有保存。分析TlogFont發現原來系統原生對象TlogFont中并沒有顏色這個字段值,字體顏色是VCL封裝上去的,所以需要另外再在Ini文件增加一個字體顏色的項目。
2、為一段文字加上HTML字體
function AddFontToText(AText: string; AFont: TFont): string;
Const
LTmpl = '<font face="%s" size="%d" color="#%s">%s</font>';//模板
var
LColorStr: string;
LR, LG, LB: Integer;
tmp: string;
begin
LR := GetRValue(AFont.Color);
LG := GetGValue(AFont.Color);
LB := GetBValue(AFont.Color);
LColorStr := Format('%x%x%x',[LR,LG,LB]);
tmp := Format(LTmpl,[AFont.Name, AFont.Size, LColorStr,AText]);
if fsBold in AFont.Style then
tmp := Format('<b>%s</b>',[tmp]);
if fsItalic in AFont.Style then
tmp := Format('<i>%s</i>',[tmp]);
if fsUnderline in AFont.Style then
tmp := Format('<u>%s</u>',[tmp]);
if fsStrikeOut in AFont.Style then
tmp := Format('<StrikeOut>%s</StrikeOut>',[tmp]);
result := tmp;
end;