(Диалог сохранения) Как автоматически изменить расширение файла при смене фильтра файлов в Vista / Win7?

При отображении диалогового окна сохранения я хочу автоматически отслеживать изменение типа фильтра пользователя и изменять расширение файла. (например, как операция MSPaint «Сохранить как».)

С TSaveDialog и установкой UseLatestCommonDialogs: = False я могу справиться с этим с помощью следующего кода. (конечно, без поддержки последних общих диалогов.)

procedure TForm1.SaveDialog1TypeChange(Sender: TObject);
var
  FName, Ext: string;
begin
  with TSaveDialog(Sender) do
  begin
    if DirectoryExists(FileName) then // FileName is Empty
      exit;
    case FilterIndex of
    1: Ext := '.png';
    2: Ext := '.bmp';
    3: Ext := '.jpg';
    end;
    FName := ChangeFileExt(ExtractFileName(FileName), Ext);
    SendMessage(Windows.GetParent(Handle), CDM_SETCONTROLTEXT, 1152, LongInt(PChar(FName)));
  end;
end;

Я хочу поддерживать как XP, так и vista / 7 с Delphi 2007.

Должен ли я использовать TFileSaveDialog вместо TSaveDialog с внутренней оболочкой? (И мне приходится бороться с программированием COM, используя IFileDialogControlEvents?)

Или я могу добиться этого с помощью TFileSaveDialog и его стандартных свойств? (Моя среда разработки все еще находится на машине XP, поэтому я никогда не пробовал. Извините.)

Я думаю, что это очень распространенная задача, но я не смог найти ни одного примера кода, поддерживающего Vista / 7 ...


person benok    schedule 27.01.2010    source источник


Ответы (3)


Насколько мне известно, TFileSaveDialog вызовет исключение в XP. Требуется Vista или выше.

Обновление: некоторый код D2010 для TFileSaveDialog, адаптированный из вашего обработчика событий ....
(у меня нет D2007 в Vista; используйте PWideChar вместо PChar)

procedure TForm1.FileSaveDialog1TypeChange(Sender: TObject);
var
  FName, Ext: string;
  pName: PChar;
begin
  with TFileSaveDialog(Sender) do
  begin
    if DirectoryExists(FileName) then // FileName is Empty
      exit;
    case FileTypeIndex of
    1: Ext := '.png';
    2: Ext := '.bmp';
    3: Ext := '.jpg';
    end;
    Dialog.GetFileName(pName);
    FName := ChangeFileExt(ExtractFileName(pName), Ext);
    Dialog.SetFileName(PChar(FName));
  end;
end;

Где находится FileSaveDialog:

object FileSaveDialog1: TFileSaveDialog
  FavoriteLinks = <>
  FileTypes = <
    item
      DisplayName = 'png files'
      FileMask = '*.png'
    end
    item
      DisplayName = 'bmp files'
      FileMask = '*.bmp'
    end
    item
      DisplayName = 'jpg files'
      FileMask = '*.jpg'
    end>
  Options = []
  OnTypeChange = FileSaveDialog1TypeChange
end
person Francesca    schedule 27.01.2010
comment
Спасибо! Но я обычно создаю эти диалоги во время выполнения, поэтому я мог переключить TSaveDialog и TFileSaveDialog с проверкой версии ОС. - person benok; 27.01.2010
comment
Работает с D2007. Я просто меняю PChar / string- ›PWideChar / WideString. (Может быть, он работает на D2009 или новее с автоматическим преобразованием типа). Спасибо! p.s. Я попытался переключиться внутри * SaveDialog1 * TypeChange, используя как if Parent.ClassName = 'TFileSaveDialogWrapper' (это удобнее, чем переключение диалоговых классов.) Но я не смог взломать оболочку, как это определено в разделе реализации ... - person benok; 28.01.2010

Вы писали, что не можете взломать обертку. Я использую этот код для своей библиотеки экспорта XLSX / XLS / ODS, чтобы изменить расширение файла как в XP, так и в Vista +.

Один недостаток: помощники классов не могут получить доступ к закрытым полям в Delphi 2007, поэтому этот код работает только в Delphi 2009+. Если вам нужна совместимость с Delphi 2007, используйте для TOpenDialog тот же прием, что и для TFileDialogWrapper в этом примере.

