【问题标题】:How to modify a cell value in Delphi TDBGrid如何在 Delphi TDBGrid 中修改单元格值
【发布时间】:2012-09-19 15:47:11
【问题描述】:

我必须在 VCL TDBGrid (Delphi XE2) 中显示一些修改后的 'masked' 值,即:将 'password' 更改为 'xxxxxxxx' 或将大写的 'pass' 更改为 'PASS' 或其他。 由于我的字段是动态创建的(但名称是编码的,所以我知道如何以及何时屏蔽它们,即:xxxx_PASSW 用于密码字段)我不能使用(我认为)OnGetText 事件。

那么最有效的方法是什么(因为我还使用 OnDrawColumnCell 进行一些演示修改,我更愿意使用它)?

【问题讨论】:

  • 有 3 种方法可以做到这一点。 1. 在 dbgrid 使用的数据集上为密码创建一个计算字段。 2、在sql select语句上为密码创建一个计算字段。或 3. 使用 dbgrid 的 onDrawColumCell/Data 事件,如您上面所述。但我个人不喜欢将密码存储在数据库中,而是密码的哈希编码版本。因为哈希码是单向函数(即不能从哈希码中得到原始密码),只有正确的密码才能产生相同的哈希码,所以使用起来更安全。]
  • @Hendra 当然,密码就是一个例子,我的需求太复杂了,无法清楚地解释。但是我知道如何使用 onDrawColumCell 更改单元格的显示,但不知道文本内容,任何示例或教程?

标签: delphi delphi-xe2 tdbgrid


【解决方案1】:

至少有 3 种方法可以做到这一点,我将通过从数据库中屏蔽密码字段来进行说明。我正在使用 sql server 为 sql 方言。

1.在 sql 字符串上定义一个计算字段。

select field1, field2, '********' as maskedPwd from table1;

然后,右键单击 dbgrid,选择列编辑器。在 dbgrid 的列编辑器中,只需选择 maskedPwd 列而不是真正的密码列。现在 dbgrid 将显示掩码值而不是密码。

2。在 dbgrid 使用的数据集上定义计算字段。

只需右键单击数据集,然后使用字段编辑器创建一个新的计算字段(例如 maskedPwd2)。然后是数据集的onCalcField事件,编写代码设置maskedPwd2的值,即

procedure TForm1.ADOQuery1CalcFields(DataSet: TDataSet);
begin
  DataSet.FieldByName('maskedPwd2').AsString := '********';
end;

确保在 dbgrid 的列编辑器中包含 maskedPwd2。

3.在 dbgrid 的 onDrawColumnCell 事件上编写自定义文本。

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  grid : TDBGrid;
  maskValue : String;
  aRect : TRect;
begin
  maskValue := '********';
  aRect := Rect;
  grid := sender as TDBGrid;

  if column.FieldName = 'password' then
  begin
    grid.Canvas.FillRect(Rect);
    DrawText(grid.Canvas.Handle, PChar(maskValue), Length(maskValue), aRect,
      DT_SINGLELINE or DT_LEFT or DT_VCENTER);
  end;
end;

请注意,上面的代码仅显示被屏蔽的值,但如果网格是可编辑的,则当单元格被聚焦/编辑时,真正的密码值将可见。

为了解决这个问题,在表单上放置一个 TEdit,清除 text 属性,将 PpasswordChar 属性设置为 '*',并将 visible 设置为 false。现在它已准备好用作单元格的内置编辑器的替代品。现在,我们需要一些粘合逻辑,即

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  grid : TDBGrid;
  maskValue : String;
  aRect : TRect;
begin
  maskValue := '********';
  aRect := Rect;
  grid := sender as TDBGrid;

  if column.FieldName = 'password' then
    if gdfocused in State then
      begin
        Edit1.Left := Rect.Left + grid.Left + 1;
        Edit1.Top  := rect.Top + grid.Top + 1;
        Edit1.Width := Rect.Right - Rect.Left + 2;
        Edit1.Height := Rect.Bottom - Rect.Top + 2;
        Edit1.Clear;
        Edit1.Visible := True;
      end
    else
      begin
        grid.Canvas.FillRect(Rect);
        DrawText(grid.Canvas.Handle, PChar(maskValue), Length(maskValue), aRect,
          DT_SINGLELINE or DT_LEFT or DT_VCENTER);
      end
  else
    grid.DefaultDrawColumnCell(Rect, DataCol, Column, state);
