LiveBindings 綁定對象裏面的圖片字段,對象要平臺中立

問題來源:

假設我有一個數據對象 TnUser = class

裏面需要存儲圖片。

因爲 Delphi 的 VCL 和 FMX 對圖片的處理,引用的單元不同,爲了讓這個 TnUser 更單純,平臺中立,它只能存儲圖片的二進制數據,而不是 TBitmap 或者 TJpeg 這樣的對象。

因此,我在這裏直接用一個 TMemroyStream 來存儲圖片的數據。

 

問題來了,我在 ViewModel 裏面通過拖放 AdapterBindSource1 和 DataGeneratorAdapter1 並且在 DataGeneratorAdapter1 裏面定義對應 TnUser 的字段,圖片字段只有 Bitmap 類型。這裏面沒有 Blob 類型也沒有 Stream 類型的字段。

 

在 View 模塊也就是界面 Form 上面拖放一個 TImage,一個 BindingsList1,可視化創建 LiveBinding 綁定。設計期可以看到圖片。

程序運行時創建 TnUser 的實例,但不給 Photo (TMemoryStream) 賦值。界面上另外兩個 string 類型的字段顯示出來了,沒有圖。到這裏一切順利。

 

點擊界面按鈕,加載一個 JPEG 圖片到 FUser 的 Photo 字段,也就是把 Jpeg 文件的數據寫入 MemoryStream,刷新綁定,則出來一個類型轉換錯誤。Image 不能對應 Stream。

 

解決辦法

如何解決這個問題。當然我首先想到運行期做類型轉換。在界面上,雙擊 BindingsList1,彈出窗口,裏面就是可視化創建的連接的對象。選中綁定圖片的那個對象,在屬性面板裏面,切換到事件,可以看到幾個事件,其中兩個是:OnAssignedValue 和 OnAssigningValue,顯然應該在 OnAssigningValue 事件裏面寫代碼轉換數據。但因爲我還不熟悉 TValue 的操作,一時搞不定轉換的數據如何寫進去,於是開始想別的辦法。

 

以下是別的辦法的測試結果:

爲了讓數據對象 TnUser 平臺獨立,也就是不要包含 VCL 或者 FMX 的代碼,

其中的 Photo 採用 TStream 直接存儲數據。

 

如果把這個 Photo 屬性直接 LiveBinding 到界面上的 Image,則運行時會說類型轉換錯誤。

那麼,這裏如何插入我自己寫的類型轉換代碼?

在 LiveBinding 的框架裏面,暫時找不到地方插入代碼。也搜索不到相關文檔。

--- 搞定。2020-2-13 凌晨 3 點。類型轉換隻需要在連接的事件 OnAssigningValue 裏面寫,去更改其 TValue 就可以了。

 

因此,我測試了以下方法:

 

1. 給 TnUser 增加一個 Helper 類,爲這個類增加一個 Photo2 屬性,這個屬性是 TBitmap;(本模塊是 ViewModel,可以平臺相關)

2. 綁定 Image 到 Photo2。但是,運行時,不會觸發到 Helper 類的方法。

2.1. 增加一個測試按鈕,測試 FUser.Photo2,可以觸發 Helper 類的方法,可以獲得 Photo2 的屬性並顯示出來,說明 Helper 類沒寫錯。

也就說明 LiveBinding 在執行綁定的時候不認識 Helper 類。

3. 本模塊自己重新引入相關屬性,然後綁定到本模塊而不是 FUser,運行時出異常;原因是 AdapterBindSource1.OnCreateAdapter 先於本模塊的創建。

3.1. 就算把 AdapterBindSource1.AutoActive 設置爲 True,它的事件方法依然先於本模塊的 Create 被觸發調用。

因此,不能採用設置 AutoActive 爲 False 然後在運行期設置爲 True 來解決這個問題。

 

4. 繼承 TnUser,在繼承的類裏面實現 Photo4 屬性,這個屬性是 TBitmap 或者 TJpeg,綁定此對象,都能顯示。

4.1. 爲了跨平臺而繼承,不是好的解決方法。

 

做完上面那些測試後,回到數據轉換,仔細查看 TValue,發現可以寫入數據。

正確的解決方法

雙擊界面上的 BindingsList1 彈出窗口:

選中上面關於圖片的連接,看看它的屬性面板裏面的事件:

 

 

雙擊 OnAssigningValue 事件,在 IDE 創建的代碼框架裏面寫如下代碼:

procedure TForm1.LinkControlToField1AssigningValue(Sender: TObject;
  AssignValueRec: TBindingAssignValueRec; var Value: TValue;
  var Handled: Boolean);
var
  Jpg: TJpegImage;
begin
  if Value.IsObject then
  begin
    if Value.AsObject is TStream then
    begin
      TStream(Value.AsObject).Position := 0;
      Jpg := TJpegImage.Create;
      try
        Jpg.LoadFromStream(TStream(Value.AsObject));

        Value := TValue.From(Jpg);
      finally
        //Jpg.DisposeOf;    這裏不能釋放 Jpg 對象,否則會 AV。因此,程序在這裏需要用一個 TForm1 裏面定義的 Jpg 對象反覆使用而不是每次都在這裏創建新的。
      end;
    end;

  end;

end;

 上述代碼搞定,完成了綁定的對象的屬性是 TStream 轉換爲目標需要的 TJpegImage 或者 TBitmap 的問題。這裏主要的問題是對 TValue 的操作。

在 VCL 框架下,這裏是 TJpegImage 或者是 TBitmap 都能夠讓界面上綁定的 TImage 正確顯示圖片。

這樣,就實現了數據對象的代碼平臺中立。而界面 View 肯定是和平臺相關的。連 ViewModel 都無需關注平臺相關代碼。

另:關於如何綁定一個對象,請參考俺的上一篇 Blog。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章