Хобрук: Ваш путь к мастерству в программировании

Как сделать все элементы управления в форме Delphi недоступными для редактирования / изменения, но все же разрешить пользователю копировать содержимое

У меня есть форма, отображающая данные из базы данных, в ней есть несколько кнопок и несколько панелей. Панели содержат множество компонентов, в частности 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-copy-and-paste

и

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

и искал в другом месте. Может быть, это невозможно сделать просто.

30.10.2018

  • Разве это небольшое изменение к моему заголовку не является немного педантичным, если вы также не пытаетесь ответить на вопрос? Я специально добавил туда слово Delphi, так как мне надоело искать в Интернете вопросы / ответы, связанные с Delphi, и находить некоторые SO, которые выглядят актуальными, но затем оказываются для другого языка. SO может использовать теги, а Google - нет. 30.10.2018
  • Почему вы не используете элементы управления базой данных, такие как TField, TDateTimeField, TMemoField, TBooleanField и т. Д. Все они имеют свойство Readonly, позволяющее контролировать, могут ли они быть изменены или нет 30.10.2018
  • Почему вы не используете элементы управления базой данных ... Потому что exe - это интерфейс к удаленной базе данных MySQL (и используется несколькими людьми). Форма выполняет большую часть обработки, и большая часть того, что она отображает, является обработанными или отформатированными данными из базы данных. Это не просто «окно» с необработанными данными базы данных. 30.10.2018
  • Странный дизайн, позволяющий отключить компонент и разрешить копирование данных из него. 30.10.2018
  • @Dima Все элементы управления отключены в этом представлении, поскольку я использую один и тот же макет как для редактирования, так и для просмотра (в представлении элементы управления отключены, чтобы предотвратить случайные изменения). Пользователю удобно иметь возможность копировать такие вещи, как адрес электронной почты или ссылочный номер, для использования в другом месте. Это можно сделать из формы «редактирования», но существует риск изменения данных, что может удивить пользователя, когда он закроет форму и получит сообщение с вопросом, хотят ли они сохранить изменения. Следовательно, я хотел также разрешить такое копирование данных из более безопасной формы просмотра, где все элементы управления отключены. 30.10.2018
  • @ Дима: совсем нет. Это имеет смысл, если вы хотите отключить редактирование, но все же сделать значения доступными и копируемыми. Вот почему ReadOnly от TEdit было бы хорошим решением, но не все элементы управления это поддерживают. 30.10.2018
  • Спасибо за объяснение. Теперь я яснее понимаю вашу проблему, но при этом остаюсь при своем мнении. 30.10.2018
  • @RudyVelthuis Я согласен с отключением компонента для ограничения редактирования (как в вашем примере), но не согласен с его реальным отключением (что делает TPanel.Enabled ;= false). Лично, если бы я написал такую ​​программу, я бы создал свои собственные компоненты (производные от стандарта) со специальным свойством, которое позволит правильно отключить их, сохраняя возможность копировать информацию из них. Конечно, этот способ потребует времени (и денег), но он будет лучше для дальнейшего сопровождения программы. 30.10.2018
  • Что я буду делать, зависит от того, что мне нужно. Если мне это нужно всего один или два раза, я бы использовал решение, которое делает то, что предлагает @dwrbudr. Если бы мне это нужно было часто, я бы написал собственные производные элементы управления. В FMX это может даже не понадобиться. Там я сначала попробую изменить стили. 30.10.2018

Ответы:


1

Я думаю, вы можете эмулировать всплывающее меню для своих компонентов (потому что стандартное всплывающее меню не работает для отключенных).

Но если у вас будет всплывающее меню для формы и 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.

30.10.2018
  • Ах, это звучит как интересная идея. Я больше думал об использовании Ctrl C и подобных сочетаний клавиш, но правильное всплывающее меню с простым копированием сделало бы свою работу. Есть подсказки по реализации? 30.10.2018
  • Я не уверен, что вы сможете реализовать работу с ярлыками, потому что отключенные элементы управления не попадают в фокус. 30.10.2018
  • Хороший ответ (хотя и не тестировал), но адаптировать этот код для каждого необходимого компонента может быть нелегко, поскольку у них разные свойства данных (например, TListBox и TDateTmePicker). 30.10.2018
  • @ Дима, надеюсь, не особо. Некоторые элементы управления могут быть переданы их родительским элементам, и мы можем использовать свойство Text. 30.10.2018
  • TListBox и TStringGrid вернут пустую строку после их преобразования. Кстати, свойство Text равно protected, поэтому OP необходимо использовать нетривиальное приведение, чтобы получить доступ к свойству Text. Надеюсь, он знает, что делает)) 30.10.2018
  • @Dima, таким образом я написал некоторые элементы управления :) 30.10.2018
  • Спасибо, да, у меня есть разумное представление о том, что я делаю. Просто не подумал сделать это таким образом. Я думал, что мне придется назначить обработчик формы OnMouseDown всем панелям, чтобы захватить его, но, к счастью, существует только один уровень вложенности, поэтому он не должен быть рекурсивным (хотя, по моему опыту, рекурсивные решения часто просты и элегантны) , Я приму этот ответ, поскольку он дает мне долгий путь к тому, что я пытаюсь сделать. Спасибо, Майами. 30.10.2018

  • 2

    Вы можете использовать этот код, чтобы сделать поле со списком доступным только для чтения. Также вы можете использовать тот же подход к другим изменениям, если вы можете получить дескриптор окна редактирования.

    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;
    
    30.10.2018
  • Этот код предотвращает только пользовательский ввод, но поле со списком по-прежнему позволяет выбирать элементы из своего списка. 30.10.2018
  • Чтобы пользователь не мог выбрать другой элемент - установите стиль combobox: = csSimple 30.10.2018
  • или csDropDownList. Знаю, но не для всех задач это приемлемо. 30.10.2018

  • 3

    Просто чтобы добавить закрытие к моему собственному вопросу. В конце концов, я сделал это, просто поместив кнопки скорости рядом с элементами управления, содержащими текст, который я хотел скопировать, и использовал обычный

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

    для копирования данных.

    Уловка, однако, заключалась не в том, чтобы разместить кнопки скорости как дочерние элементы панели, содержащей элемент управления (которая отключена, следовательно, также отключает все элементы управления внутри нее), а вместо этого просто поместите их в форму, а затем переместите их так, чтобы они были расположены перед панелью рядом с соответствующим элементом управления.

    Таким образом, кнопки скорости выглядят так, как будто они являются частью панели, но все равно будут работать, когда панель и все ее элементы управления отключены.

    23.11.2018
    Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..