end;

procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
  Edit1.Visible := False;
end;

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = Chr(9) then Exit;

  if (Sender as TDBGrid).SelectedField.FieldName = 'password' then
  begin
    Edit1.SetFocus;
    SendMessage(Edit1.Handle, WM_CHAR, word(Key), 0);
  end;
end;

procedure TForm1.Edit1Change(Sender: TObject);
begin
  if DBGrid1.DataSource.State in [dsEdit, dsInsert] then
    DBGrid1.DataSource.DataSet.FieldByName('password').AsString := Edit1.Text;
end;

procedure TForm1.Edit1Enter(Sender: TObject);
begin
  DBGrid1.DataSource.Edit;
end;

请注意,上面的代码并不完美,但精髓就在那里。我把它留给你锻炼。

【讨论】:

    【解决方案2】:

    我会为我的数据集中的密码字段写一个OnGetText,因为该字段的值根本不应该显示在任何控件中

    【讨论】:

    • 当然,但我的问题不完整(抱歉),我的 DBGrid 是动态创建的,我可能有不同的 TField,因此我无法分配 OnGetText。我考虑改为 OnDrawColumnCell。
    • 我从未尝试过。我想你可以使用这个:with (Sender as TDBGrid).Canvas do begin Font.Color := clWhite; Brush.Color := clWhite; Brush.Style := bsSolid; end; 来隐藏你想要的值,但我不确定如何在那里更改字段的实际文本。将一个OnGetText 分配给多个TFields 不是更容易吗?
    • 现在我明白了为什么你不能使用OnGetText,但是你可以很容易地为你的密码OnDrawColumnCell 中为fontbrush 分配相同的颜色> 列。
    • 是的,但密码只是一个例子,我想对显示的值应用一个函数,它可能是密码的掩码,但它可能是大写或其他。
    • @phil - 您可以将事件处理程序分配给动态创建的对象。 MyField.OnGetText:=PassFieldGetText 其中 PassFieldGetText 是某个类的过程,其签名为 (Sender: TField; var Text: string; DisplayText: Boolean);
    【解决方案3】:

    您是否必须屏蔽整列中的所有值?在这种情况下,如果您知道要为哪个 TField(或字段名)执行此操作:尝试使用修改后的值动态创建计算字段并将其显示在列中。

    【讨论】:

    • 我没有时间做一个例子。在您创建需要屏蔽的 TFields 时,还要执行 TFields.Add 以添加计算字段。浏览 TDBGrid.Columns,替换该列使用的字段。当然,您还必须将 OnCalcFields 事件处理程序挂钩到您的数据集(也许您已经有一个)。希望这能让你开始。
    【解决方案4】:

    我修改了上面的代码来显示和隐藏密码。如果用户单击“密码”单元格,它将显示它,当他们单击该单元格时,它将再次隐藏它。

    // Add a cell click event from the TDBGrid
    procedure TForm1.DBGrid1CellClick(Column: TColumn);
    begin
    if DBGrid1.SelectedField.FieldName = 'password' then
    Edit1.Text := Your_Table_Name.FieldByName('password').AsString;
    Edit1.PasswordChar:=#0;
    end;
    
    // Change the edit1change event to this
    procedure TForm1.Edit1Change(Sender: TObject);
    begin
    if DBGrid1.DataSource.State in [dsEdit, dsInsert] then
    Your_Table_Name.FieldByName('password').AsString := Edit1.Text;
    Edit1.PasswordChar:=#0;
    end;
    
    // You should change colexit event to read like this
    procedure TForm1.DBGrid1ColExit(Sender: TObject);
    begin
    if DBGrid1.SelectedField.FieldName = 'password' then
    Edit1.Visible := False;
    end; 
    

    无需花费太多精力就可以将其变成一个很酷的密码字段。

    忘记了关于 DBGrid Draw Column Cell 事件的一件事,您应该更改 Edit1.Clear;到 Edit1.Text := Your_Table_Name.FieldByName('Password').AsString;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-07
      • 1970-01-01
      相关资源
      最近更新 更多