2011-03-01 18 views
8

我有一些使用Delphi自定義數據庫持久性方案編寫的業務對象,最終可以滿足我的需求。太好了。現在是GUI實現的時候了。在這裏開始的問題。「Object Aware」GUI控件

如何正確地將我的對象綁定到GUI?

我無法使用數據感知控件,因爲我將所有數據訪問組件都分離到了ORM層中,因此我開始使用RTTI單元編寫一些「Object Aware」控件(我正在使用Delphi 2010),但我有感覺我走錯了路...

有關如何解決這個問題的一些想法,只使用VCL控件?

回答

6

您有幾種鏈接ORM與用戶界面的模式。

參見例如Model GUI Mediator模式。總之,你寫了一個觀察者,它將ORM內容反映到UI組件中,反之亦然。這已經在例如tiOpf framework for Delphi(該鏈接具有視頻)中實施。

另一種方法是將你的數據在運行時映射:您設計的形式,就像往常一樣,那麼您填寫OnShow事件的內容,那麼「保存」或「OK」按鈕將驗證然後保存將內容放入ORM記錄中。這就是main Sample application of our framework所做的。易於在這個簡單的示例代碼,但如果你有很多領域和驗證操作可能會導致意大利麪代碼。

最後一種方法是讓你的ORM創建表單。

在我們的框架中,您可以在專用結構中定義關於每個表的一些UI屬性。然後你的ORM對象的a single unit will create a form with all editable fields。鏈接到其他記錄將顯示爲組合框,布爾值作爲複選框,設置爲無線電框等。然後,過濾(例如,從左側或右側的空間修剪文本字段)和驗證(例如,確保字段值是唯一的或有效的IP地址)被處理not in the UI part, but in the business logic itself, i.e. the ORM

恕我直言,它是強制性的,以保持一個真正的多層架構。也就是說,UI必須主要依賴業務邏輯。例如,數據驗證必須是ORM的一部分,而不是UI的一部分。例如,如果您決定將Web客戶端添加到您的Delphi客戶端應用程序中,則不必再次對驗證進行編碼:這對兩個客戶端都是通用的,與UI實現細節分開。

+0

儘管第二種方法在我的特殊情況下可以更容易實現,但我認爲我會採取第一種方法,因爲從長遠角度來看,製作和維護複雜表單會更容易。我閱讀鏈接上的文檔,MGM模式似乎適合作爲我的手套。非常感謝。 – 2011-03-01 21:25:09

0

目前沒有辦法只使用VCL控件來做到這一點。我知道Lazarus有一套基於RTTI的數據感知控件;你可能想看看他們的一些基本想法。但起初比你想象的要困難得多。例如,與數據集不同,對象在其成員值發生更改時沒有內置的信號機制。這意味着,除非你的數據綁定控件完全擁有這個對象,並且沒有其他人可以訪問它,否則其他代碼可能會改變某個值,然後這個改變就不會反映在UI中。

在過去的幾年裏,我聽說過Delphi團隊的各種事情,關於擴展對象模型或RTTI模型以實現更好的數據綁定,但無論如何還需要幾年時間。

+2

我在1998年圍繞這些原則開發了一個完整的對象感知框架(Delphi 4),以及一個等效的數據映射層。然而,正確地完成這項工作的數量遠遠超出了您的想象 - 給自己6-12個月的時間來開發一些可以在非平凡的商業應用中使用的東西。雖然你可以在一天左右的時間內對這些東西進行原型設計,但這絕對沒有說明正確實施設計所需的工作量。 – Misha 2011-03-01 22:28:49

2

你能做什麼(雖然我沒有代碼樣本)是使用

  • 類傭工或攔截器類
  • 單域對象和/或域名的對象列表綁定接口的組合

類助手的缺點是他們沒有得到官方的支持,你不能在你正在幫助的類中添加任何字段。

攔截器類是簡單的子類具有相同的名稱作爲自己的祖先:

uses 
    stdctrls; 

type 
    TButton = class(stdctrls.TButton) 
    end; 

你可以把攔截器類在自己的單位和使用等。無論你想要的。只要確保這些單元包含在標準單元之後,那麼您的後代就可以在運行時使用。

攔截器類的好處:

  • 您可以繼續使用設計標準的VCL或第三方控件的UI。
  • 你得到了後代的所有優點。
  • 您不需要創建或安裝自己的控件。
  • 不需要特殊的映射器類或使用RTTI。
  • 輕鬆(當然,相對容易地)集成到一起儒略巴克納爾的在(不同)的Delphi雜誌,對本文的線(DUnit-)可測試的用戶界面如本的問題/答案簡稱:Unit-testing mouse event handlers

與結合界面/命令接口攔截控制的僞樣品:

uses 
    stdctrls; 

type 
    ICommandAction = interface(IInterface) 
    function IsEnabled: Boolean; 
    procedure Execute; 
    procedure Update; 
    end; 

    IBindSingle = interface(IInterface) 
    function GetValueFromControl: string; 
    procedure LoadValueIntoControl(const aValue: string); 
    end; 

    TButton = class(stdctrls.TButton, ICommandAction) 
    protected 
    function IsEnabled: Boolean; 
    procedure Execute; 
    procedure Update; 
    end; 

    TEdit = class(stdctrls.TEdit, IBindSingle) 
    function GetValueFromControl: string; 
    procedure LoadValueIntoControl(const aValue: string); 
    end; 

實現可以是沿着線:

function TButton.IsEnabled: Boolean; 
    begin 
    Result := Self.Enabled; 
    end; 

    procedure TButton.Execute; 
    begin 
    Self.Action.Execute; 
    end; 

    procedure TButton.Update; 
    begin 
    Self.Action.Update; 
    end; 

    function TEdit.GetValueFromControl: string; 
    begin 
    Result := Self.Text; 
    end; 

    procedure LoadValueIntoControl(const aValue: string); 
    begin 
    Self.Text := aValue; 
    end; 
1

我現在的客戶在過去(我來之前)做過他們自己的「mapper」類。 他們的數據對象具有字段(它們是對象),您可以將這些字段映射到控件。

edtTarraCode: TAdvEdit; 

procedure TframTarraTab.InitMapping; 
begin 
    ... 
    Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID); 
    ... 
end; 

每控件創建一個簡單的「映射」類:

TMappingAdvEdit = class(TBaseEditMapping) 
protected 
    procedure InitControl; override; 

    procedure AppData2Control; override; 
    procedure Control2AppData; override; 
end; 

沒有火箭sience,也許更好的解決方案在平均可我使用MVC式的方法擴展框架時間(這在D6和更低的工作:-)),但它對客戶足夠好。

Btw:還使用了一個數據對象生成器。因此,如果數據庫中的某個字段發生更改(例如tarra.tarid更改爲tareID),則會出現編譯器錯誤,因爲「tarid」不再存在。這比「固定字符串」映射(運行時錯誤)好得多。

+0

這是一個有趣的解決方案,但它需要對我的業務對象進行完全重新設計......它會更加複雜,但我認爲我會嘗試按A. Bouchwz所建議的模型GUI介體模式。許多泰克人,男人。 – 2011-03-01 21:01:17

0

查看EverClassy數據集http://www.inovativa.com.br。它可能會滿足你的需求。 EverClassy數據集是一個Delphi數據集,用於填充數據庫系統中的對象而不是記錄。

有了這個組件,您將有機會將您的域對象與數據軟件組件進行互操作,這將爲您提供構建GUI的強大功能。