I am using the WPF OpenFileDialog and SaveFileDialog in my .NET 4 WPF application. I use the Filter property to allow the user to set different file filters. In .NET 4, this uses the native file dialogs introduced with Windows Vista (if possible).
However, these dialogs show the extensions which make up the file filters. Since some of these use quite a bit of extensions, this is quite ugly.
For example, I have a filter I开发者_运维问答mage files|*.bmp;*.dib;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif;*.tif;*.tiff;*.png;*.ico|All files|*.*
, which is displayed as Image files (*.bmp;*.dib;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif;*.tif;*.tiff;*.png;*.ico)
in the dialog. Everything in the parenthesis is automatically added, i.e. according to the Filter string, it should display Image files
. But somewhere the stuff in the parenthesis is added. I tried looking at the code with Reflector to see if it is done somewhere, but quickly gave up since it is quite convoluted.
Starting Paint, for example, I can see that it is possible to use these file dialogs without the stuff in the parenthesis, i.e. it shows Image files
.
Does anyone know a workaround for this "feature"?
You're on the right track. There are effectively 2 ways to display an open file dialog. The newer method uses IFileOpenDialog (which extends IFileDialog). With this method, the filter descriptions and file specifications are defined using the COMDLG_FILTERSPEC structure. This keeps the separated into their own fields, which is more nature.
If you wanted to remove the file specs from the combo box using this method, then you'd have to either add your own custom control or manipulate the combo box control on the dialog. This would get messy though, but should be doable.
The old school method uses GetOpenFileName and the OPENFILENAME structure. The trick with this one, is that is can display a dialog with the old look or the new look. The look is determined by the settings in the OPENFILENAME structure, as described here.
The problem with the WinForms OpenFileDialog is that they either use IFileOpenDialog when say AutoUpgradeEnabled is true, and GetOpenFileName with the older look when AutoUpgradeEnabled is false.
The WPF version doesn't give you a choice, but still uses the same logic as the WinForms one, but does it automatically as needed. This is true for WPF in .NET 4, in previous versions it would just use GetOpenFileName with the old look.
Paint is most likely using GetOpenFileName with the new look. Here is a C# example:
private delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private const int OFN_ALLOWMULTISELECT = 0x00000200;
private const int OFN_CREATEPROMPT = 0x00002000;
private const int OFN_DONTADDTORECENT = 0x02000000;
private const int OFN_ENABLEHOOK = 0x00000020;
private const int OFN_ENABLEINCLUDENOTIFY = 0x00400000;
private const int OFN_ENABLESIZING = 0x00800000;
private const int OFN_ENABLETEMPLATE = 0x00000040;
private const int OFN_ENABLETEMPLATEHANDLE = 0x00000080;
private const int OFN_EXPLORER = 0x00080000;
private const int OFN_EXTENSIONDIFFERENT = 0x00000400;
private const int OFN_FILEMUSTEXIST = 0x00001000;
private const int OFN_FORCESHOWHIDDEN = 0x10000000;
private const int OFN_HIDEREADONLY = 0x00000004;
private const int OFN_LONGNAMES = 0x00200000;
private const int OFN_NOCHANGEDIR = 0x00000008;
private const int OFN_NODEREFERENCELINKS = 0x00100000;
private const int OFN_NOLONGNAMES = 0x00040000;
private const int OFN_NONETWORKBUTTON = 0x00020000;
private const int OFN_NOREADONLYRETURN = 0x00008000;
private const int OFN_NOTESTFILECREATE = 0x00010000;
private const int OFN_NOVALIDATE = 0x00000100;
private const int OFN_OVERWRITEPROMPT = 0x00000002;
private const int OFN_PATHMUSTEXIST = 0x00000800;
private const int OFN_READONLY = 0x00000001;
private const int OFN_SHAREAWARE = 0x00004000;
private const int OFN_SHOWHELP = 0x00000010;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class OPENFILENAME_I {
public int lStructSize;
public IntPtr hwndOwner;
public IntPtr hInstance;
public string lpstrFilter;
public IntPtr lpstrCustomFilter;
public int nMaxCustFilter;
public int nFilterIndex;
public IntPtr lpstrFile;
public int nMaxFile = 260;
public IntPtr lpstrFileTitle;
public int nMaxFileTitle = 260;
public string lpstrInitialDir;
public string lpstrTitle;
public int Flags;
public short nFileOffset;
public short nFileExtension;
public string lpstrDefExt;
public IntPtr lCustData;
public WndProc lpfnHook;
public string lpTemplateName;
public IntPtr pvReserved;
public int dwReserved;
public int FlagsEx;
}
[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetOpenFileName([In, Out] OPENFILENAME_I ofn);
private void ShowOpenFileDialog() {
OPENFILENAME_I ofn = new OPENFILENAME_I();
ofn.lStructSize = Marshal.SizeOf(typeof(OPENFILENAME_I));
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files\0*.*\0\0";
ofn.nFilterIndex = 0;
//ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_NODEREFERENCELINKS | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
ofn.Flags = OFN_ENABLESIZING | OFN_NODEREFERENCELINKS | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
GetOpenFileName(ofn);
}
You can try adding the OFN_EXPLORER
and/or OFN_ENABLEHOOK
flags back in, and it will revert to the old look. But as-is, the code above will display an open file dialog with All Files not showing it's file spec.
After a bunch of testing, I got the following result: The FileDialog in WPF calls the native IFileDialog::SetFileTypes method with the strings as defined by the filter. Depending on the Windows option to hide the extension on known file types (in the Windows Explorer folder settings), the extensions are automatically added or not.
Now the only question remains is how Paint is able to not show the extensions for "Image files" in its open file dialog.
You might want to head here...
Hide Extensions
I didn’t like the GUI of neither GetOpenFileName nor WinForm’s dialog with AutoUpgradeEnabled=false.
To fix the WPF’s dialog, I’ve wrote a small class which implements IDisposable
, in constructor I check if HideFileExt
value is set to 0
under HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
registry key, if it’s 0
I write 1
there, keep the key open in a readonly field, and restore 0
when the class is disposed.
Usage example:
bool result;
using( var hfe = new HideExplorerExtensions() )
result = ofd.ShowDialog( ownerWindow ) ?? false;
The cleaner way is patch RegGetValueW
or whatever else API comdlg32.dll!CFileOpenSave::Show(struct HWND__ *)
method uses to read that value, with e.g. MinHook so only the dialog hosted in the current process sees 1
in that registry value. It was much more complex to do however.
精彩评论