Khắc phục lỗi SmartBinding trong kbmMW 5.10.10

Cập nhật 2020-01-09: Phiên bản kbmMW 5.10.20 đã giải quyết vấn đề này. Nếu bạn đã nâng cấp, nội dung dưới đây không cần thiết.

Phiên bản kbmMW 5.10.10 cuối cùng cũng phát hành với nhiều cải tiến, đặc biệt là hỗ trợ SmartBinding cho ListView. Tuy nhiên, trong quá trình kiểm tra, tôi phát hiện một lỗi nghiêm trọng liên quan đến cơ chế ghi dữ liệu hai chiều.

Hãy xem xét đoạn mã sau:

bnd := Binding.Bind(dataset, 'f1', Edit1, 'Text', [mwboTwoWay]);

Mục đích là liên kết trường f1 của dataset với thuộc tính Text của Edit1 theo hướng hai chiều (two-way binding), cho phép chỉnh sửa dữ liệu qua ô Edit như DBEdit trong VCL. Tuy nhiên, hành vi thực tế lại ngược lại: giá trị từ Edit1.Text được ghi vào trường f1 thay vì ngược lại.

Minh họa lỗi: Ban đầu, Edit1.Text là "Edit1", trong khi giá trị trường f1 là 0. Kết quả là "Edit1" bị ghi đè vào dataset, khiến ListView hiển thị sai dữ liệu.

Sau khi phản hồi nhưng không được xác nhận là lỗi, tôi đã tự phân tích mã nguồn SmartBinding và tìm ra giải pháp. Tất cả các overload của constructor TkbmMWBinding.Create cần được sửa như sau:

constructor TkbmMWBinding.Create(const ABindings: TkbmMWBindings;
                                 const ASource: TValue; const ASourceMember: string;
                                 const ADestination: TValue; const ADestinationMember: string;
                                 const AOptions: TkbmMWBindingOptions = []);
var
   v: TValue;
   b: boolean;
begin
     inherited Create;
     // ... các xử lý khác ...
     UpdateInfo;

     // Thêm dòng này để đồng bộ giá trị ban đầu từ nguồn sang đích
     FDestinationProxy.SetValue(FSourceProxy.Value, true);
end;

Kết quả sau khi sửa: Cả hai ô Edit và ListView đều hiển thị chính xác dữ liệu từ dataset.

Phân tích nguyên nhân: Quá trình binding tạo ra đối tượng TkbmMWBinding nhưng không sao chép giá trị ban đầu từ nguồn sang đích. Khi SmartBinding thực hiện đồng bộ, nó sai lầm ghi giá trị mặc định của đích (Destination) vào nguồn (Source), dẫn đến lỗi logic. Sửa lỗi bằng cách thêm bước khởi tạo giá trị.

Cập nhật ngày 2019-11-27

Bạn Aone đề xuất cách giải quyết thay thế:

procedure TForm1.Button9Click(Sender: TObject);
begin
  bnd := nil;
  Binding.Clear;
  //ListView1.Items.Clear;

  dataset := mt;

  dataset.DisableControls;  // Tạm dừng cập nhật UI

  bnd := Binding.Bind(dataset, 'f1', Edit1, 'Text', [mwboTwoWay]);
  Binding.Bind(dataset, 'f2', Edit2, 'Text', [mwboTwoWay]);
  Binding.Bind(dataset, 'f3', DateEdit1, 'Date', [mwboTwoWay]);

  bnd := Binding.Bind(dataset, 'f1', ListView1, '#Text1');
  Binding.Bind(dataset, 'f2', ListView1, '#Text2');
  Binding.Bind(dataset, 'f3', ListView1, '#Text3');

  Binding.Bind(dataset, '@', ListView1, '@', [mwboTwoWay]);

  dataset.EnableControls;  // Khôi phục cập nhật UI
end;

Lỗi mới trong kbmMW 5.10.10 (vẫn tồn tại ở 5.10.20)

Đoạn mã sau gây lỗi "dataset not in edit mode":

procedure TForm1.Button11Click(Sender: TObject);
begin
    bnd.Navigator.Append;
    bnd.Navigator.Value['f1'] := '101';
    bnd.Navigator.Value['f2'] := 101;
end;

Sửa lỗi: Thêm kiểm tra trạng thái dataset trước khi ghi:

procedure TkbmMWBindingDatasetNavigator.SetValue(const AName: string; const AValue: TValue);
var
   fld: TField;
begin
     if IsValid then
     begin
          fld := FDataset.FindField(AName);
          if fld <> nil then
          begin
             // Kiểm tra và chuyển sang chế độ Edit nếu cần
             if not (FDataset.State in [dsInsert, dsEdit]) then
               FDataSet.Edit;

             fld.AsVariant := TkbmMWRTTI.ConvertValue2Variant(AValue, nil);
          end;
     end;
end;

Đã gửi đề xuất sửa lỗi cho tác giả, chờ phản hồi.

Thẻ: kbmMW SmartBinding Delphi ListView data-binding

Đăng vào ngày 23 tháng 6 lúc 22:28