{ interface }

  //some hacking needed to change the file extension at type change,
  //empty class is just fine...
  TFileDialogWrapper = class(TObject)
  private
  {$HINTS OFF}
    procedure AssignFileTypes;
    procedure AssignOptions;
    function GetFileName: TFileName;
    function GetHandle: HWND;
    procedure HandleShareViolation(Sender: TObject;
      var Response: TFileDialogShareViolationResponse);
    procedure OnFileOkEvent(Sender: TObject; var CanClose: Boolean);
    procedure OnFolderChangeEvent(Sender: TObject);
    procedure OnSelectionChangeEvent(Sender: TObject);
    procedure OnTypeChangeEvent(Sender: TObject);
  protected
    FFileDialog: TCustomFileDialog;
  {$HINTS ON}
  end;
  TOpenDialogHelper = class helper for TOpenDialog
  public
    function GetInternalWrapper: TFileDialogWrapper;
  end;

{ implementation }

{ TOpenDialogHelper }

function TOpenDialogHelper.GetInternalWrapper: TFileDialogWrapper;
begin
  Result := TFileDialogWrapper(Self.FInternalWrapper);
end;

{ TFileDialogWrapper }

procedure TFileDialogWrapper.AssignFileTypes;
begin
end;

procedure TFileDialogWrapper.AssignOptions;
begin
end;

function TFileDialogWrapper.GetFileName: TFileName;
begin
end;

function TFileDialogWrapper.GetHandle: HWND;
begin
end;

procedure TFileDialogWrapper.HandleShareViolation(Sender: TObject;
  var Response: TFileDialogShareViolationResponse);
begin
end;

procedure TFileDialogWrapper.OnFileOkEvent(Sender: TObject;
  var CanClose: Boolean);
begin
end;

procedure TFileDialogWrapper.OnFolderChangeEvent(Sender: TObject);
begin
end;

procedure TFileDialogWrapper.OnSelectionChangeEvent(Sender: TObject);
begin
end;

procedure TFileDialogWrapper.OnTypeChangeEvent(Sender: TObject);
begin
end;

//use this for OnTypeChane event of a "normal" TOpenDialog / TSaveDialog

procedure TForm1.DialogTypeChange(Sender: TObject);
var
  xFN: WideString;
  xExporter: TOCustomExporter;
  xFileName: PWideChar;
  xFD: TFileDialogWrapper;
  xFilterIndex: UINT;
begin
  if Sender is TOpenDialog then
  with TOpenDialog(Sender) do begin
    xFD := GetInternalWrapper;
    if (xFD <> nil) and (xFD.FFileDialog <> nil)
    then begin
      //Vista file dialog

      xFD.FFileDialog.Dialog.GetFileName(xFileName);
      if xFileName = '' then
        exit;
      xFN := xFileName;
      xFD.FFileDialog.Dialog.GetFileTypeIndex(xFilterIndex);

      // DO WHATEVER YOU WANT WITH THE FILENAME HERE //

      xFD.FFileDialog.Dialog.SetFileName(PWideChar(xFN));
    end else begin
      //Old dialog
      xFN := ExtractFileName(FileName);
      if xFN = '' then
        exit;

      // DO WHATEVER YOU WANT WITH THE FILENAME HERE //

      {$HINTS OFF}
      SendMessage(Windows.GetParent(Handle), CDM_SETCONTROLTEXT, 1152, LongInt(PWideChar(xFN)));
      {$HINTS ON}
    end;
  end;
end;

РЕДАКТИРОВАТЬ: на самом деле, если вы установите свойство DefaultExt, Delphi / Windows позаботится об изменении расширения файла за вас. В этом случае вам не нужно ничего делать в событии OnTypeChange.

person oxo    schedule 08.10.2012
comment
Собственно, сейчас проверил - работает только в Delphi XE + из-за архитектуры модуля Dialogs. - person oxo; 01.11.2012
comment
Спасибо за ваш ответ. Я только что заметил. D2007 больше не использую, почитаю ваш код. Спасибо. - person benok; 13.12.2012

Эта функция реализована в Delphi, но по умолчанию отключена.

Чтобы активировать его, просто введите расширение по умолчанию в свойстве DefaultExt.

person Jacek Krawczyk    schedule 17.08.2016