Delphi 的 FireDAC 元件簡化了資料函式庫應用程式的開發流程。透過 FireDAC,開發者可以輕鬆地執行 SQL 查詢、管理資料函式庫連線以及處理資料集。本文除了資料函式庫操作,也涵蓋 UI 設計,包含使用 TListView 元件展示資料、客製化列表專案的外觀、以及處理使用者互動事件。這使得開發者能夠快速建立功能完善、使用者友善的資料函式庫應用程式。文章也示範瞭如何透過介面設計模式來分離資料存取邏輯和 UI 邏輯,提升程式碼的可維護性和擴充性。

使用 FireDAC 存取資料函式庫

在 Delphi 中使用 FireDAC 進行資料函式庫操作是一種相對簡單直接的方式。首先,我們需要設定查詢編輯器中的 SQL 陳述式,以取得 ToDos 資料表中的最大 Id 值。

建立取得最大 Id 值的 SQL 查詢

SELECT MAX(Id) AS MaxId FROM ToDos

在查詢編輯器中執行此查詢,可以檢查 SQL 陳述式是否有錯誤。雖然此時資料函式庫中尚無記錄,但仍可藉此檢查語法正確性。

內容解密:

  1. SELECT MAX(Id) AS MaxId FROM ToDos:這條 SQL 陳述式用於取得 ToDos 資料表中最大的 Id 值,並將結果命名為 MaxId
  2. 使用 MAX 聚合函式來計算 Id 的最大值。
  3. 如果資料表為空,MAX(Id) 將傳回 NULL,因此需要特別處理這種情況。

實作 GetNewId 方法

function TDMToDo.GetNewId: Integer;
begin
  FdqToDoMaxId.Open;
  try
    var Fld := FdqToDoMaxId.FieldByName('MaxId');
    if Fld.IsNull then
      Result := 1
    else
      Result := Fld.AsInteger + 1;
  finally
    FdqToDoMaxId.Close;
  end;
end;

內容解密:

  1. 開啟 FdqToDoMaxId 查詢並嘗試取得 MaxId 欄位的值。
  2. 使用區域性變數 Fld 簡化程式碼並提高可讀性。
  3. 如果 MaxIdNULL,表示資料表為空,因此傳回 1;否則,傳回目前最大值加 1。
  4. 確保在 finally 區塊中關閉查詢,以釋放資源。

新增 Todo 專案

設定插入查詢的 SQL 陳述式

INSERT INTO ToDos (Id, Title, Category) VALUES (:Id, :Title, :Category)

在查詢編輯器中設定引數的資料型別和方向:

  • IdftIntegerptInput
  • TitleftStringptInput
  • CategoryftStringptInput

實作 ToDoCreate 方法

function TDMToDo.ToDoCreate(AValue: TToDo): Integer;
begin
  var Id := GetNewId;
  FdqToDoInsert.ParamByName('Id').AsInteger := Id;
  FdqToDoInsert.ParamByName('Title').AsString := AValue.Title;
  FdqToDoInsert.ParamByName('Category').AsString := AValue.Category;
  try
    FdqToDoInsert.ExecSQL;
    Result := Id;
  except
    Result := -1;
  end;
end;

內容解密:

  1. 首先呼叫 GetNewId 以取得新的 Id 值。
  2. AValue 的屬性指定給對應的引數。
  3. 執行插入查詢,如果成功則傳回新的 Id,否則傳回 -1。

讀取 Todo 專案

設定查詢的 SQL 陳述式

SELECT Title, Category FROM ToDos WHERE Id = :Id

設定 Id 引數的資料型別為 ftInteger 和方向為 ptInput

實作 ToDoRead 方法

function TDMToDo.ToDoRead(Id: Integer; out AValue: TToDo): Boolean;
begin
  FdqToDoSelect.ParamByName('Id').AsInteger := Id;
  FdqToDoSelect.Open;
  try
    if FdqToDoSelect.RecordCount > 0 then
    begin
      Result := True;
      AValue.Id := Id;
      AValue.Title := FdqToDoSelect.FieldByName('Title').AsString;
      AValue.Category := FdqToDoSelect.FieldByName('Category').AsString;
    end
    else
      Result := False;
  finally
    FdqToDoSelect.Close;
  end;
end;

內容解密:

  1. 設定 Id 引數並開啟查詢。
  2. 如果找到記錄,則將其屬性指定給 AValue,並傳回 True;否則傳回 False

更新 Todo 專案

設定更新查詢的 SQL 陳述式

UPDATE ToDos SET Title = :Title, Category = :Category WHERE Id = :Id

設定引數的資料型別和方向,與插入查詢類別似。

實作 ToDoUpdate 方法

function TDMToDo.ToDoUpdate(AValue: TToDo): Boolean;
begin
  FdqToDoUpdate.ParamByName('Id').AsInteger := AValue.Id;
  FdqToDoUpdate.ParamByName('Title').AsString := AValue.Title;
  FdqToDoUpdate.ParamByName('Category').AsString := AValue.Category;
  try
    FdqToDoUpdate.ExecSQL;
    Result := True;
  except
    Result := False;
  end;
