Delphi 2007 - Разрешение выбора файла только для чтения в TSaveDialog

Мне нужно, чтобы пользователь мог выбрать существующий файл только для чтения из диалогового окна сохранения. Я думал, что вы могли бы сделать это с разумным выбором TSaveDialog опций, но я не могу это сделать. Если я выбираю файл R/O, как только я нажимаю кнопку Save, я получаю сообщение:

    Read-only.txt
    This file is set to read-only.
    Try again with a different file name.

Я предполагал, что бит опции ofNoReadOnlyReturn будет управлять этим, но, похоже, это не имеет никакого эффекта.

Что мне не хватает?

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

-

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    SaveDialog1: TSaveDialog;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
if SaveDialog1.Execute then
    begin
    Windows.Beep (1000, 300) ;
    end ;
end ;

end.

-

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 69
  ClientWidth = 195
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 56
    Top = 18
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object SaveDialog1: TSaveDialog
    FileName = 'Read-only.txt'
    InitialDir = 'C:\Users\Ross\Documents\RAD Studio\Projects'
    Options = [ofHideReadOnly, ofNoReadOnlyReturn, ofEnableSizing]
    Left = 16
    Top = 16
  end
end

person rossmcm    schedule 17.01.2018    source источник
comment
Если файл доступен только для чтения, вы не можете выбрать его в диалоговом окне сохранения, потому что вы не можете перезаписать файл (он доступен только для чтения). Если вы хотите, чтобы пользователь мог перезаписать файл, удалите атрибут только для чтения перед отображением диалогового окна.. (Едва ли имеет смысл делать файл доступным только для чтения, когда вы знаете, что хотите, чтобы пользователь мог чтобы перезаписать его. Я сделаю этот файл доступным только для чтения, чтобы пользователь не мог его перезаписать. Подождите, как мне разрешить пользователю перезаписывать файл?)   -  person Ken White    schedule 18.01.2018
comment
Намерение состояло в том, чтобы сбросить флаг R/O, разрешить перезапись, а затем снова установить флаг R/O. Проблема в том, что файлы помечены как доступные только для чтения, потому что мы хотим, чтобы обычные пользователи не перезаписывали их, но иногда эти файлы должны обновляться руководителями (используя программу, к которой имеют доступ только они), и я хотел сделать этот процесс как максимально безболезненно.   -  person rossmcm    schedule 18.01.2018
comment
Небольшая игра показывает, что TOpenDialog позволяет выбрать файл только для чтения, а TSaveDialog — нет (независимо от флагов, которые вы используете в параметрах). По-видимому, что-то в оболочке VCL мешает возможности выбирать файлы только для чтения с помощью TSaveDialog. Не знаю, поможет ли это, но... Также не уверен, что вы в курсе, поэтому упомяну об этом: вы можете поэкспериментировать с диалогами во время разработки, установив нужные параметры в OI, а затем дважды- щелкните компонент, который откроет диалоговое окно с текущими параметрами. Это делает тестирование довольно быстрым.   -  person Ken White    schedule 18.01.2018
comment
Спасибо, Кен. Нет, я не знал о предварительном просмотре во время разработки с помощью двойного щелчка — очень полезно, особенно если для сборки и запуска проекта требуется время. В конце концов, я использовал TOpenDialog с кодом в событии OnShow, чтобы согнуть заголовок кнопки Open, чтобы прочитать Save   -  person rossmcm    schedule 18.01.2018
comment
Да, предварительный просмотр во время разработки позволяет даже тестировать маски файлов, начальные каталоги и другие вещи, а не только настройки параметров. Не все знают, что я подумал, что упомяну об этом. Это чрезвычайно полезная функция, и она работает со всеми компонентами диалога (TSaveDialog, TFileSaveDialog и т. д.).   -  person Ken White    schedule 18.01.2018


Ответы (1)


Проблема не в самом TSaveDialog, а в базовых API-интерфейсах Win32 GetSaveFileName()/IFileSaveDialog, которые TSaveDialog использует внутри. Они просто не позволяют файлу только для чтения быть результатом диалога «сохранить».

При этом IFileSaveDialog предоставляет возможный (хотя и некрасивый) обходной путь. Если вы активируете флаг FOS_OVERWRITEPROMPT диалогового окна, то при выборе существующего файла пользователю будет предложено перезаписать файл перед закрытием диалогового окна. Интерфейс IFileDialogEvents имеет событие OnOverwrite, которое запускается перед появлением этого приглашения (и может возвращать FDESVR_ACCEPT, чтобы полностью пропустить приглашение). Таким образом, в этом случае вы можете удалить атрибут FILE_ATTRIBUTE_READONLY файла до закрытия диалогового окна. Однако диалоговое окно по-прежнему будет отображать то же сообщение об ошибке «Этот файл установлен только для чтения» и отказываться от закрытия (предположительно, потому, что оно проверило атрибут перед запуском события OnOverwrite), но если вы затем снова выберете тот же файл, атрибут будет очищен, и диалоговое окно примет файл (это уродливая часть - вам придется научить своих менеджеров игнорировать эту ошибку и повторить попытку).

GetSaveFileName() имеет флаг ofOverwritePrompt, но нет события для запроса на перезапись.

При этом TSaveDialog не предоставляет доступ к событию IFileSaveDialog.OnOverwrite, когда внутри используется IFileSaveDialog, но вместо этого вы можете использовать TFileSaveDialog, что и делает.

person Remy Lebeau    schedule 18.01.2018