(Save Dialog) How to change file extension automatically on file filter change in Vista/Win7?
Asked Answered
M

3

10

While showing a save dialog, I want to hook user's filter type change and change file extension automatically. (e.g. like MSPaint's "Save As" operation.)

With TSaveDialog and setting UseLatestCommonDialogs := False, I can handle this by the following code. (without latest common dialog support, of cource.)

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;

I want to support both XP, and vista/7 with Delphi 2007.

Should I use TFileSaveDialog instead of TSaveDialog with internal wrapper ? (And I have to struggle with COM programming using IFileDialogControlEvents ?)

Or can I achieve this with TFileSaveDialog and it's standard properties only ? (My development environment is still on XP machine, so I've never tried. sorry.)

I think it's very common task, but I couldn't find any sample code supporting Vista/7...

Martyrology answered 27/1, 2010 at 8:0 Comment(0)
W
6

As far as I know, TFileSaveDialog will raise an exception on XP. It needs Vista or up.

Update: some D2010 code for TFileSaveDialog adapted from your event handler....
(I don't have D2007 on Vista; use PWideChar instead of 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;

Where the FileSaveDialog is:

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
Willywilly answered 27/1, 2010 at 8:35 Comment(2)
Thanks! But I usually create these dialogs in runtime, so I could switch both TSaveDialog and TFileSaveDialog with OS version check.Martyrology
It works with D2007. I just change PChar/string->PWideChar/WideString.(May be it works on D2009 or later with auto type conversion.). Thanks! p.s. I tried to switch inside "*SaveDialog1*TypeChange", using like "if Parent.ClassName = 'TFileSaveDialogWrapper'".(It is handier than switching dialog classes.) But I couldn't hack wrapper, as it is defined in implementation section...Martyrology
Y
4

You wrote that you couldn't hack the wrapper. I use this code for my XLSX/XLS/ODS exporting library to change the file extension on both XP and Vista+.

One drawback: Class helpers cannot access private fields in Delphi 2007, so this code works only in Delphi 2009+. If you want Delphi 2007 compatibility, use the same hack for TOpenDialog like I used for TFileDialogWrapper in this example.

{ 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;

EDIT: actually, if you set the DefaultExt property, Delphi/Windows care about the file extension change for you. In that case you don't have to do anything in the OnTypeChange event.

Yettayetti answered 8/10, 2012 at 20:4 Comment(2)
Actually, I checked it now - it works only in Delphi XE+ because of Dialogs unit architecture.Yettayetti
Thank you for your answer. I've just noticed. I'm not using D2007 anymore, I'll read your code. Thanks.Martyrology
C
3

This feature is implemented in Delphi, but disabled by default.

In order to activate it, just entry the default extension in DefaultExt property.

Coincidence answered 17/8, 2016 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.