end;

內容解密:

  1. AValue 的屬性指定給對應的引數。
  2. 執行更新查詢,如果成功則傳回 True,否則傳回 False

刪除 Todo 專案

設定刪除查詢的 SQL 陳述式

DELETE FROM ToDos WHERE Id = :Id

設定 Id 引數的資料型別為 ftInteger 和方向為 ptInput

實作 ToDoDelete 方法

function TDMToDo.ToDoDelete(Id: Integer): Boolean;
begin
  FdqToDoDelete.ParamByName('Id').AsInteger := Id;
  try
    FdqToDoDelete.ExecSQL;
    Result := True;
  except
    Result := False;
  end;
end;

內容解密:

  1. 設定 Id 引數並執行刪除查詢。
  2. 如果成功則傳回 True,否則傳回 False

取得所有 Todo 專案列表

設定查詢的 SQL 陳述式

SELECT * FROM ToDos ORDER BY Id DESC

實作 ToDoList 方法

procedure TDMToDo.ToDoList(AList: TToDos);
var
  Item: TToDo;
begin
  if AList <> nil then
  begin
    AList.Clear;
    FdqToDoSelectAll.Open;
    try
      while not FdqToDoSelectAll.Eof do
      begin
        Item.Id := FdqToDoSelectAll.FieldByName('Id').AsInteger;
        Item.Title := FdqToDoSelectAll.FieldByName('Title').AsString;
        Item.Category := FdqToDoSelectAll.FieldByName('Category').AsString;
        AList.Add(Item);
        FdqToDoSelectAll.Next;
      end;
    finally
      FdqToDoSelectAll.Close;
    end;
  end;
end;

內容解密:

  1. 首先檢查 AList 是否為 nil,然後清除列表。
  2. 開啟查詢並逐一讀取記錄,將每筆記錄的屬性指定給 Item 並新增到列表中。
  3. 確保在 finally區塊中關閉查詢,以釋放資源。

至此,我們完成了資料存取層的構建。下一步是建立使用者介面(UI),以便利用資料存取層提供的服務。

實作資料驅動的使用者介面

資料存取邏輯實作與介面實作

在完成資料存取邏輯後,我們透過 IToDoData 介面提供資料存取功能。這樣可以確保 UI 邏輯只能透過此介面存取資料,實作真正的外掛架構。以下程式碼展示如何在主表單單元中實作 GetToDoData 方法以傳回資料存取介面的參考:

uses uDMToDo;

function TFormToDo.GetToDoData: IToDoData;
begin
  if DMToDo = nil then
    DMToDo := TDMToDo.Create(Application);
  Result := DMToDo;
end;

內容解密:

  • GetToDoData 方法確保 DMToDo 例項在需要時被建立,並傳回 IToDoData 介面的實作。
  • 這種設計允許在未來更改底層資料庫存取邏輯或資料函式庫平台,只要新的類別實作了 IToDoData 介面即可。

使用者介面設計

  1. 在表單上放置 TTabControl 元件,並將其對齊設為 Client,然後重新命名為 TbctrlMain
  2. 右鍵點選 TbctrlMain,新增兩個分頁,分別用於顯示所有待辦事項和編輯選定的待辦事項。
  3. 在每個分頁上放置一個 TToolBar 元件。
  4. TbctrlMainTabPosition 屬性設為 None 以隱藏分頁標籤。
  5. 將兩個分頁分別重新命名為 TbiListTbiEdit

切換分頁與事件處理

  1. 在表單上放置 TActionList 元件,雙擊它並新增兩個 ChangeTabActions 元件,分別命名為 CtaListCtaEdit,並將它們的 Tab 屬性連線到對應的分頁。
  2. 雙擊表單的 OnCreateOnDestroy 事件。在這裡,我們將建立和釋放一個全域的待辦事項列表。
procedure TFormToDo.FormCreate(Sender: TObject);
begin
  FToDos := TToDos.Create;
  TbctrlMain.ActiveTab := TbiList;
  RefreshList;
end;

procedure TFormToDo.FormDestroy(Sender: TObject);
begin
  FToDos.Free;
end;

內容解密:

  • FormCreate 事件中,建立待辦事項列表並顯示第一個分頁,接著呼叫 RefreshList 方法來更新列表。
  • FormDestroy 事件中,釋放待辦事項列表。

使用 TListView 顯示資料

  1. 在第一個分頁上放置一個 TListView 元件,重新命名為 LstvwToDos,並將其對齊設為 Client
  2. 調整 LstvwToDosItemAppearance 屬性為 DynamicAppearance 以自訂列表專案的外觀。
  3. 在 Object Inspector 中新增自訂元素,如標題、類別和附件。
// 示例程式碼:設定 TListView 的動態外觀
// 將 Appearance 設定為 DynamicAppearance 後,可以透過程式碼或設計模式調整專案外觀

