Đồng bộ hóa dữ liệu giữa TDataSet và TListView thông qua SmartBinding trong kbmMW

Phiên bản kbmMW 5.10.20 đã khắc phục một hạn chế quan trọng: khi thêm hoặc xóa bản ghi trên TDataSet, danh sách hiển thị (TListView) giờ đây tự động cập nhật mà không cần gọi thủ công Refresh. Cơ chế này dựa vào việc mở rộng giao diện IkbmMWBindingNavigator với các phương thức điều khiển trực tiếp dữ liệu.

IkbmMWBindingNavigator = interface
  // … các phương thức khác …
  function Delete: Boolean;
  function Insert: Boolean; overload;
  function Insert(const AFieldNames: array of string; 
                 const AFieldValues: array of TValue): Boolean; overload;
  function Append: Boolean; overload;
  function Append(const AFieldNames: array of string; 
                 const AFieldValues: array of TValue): Boolean; overload;

  procedure Refresh;

  property Value[const AFieldName: string]: TValue read GetValue write SetValue;
end;

Các phương thức như Append, Delete, và Insert cho phép thao tác dữ liệu thông qua lớp ràng buộc — thay vì gọi trực tiếp lên dataset — từ đó đảm bảo tính nhất quán giữa nguồn dữ liệu và giao diện người dùng.

Để thiết lập kết nối hai chiều giữa TDataSet (ví dụ: mt) và TListView, ta thực hiện như sau:

procedure TForm1.BindDataSetToListView;
begin
  Binding.Clear;
  bnd := nil;

  // Gắn các trường riêng lẻ cho các control nhập liệu
  bnd := Binding.Bind(mt, 'f1', Edit1, 'Text', [mwboTwoWay]);
  Binding.Bind(mt, 'f2', Edit2, 'Text', [mwboTwoWay]);
  Binding.Bind(mt, 'f3', DateEdit1, 'Date', [mwboTwoWay]);

  // Gắn đồng thời nhiều cột vào TListView
  Binding.Bind(mt, 'f1', ListView1, '#Text1');
  Binding.Bind(mt, 'f2', ListView1, '#Text2');
  Binding.Bind(mt, 'f3', ListView1, '#Text3');

  // Kích hoạt ràng buộc toàn cục (bản ghi hiện tại)
  Binding.Bind(mt, '@', ListView1, '@', [mwboTwoWay]);
end;

Sau khi gọi hàm trên, TListView sẽ phản ánh đầy đủ các bản ghi trong dataset. Việc di chuyển vị trí bản ghi bằng mt.Next hoặc bnd.Navigator.Next đều dẫn đến sự đồng bộ tức thì giữa dữ liệu nền và giao diện.

Để xóa bản ghi hiện tại, chỉ cần gọi:

procedure TForm1.DeleteCurrentRecord;
begin
  bnd.Navigator.Delete; // Không cần Refresh
end;

Tương tự, để chèn bản ghi mới:

procedure TForm1.AddNewRecord;
begin
  bnd.Navigator.Append;
  bnd.Navigator.Value['f1'] := '101';
  bnd.Navigator.Value['f2'] := 101;
  bnd.Navigator.Value['f3'] := Now;
end;

Lưu ý rằng nếu thao tác trực tiếp trên dataset (ví dụ: mt.Append), bạn phải gọi bnd.Navigator.Refresh để cập nhật lại trạng thái binding — trong khi cách dùng Navigator thì tự động xử lý điều này.

Một điểm cần lưu ý về tính ổn định: phiên bản 5.10.20 vẫn tồn tại lỗi nhỏ khi gán giá trị qua thuộc tính Value[] — dataset có thể chưa ở trạng thái chỉnh sửa. Để khắc phục, sửa phương thức SetValue trong TkbmMWBindingDatasetNavigator như sau:

procedure TkbmMWBindingDatasetNavigator.SetValue(
  const AFieldName: string; 
  const AValue: TValue);
var
  LField: TField;
begin
  if IsValid then
  begin
    LField := FDataset.FindField(AFieldName);
    if Assigned(LField) then
    begin
      if not (FDataset.State in [dsEdit, dsInsert]) then
        FDataset.Edit; // Đảm bảo dataset ở trạng thái có thể ghi

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

Về quản lý tài nguyên, từ phiên bản 5.10.20, việc giải phóng binding trở nên an toàn hơn. Tuy nhiên, vẫn nên chủ động dọn dẹp khi đóng form:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  bnd := nil;
  Binding.Clear;
  Binding.Shutdown; // Gọi shutdown để dọn sạch event và thread nội bộ
end;

Một vấn đề thường gặp với các control nhập liệu có định dạng (như TDateEdit) là ràng buộc hai chiều gây lỗi do giá trị chưa hợp lệ trong quá trình nhập. Giải pháp đơn giản là tạm ngắt binding khi focus vào control:

procedure TForm1.DateEdit1Enter(Sender: TObject);
begin
  Binding.EnableByName('DateEdit1', False);
end;

procedure TForm1.DateEdit1Exit(Sender: TObject);
begin
  Binding.EnableByName('DateEdit1', True);
end;

Ngoài ra, phiên bản 5.10.20 còn bổ sung phương thức Shutdown cho các thành phần như TkbmMWScheduler, TkbmMWBindings, và TkbmMWEvents, giúp ứng dụng thoát một cách sạch sẽ, tránh rò rỉ tài nguyên hoặc treo tiến trình nền.

Thẻ: kbmMW SmartBinding TDataSet TListView Delphi

Đăng vào ngày 19 tháng 5 lúc 17:05