Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a form displaying data from a database, It has a few buttons and several panels. The panels contain a variety of components, specifically TEdits, TComboBox, TDateTimePicker, TCheckBox, TListBox and TstringGrid.

When the form is opened such that the user can view, but not edit, the data I currently disable all the components except for the buttons using

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

This works fine except that I would now like the user to be able to copy to the clipboard the text in the TEdits, the date in the TDateTimePicker, the selected item in the TComboBox etc etc. but still not change it.

I altered the code to the following which sets the TEdits to Read Only. This does what I want for TEdits but the other type of controls do not have a read only property, so I disabled them as before.

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;

Is there a way to make the other controls non editable but still allow their contents to be copied to the clipboard?

PS I have looked at

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

and

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

and searched elsewhere. Maybe it cannot be done simply.

like image 211
user2834566 Avatar asked Oct 16 '22 11:10

user2834566


1 Answers

I think, you can emulate a popup menu for your components (because standard popup menu will not work for disabled ones).

But if you will have popup menu for the form and FormMouseDown event handler, you can analyze where mouse pointer is (under which component, I mean) and call popup with Copy menu item.

Quick example for listboxes:

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.

Here ListBox1 is placed on the TPanel component. Please note you should assign form's OnMouseDown handler to all your panels or other containers. Also, if you have nested containers, you need use recursive algorithm to find parentX, parentY.

like image 154
Miamy Avatar answered Oct 31 '22 19:10

Miamy