內容解密:

  • 使用 DynamicAppearance 可以靈活地定義列表專案的外觀和佈局。
  • 自訂元素名稱如 TitleCategoryMore 可在程式碼中使用。

自訂列表專案外觀

  1. 將列表專案的高度設為 64,並進入設計模式以調整各元素的位置和大小。
  2. 自訂各元素的字型樣式,如將標題設為粗體,類別設為斜體。
// 示例程式碼:自訂 TListView 專案外觀
// 在設計模式下調整元素佈局和樣式

內容解密:

  • 在設計模式下,可以直觀地調整列表專案的佈局和樣式,以滿足應用程式的需求。

在 Delphi 中建立待辦事項應用程式:資料驅動的使用者介面實作

在開發行動應用程式時,建立一個資料驅動的使用者介面(UI)是非常重要的。本篇文章將介紹如何在 Delphi 中實作一個待辦事項應用程式的 UI,並將其與資料函式庫進行繫結。

重新整理列表方法實作

首先,我們需要實作 RefreshList 方法,該方法負責從資料函式庫中讀取資料並將其顯示在列表檢視中。

procedure TFormToDo.RefreshList;
begin
  GetToDoData.ToDoList(FToDos);
  LstvwToDos.BeginUpdate;
  try
    LstvwToDos.Items.Clear;
    for var Todo in FToDos do
    begin
      var Item := LstvwToDos.Items.Add;
      Item.Tag := Todo.Id;
      Item.Objects.FindObjectT<TListItemText>('Title').Text := Todo.Title;
      Item.Objects.FindObjectT<TListItemText>('Category').Text := Todo.Category;
    end;
  finally
    LstvwToDos.EndUpdate;
  end;
end;

內容解密:

  1. GetToDoData.ToDoList(FToDos);:呼叫 GetToDoData 物件的 ToDoList 方法,將待辦事項列表讀取到 FToDos 變數中。
  2. LstvwToDos.BeginUpdate;LstvwToDos.EndUpdate;:這兩個方法用於暫停和還原列表檢視的更新,以提高效能。
  3. for var Todo in FToDos do:遍歷 FToDos 列表中的每個待辦事項物件。
  4. Item.Objects.FindObjectT<TListItemText>('Title').Text := Todo.Title;:將待辦事項的標題和類別顯示在列表檢視中。

新增、刪除和編輯待辦事項

接下來,我們將介紹如何新增、刪除和編輯待辦事項。

新增待辦事項

  1. 在工具欄上放置一個 TSpeedButton 元件,並將其 StyleLookup 屬性設定為 Addtoolbutton
  2. 在動作列表中新增一個動作,並將其連線到新增按鈕。
procedure TFormToDo.ActAddExecute(Sender: TObject);
begin
  FCurrentId := -1;
  EdtTitle.Text := '';
  EdtCategory.Text := '';
  CtaEdit.ExecuteTarget(self);
end;

內容解密:

  1. FCurrentId := -1;:將 FCurrentId 設定為 -1,表示正在新增一個新的待辦事項。
  2. EdtTitle.Text := '';EdtCategory.Text := '';:清空標題和類別編輯框。
  3. CtaEdit.ExecuteTarget(self);:切換到編輯標籤頁。

儲存待辦事項

儲存待辦事項的邏輯取決於目前的操作是新增還是更新。

procedure TFormToDo.ActSaveExecute(Sender: TObject);
var
  Todo: TToDo;
begin
  Todo.Title := EdtTitle.Text;
  Todo.Category := EdtCategory.Text;
  if FCurrentId < 0 then
    GetToDoData.ToDoCreate(Todo)
  else
  begin
    Todo.Id := FCurrentId;
    GetToDoData.ToDoUpdate(Todo);
  end;
  RefreshList;
  CtaList.ExecuteTarget(self);
end;

內容解密:

  1. if FCurrentId < 0 then:如果是新增操作,則呼叫 ToDoCreate 方法建立新的待辦事項。
  2. else:如果是更新操作,則呼叫 ToDoUpdate 方法更新現有的待辦事項。
  3. RefreshList;:重新整理列表檢視。
  4. CtaList.ExecuteTarget(self);:切換回列表標籤頁。

刪除待辦事項

刪除待辦事項的邏輯如下:

procedure TFormToDo.ActDeleteExecute(Sender: TObject);
begin
  if FCurrentId > 0 then
  begin
    GetToDoData.ToDoDelete(FCurrentId);
    RefreshList;
  end;
  if TbctrlMain.ActiveTab <> TbiList then
    CtaList.ExecuteTarget(self);
end;

內容解密:

  1. if FCurrentId > 0 then:只有當 FCurrentId 大於 0 時,才執行刪除操作。
  2. GetToDoData.ToDoDelete(FCurrentId);:呼叫 ToDoDelete 方法刪除指定的待辦事項。
  3. RefreshList;:重新整理列表檢視。