(保存对话框)如何在 Vista/Win7 中更改文件过滤器时自动更改文件扩展名?

Posted

技术标签:

【中文标题】(保存对话框)如何在 Vista/Win7 中更改文件过滤器时自动更改文件扩展名?【英文标题】:(Save Dialog) How to change file extension automatically on file filter change in Vista/Win7? 【发布时间】:2011-01-09 20:37:13 【问题描述】:

在显示保存对话框时,我想挂钩用户的过滤器类型更改并自动更改文件扩展名。 (例如,像 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;

我想在 Delphi 2007 中同时支持 XP 和 vista/7。

我应该使用 TFileSaveDialog 而不是带有内部包装的 TSaveDialog 吗? (而且我不得不为使用IFileDialogControlEvents 进行 COM 编程而苦苦挣扎?)

或者我可以使用 TFileSaveDialog 实现这一点,而且它只是标准属性吗? (我的开发环境还在XP机器上,所以没试过。抱歉。)

我认为这是很常见的任务,但我找不到任何支持 Vista/7 的示例代码...

【问题讨论】:

【参考方案1】:

此功能在 Delphi 中实现,但默认禁用。

要激活它,只需在DefaultExt 属性中输入默认扩展名。

【讨论】:

【参考方案2】:

您写道,您无法破解包装器。我将此代码用于我的 XLSX/XLS/ODS 导出库,以更改 XP 和 Vista+ 上的文件扩展名。

一个缺点:类助手无法访问 Delphi 2007 中的私有字段,因此此代码仅适用于 Delphi 2009+。如果您希望与 Delphi 2007 兼容,请对 TOpenDialog 使用相同的 hack,就像我在此示例中用于 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 事件中执行任何操作。

【讨论】:

实际上,我现在检查了它 - 因为对话框单元架构,它只适用于 Delphi XE+。 感谢您的回答。我刚刚注意到。我不再使用 D2007,我会阅读您的代码。谢谢。【参考方案3】:

据我所知,TFileSaveDialog 在 XP 上会引发异常。它需要 Vista 或更高版本。

更新TFileSaveDialog的一些D2010代码改编自您的事件处理程序.... (我在 Vista 上没有 D2007;使用 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

【讨论】:

谢谢!但是我通常在运行时创建这些对话框,所以我可以通过操作系统版本检查来切换 TSaveDialog 和 TFileSaveDialog。 它适用于 D2007。我只是更改 PChar/string->PWideChar/WideString。(可能它适用于 D2009 或更高版本的自动类型转换。)。谢谢! p.s.我尝试在“*SaveDialog1*TypeChange”内部切换,使用类似“if Parent.ClassName = 'TFileSaveDialogWrapper'”。(它比切换对话框类更方便。)但我无法破解包装器,因为它在实现部分中定义...

以上是关于(保存对话框)如何在 Vista/Win7 中更改文件过滤器时自动更改文件扩展名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在应用程序关闭之前显示未保存的更改对话框? [关闭]

有没有办法从 C# WPF 应用程序中刷新 DNS 缓存? (在 XP、Vista、Win7 上)

如何禁用“您所做的更改可能无法保存”。对话框(Chrome)?

EXCEL电子表格在更改完数据后关闭,却不提示保存对话框而直接关闭,再打开发现啥也没保存上,请问怎么办

CreateFile:直接对原始磁盘进行写入操作“访问被拒绝” - Vista,Win7

在 Excel 中的“想要保存更改”对话框后无法捕获