如何使 Delphi 表单上的所有控件都非 editable/changeable 但仍允许用户复制内容

How do I make all controls on a Delphi form non editable/changeable but still allow user to copy contents

我有一个显示数据库数据的表单,它有几个按钮和几个面板。这些面板包含各种组件,特别是 TEdits、TComboBox、TDateTimePicker、TCheckBox、TListBox 和 TstringGrid。

当打开表单以便用户可以查看但不能编辑数据时,我目前禁用了除按钮之外的所有组件

for i := 0 to FrmAddNewMember.ComponentCount-1 do
    if FrmAddNewMember.Components[i] is TPanel then
      (FrmAddNewMember.Components[ i ] as TPanel).enabled := false;

除了我现在希望用户能够将 TEdits 中的文本、TDateTimePicker 中的日期、TComboBox 中的选定项目等复制到剪贴板外,这工作正常,但仍然不更改它.

我将代码更改为以下代码,将 TEdits 设置为只读。这可以满足我对 TEdits 的要求,但其他类型的控件没有只读 属性,因此我像以前一样禁用了它们。

for i := 0 to FrmAddNewMember.ComponentCount-1 do
    if not (FrmAddNewMember.Components[i] is TButton) then //(keep buttons working)
       case FrmAddNewMember.Components[i] of
          TEdit: (FrmAddNewMember.Components[ i ] as TEdit).readonly := true; //allows copying but not editing
          TComboBox: (FrmAddNewMember.Components[ i ] as TComboBox).enabled := false;  //no read only propert?
          TDateTimePicker: (FrmAddNewMember.Components[ i ] as TDateTimePicker).enabled := false;  //ditto
          TCheckBox: (FrmAddNewMember.Components[ i ] as TCheckBox).enabled := false;
          TListBox:  (FrmAddNewMember.Components[ i ] as TListBox).enabled := false;
          TstringGrid: (FrmAddNewMember.Components[ i ] as TstringGrid).enabled := false;
       end;

有没有办法使其他控件不可编辑,但仍允许将其内容复制到剪贴板?

PS 我看过

disable-edits-on-datagridview-but-still-allow-for-highlighting-to-copy-and-paste

make-all-controls-on-a-form-read-only-at-once-without-one-linkbutton

并在别处搜索。或许不能简单的做到。

我认为,您可以为您的组件模拟弹出菜单(因为标准弹出菜单不适用于禁用的组件)。

但是如果你有表单的弹出菜单和 FormMouseDown 事件处理程序,你可以分析鼠标指针的位置(我的意思是在哪个组件下)并使用 Copy 菜单项调用弹出菜单.

列表框的快速示例:

unit Unit6;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.StdCtrls, Vcl.ExtCtrls, Clipbrd;

type
  TForm6 = class(TForm)
    Panel1: TPanel;
    ListBox1: TListBox;
    ListBox2: TListBox;
    PopupMenu1: TPopupMenu;
    miCopy: TMenuItem;
    procedure miCopyClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
    selectedText: string;
  public
    { Public declarations }
  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

procedure TForm6.FormCreate(Sender: TObject);
begin
  ListBox1.ItemIndex := 1;
  ListBox2.ItemIndex := 1;
  Panel1.OnMouseDown := FormMouseDown;
end;

procedure TForm6.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i, parentX, parentY: integer;
  p: TPoint;
  lb: TListBox;
begin
  if Button <> mbRight then
    exit;

  selectedText := '';
  for i := 0 to ComponentCount - 1 do
    if Components[i] is TListBox then
    begin
      lb := TListBox(Components[i]);
      begin
        p := lb.ParentToClient(Point(X, Y));
        if lb.ClientRect.Contains(p) then
        begin
          parentX := 0;
          parentY := 0;
          if Assigned(lb.Parent) then
          begin
            parentX := lb.Parent.ClientOrigin.X;
            parentY := lb.Parent.ClientOrigin.Y;
          end;

          if lb.ItemIndex > -1 then
          begin
            selectedText := lb.Items[lb.ItemIndex];
            PopupMenu1.Popup(lb.Left + parentX + p.X, lb.Top + parentY + p.Y);
          end;
          break;
        end;
      end;
    end;
end;

procedure TForm6.miCopyClick(Sender: TObject);
begin
  if selectedText = '' then
    exit;

  Clipboard.AsText := selectedText;
end;

end.

这里ListBox1放在TPanel组件上。请注意,您应该将表单的 OnMouseDown 处理程序分配给所有面板或其他容器。此外,如果您有嵌套容器,则需要使用递归算法来查找 parentX, parentY.

您可以使用此代码将组合框设置为只读。如果您可以获得编辑的 window 句柄,您也可以对其他编辑使用相同的方法。

procedure MakeComboboxReadOnly(const ACombobox: TCombobox);
var cbi: TComboBoxInfo;
begin
    cbi.cbSize := SizeOf(cbi);
    GetComboBoxInfo(ACombobox.Handle, cbi);
    SendMessage(cbi.hwndItem, EM_SETREADONLY, 1, 0);
end;

只是为了结束我自己的问题。我最后的做法是简单地将速度按钮放在包含我要复制的文本的控件旁边,并使用正常的

uses ClipBrd;
...
Clipboard.AsText := MyControl1.text;

复制数据。

虽然技巧不是将速度按钮放置为包含控件的面板的子项(已禁用,因此也禁用其中的所有控件),而是将它们放在窗体上然后移动它们因此它们位于相关控件旁边的面板前面。

这样一来,速度按钮看起来就像是面板的一部分,但在面板及其所有控件都被禁用时仍会运行。