允许选择从SaveFileDialog只读文件吗?文件、SaveFileDialog

2023-09-03 08:11:25 作者:绿萝@

我使用 Microsoft.Win32.SaveFileDialog 在一个应用程序,其中所有的文件被保存为只读但是用户需要能够选择现有文件。被替换现有的文件重命名,例如:blah.png成为blah.png-0.bak,blah.png-1.bak等

因此​​,对于OverwritePrompt的语言是不恰当的 - 我们的不可以使他们能够覆盖文件 - 所以我设置 dlog.OverwritePrompt = FALSE;

初​​始文件名的对话框是基于文件的值产生,以便对他们来说,这很容易 - 我们重命名候选文件提前,如果用户取消或选择一个不同的名称,然后再返回其重命名为

在我提供的功能,测试人员迅速抱怨,因为他们要反复地保存文件名的从约定的工作流程的(那些愚蠢的,好玩的家伙!)。

不同

我不能想出一个办法与标准对话框做到这一点,在某种程度上,将安全在XP(现在)运行和Windows 7。

我希望能钩到 FileOK事件但那个叫在我得到一个警告对话框:

  | ------------------------------------- ---- |
| blah.png |
|这个文件设置为只读。 |
|再次尝试使用不同的文件名。 |
| ----------------------------------------- |
 

解决方案

试图选择从Perforce的工作区,这是只读的,直到签出的文件时,有同样的问题。通过调用本地SaveFileDialog解决它。

VB中CommonDialog1.ShowSave只读文件保存出错

您可以删除FOS_NOREADONLYRETURN在本机版本,但它是相同的.NET。在Windows API只是自动添加标记。

首先,我试图用打开文件对话框,并与本地SetOkButtonLabel方法改变OK按钮的文字。但是,这给你一个问题,本地化,你也失去了覆盖检查。

我结束了使用OnSelectionChange事件(未在.NET版本曝光),以暂时删除所选文件的只读标志。这招工作得很好,除了在需要提升权限(如C的根文件夹:)文件夹。但我可以忍受的。

 使用System.IO;
使用了System.Runtime.InteropServices;

命名空间System.Windows.Forms的
{
    ///<总结>
    ///同.NETs SaveFileDialog,但它也可以让你选择只读文件。
    ///
    ///基于天然通用项对话框,这也可用于内部由SaveFileDialog。用途
    ///的OnSelectionChange事件暂时删除所选文件的只读标志
    ///欺骗对话框。不幸的是,这一事件不会exponsed在.NET版本。
    ///
    ///由于公共项目对话框不可用,直到Windows Vista中,调用静态则isSupported()
    ///方法首先。如果返回false,使用常规SaveFileDialog代替。在XP中,经常
    ///对话框的工作也为只读文件。
    ///
    ///注意,在需要提升权限(例如,在C的根文件夹:)这招是行不通的。
    ///< /总结>
    公共类SaveFileDialogRO:IDisposable的
    {
        私人const int的S_OK = 0;

        私人FileDialogNative.IFileSaveDialog对话框;
        私人字符串defaultExt =的String.Empty;
        私人FileDialogNative.FOS选项;
        私人字符串过滤器=的String.Empty;
        私人字符串initialDirectory =的String.Empty;
        私人字符串标题=的String.Empty;

        ///<总结>
        ///返回true,如果支持通用项对话框,其中SaveFileDialogRO内部使用。
        ///如果不是,只需使用常规的SaveFileDialog。
        ///< /总结>
        公共静态布尔则isSupported()
        {
            返回Environment.OSVersion.Version.Major> 5;
        }

        公共SaveFileDialogRO()
        {
             对话框=(FileDialogNative.IFileSaveDialog)新FileDialogNative.FileSaveDialogRCW();
             dialog.GetOptions(出选项);
        }

        〜SaveFileDialogRO()
        {
            处置(假);
        }

        公共无效的Dispose()
        {
            处置(真);
            GC.Sup pressFinalize(本);
        }

        保护无效的Dispose(BOOL处置)
        {
            对Marshal.ReleaseComObject(对话);
        }

        ///<总结>
        ///获取或设置一个值,指示对话框是否显示警告,如果用户指定的文件名
        ///不存在。
        ///< /总结>
        公共BOOL CheckFileExists
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_FILEMUSTEXIST)!= 0;
            }
            组
            {
                如果(值)
                    选项​​| = FileDialogNative.FOS.FOS_FILEMUSTEXIST;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_FILEMUSTEXIST;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置一个值,指示对话框是否显示警告,如果用户指定
        ///不存在路径。
        ///< /总结>
        公共BOOL CheckPathExists
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_PATHMUSTEXIST)!= 0;
            }
            组
            {
                如果(值)
                    选项​​| = FileDialogNative.FOS.FOS_PATHMUSTEXIST;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_PATHMUSTEXIST;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置一个值,指示是否对话框提示用户权限创建一个
        ///如果用户指定了一个不存在的文件档案。
        ///< /总结>
        公共BOOL CreatePrompt
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_CREATEPROMPT)!= 0;
            }
            组
            {
                如果(值)
                    选项​​| = FileDialogNative.FOS.FOS_CREATEPROMPT;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_CREATEPROMPT;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置默认文件扩展名。
        ///< /总结>
        公共字符串DefaultExt
        {
            得到
            {
                返回defaultExt;
            }
            组
            {
                dialog.SetDefaultExtension(值);
                defaultExt =价值;
            }
        }

        ///<总结>
        ///获取或设置默认文件扩展名。
        ///< /总结>
        公共BOOL DereferenceLinks
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_NODEREFERENCELINKS)== 0;
            }
            组
            {
                如果(!值)
                    选项​​| = FileDialogNative.FOS.FOS_NODEREFERENCELINKS;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_NODEREFERENCELINKS;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置包含在文件对话框中选择文件名的字符串。
        ///< /总结>
        公共字符串文件名
        {
            得到
            {
                //获取所选择的文件名称(失败,如果已被取消的对话框或尚未示出)
                字符串文件名;
                尝试
                {
                    FileDialogNative.IShellItem项目;
                    dialog.GetResult(出项);

                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH,出的文件);
                }
                赶上(例外)
                {
                    //返回一个通过SetFileName设置名称(如果没有设置失败)
                    尝试
                    {
                        dialog.GetFileName(输出文件名);
                    }
                    赶上(例外)
                    {
                        文件名=的String.Empty;
                    }
                }
                返回文件名;
            }
            组
            {
                dialog.SetFileName(值);
            }
        }

        ///<总结>
        ///获取对话框中所有选定文件的文件名。
        ///对于SaveFileDialog,这永远是至多一个文件。
        ///< /总结>
        公共字符串[]文件名
        {
            得到
            {
                //获取所选择的文件名称(失败,如果已被取消的对话框或尚未示出)
                尝试
                {
                    字符串文件名;
                    FileDialogNative.IShellItem项目;
                    dialog.GetResult(出项);

                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH,出的文件);
                    返回新的String [] {文件名};
                }
                赶上(例外)
                {
                    返回新的字符串[0];
                }
            }
        }

        ///<总结>
        ///获取或设置当前文件名过滤字符串,这就决定了显示的选择
        ///中的另存为文件类型或文件类型对话框,在对话框。
        ///< /总结>
        ///<说明>
        ///对于每个过滤选项,过滤字符串包含滤波器的说明,接着
        ///由竖线(|)和过滤器模式。对于不同的过滤选项字符串
        ///相隔竖线< / BR>
        ///下面是一个过滤字符串的例如:< / BR>
        ///文本文件(* .txt)| * .TXT |所有文件(*。*)| *。*
        ///< /说明>
        公共字符串过滤器
        {
            得到
            {
                返回过滤器;
            }
            组
            {
                //拆分为竖线
                字符串[]类型= value.Split(新的char [] {'|'},StringSplitOptions.RemoveEmptyEntries);
                如果(types.Length == 0 || types.Length%2!= 0)
                    抛出新的ArgumentException(无效过滤器:+值);

                //转换为COMDLG_FILTERSPEC阵列
                INT numTypes = types.Length / 2;
                FileDialogNative.COMDLG_FILTERSPEC []规格=新FileDialogNative.COMDLG_FILTERSPEC [numTypes]
                的for(int i = 0; I< numTypes ++ I)
                {
                    规格[i] =新FileDialogNative.COMDLG_FILTERSPEC
                    {
                        pszName =类型[I * 2 + 0]
                        pszSpec =类型[I * 2 + 1]
                    };
                }

                //设置新的过滤器
                dialog.SetFileTypes((UINT)numTypes,规格);
                过滤器=价值;
            }
        }

        ///<总结>
        ///获取或设置当前在文件对话框中选择的过滤器的索引。
        ///注意:第一个过滤器条目的索引值是1!
        ///< /总结>
        公众诠释FilterIndex
        {
            得到
            {
                UINT指数;
                dialog.GetFileTypeIndex(出指数);
                返回(INT)指数;
            }
            组
            {
                dialog.SetFileTypeIndex((UINT)值);
            }
        }

        ///<总结>
        ///获取或设置文件对话框显示的初始目录。
        ///< /总结>
        公共字符串InitialDirectory
        {
            得到
            {
                返回initialDirectory;
            }
            组
            {
                FileDialogNative.IShellItem项目;
                IntPtr的IDL;
                UINT的ATT = 0;
                如果(SHILCreateFromPath(值,出IDL,裁判的ATT)== S_OK)
                {
                    如果(SHCreateShellItem(IntPtr.Zero,IntPtr.Zero,IDL,出项)== S_OK)
                    {
                        dialog.SetFolder(项目);
                        initialDirectory =价值;
                    }

                    CoTaskMemFree(IDL);
                }
            }
        }

        ///<总结>
        ///获取或设置一个值,指示另存为对话框中是否显示,如果用户发出警告
        ///指定已存在的文件名。
        ///< /总结>
        公共BOOL OverwritePrompt
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_OVERWRITEPROMPT)!= 0;
            }
            组
            {
                如果(值)
                    选项​​| = FileDialogNative.FOS.FOS_OVERWRITEPROMPT;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_OVERWRITEPROMPT;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置一个值,指示对话框是否恢复关闭前的当前目录。
        ///< /总结>
        公共BOOL RestoreDirectory
        {
            得到
            {
                返回(选项和放大器; FileDialogNative.FOS.FOS_NOCHANGEDIR)!= 0;
            }
            组
            {
                如果(值)
                    选项​​| = FileDialogNative.FOS.FOS_NOCHANGEDIR;
                其他
                    选择与放大器; =〜FileDialogNative.FOS.FOS_NOCHANGEDIR;
                dialog.SetOptions(选件);
            }
        }

        ///<总结>
        ///获取或设置一个值,指示是否显示在文件对话框中的帮助按钮。
        ///< /总结>
        公共BOOL使用showHelp
        {
            得到
            {
                返回true;
            }
            组
            {
                //似乎总是正确的情况下,公共项目对话框
            }
        }

        ///<总结>
        ///获取或设置对话框,是否支持显示和保存有多个扩展名的文件。
        ///< /总结>
        公共BOOL SupportMultiDottedExtensions
        {
            得到
            {
                返回true;
            }
            组
            {
                //似乎总是正确的情况下,公共项目对话框
            }
        }

        ///<总结>
        ///获取或设置文件对话框标题。
        ///< /总结>
        公共字符串标题
        {
            得到
            {
                返回称号;
            }
            组
            {
                dialog.SetTitle(值);
                标题=价值;
            }
        }

        ///<总结>
        ///获取或设置一个值,指示对话框是否只接受有效的Win32文件名。
        ///< /总结>
        公共BOOL ValidateNames
        {
            得到
            {
                返回true;
            }
            组
            {
                //似乎总是正确的情况下,公共项目对话框
            }
        }

        ///<总结>
        ///运行具有默认的所有者对话框。
        ///< /总结>
        公众的DialogResult ShowDialog的()
        {
            返回的ShowDialog(空);
        }

        ///<总结>
        ///使用指定的所有者对话框。
        ///< /总结>
        公众的DialogResult的ShowDialog(IWin32Window所有者)
        {
            //设置事件处理程序
            SaveFileDialogROEvents事件=新SaveFileDialogROEvents();
            UINT饼干;
            dialog.Advise(事件,退出cookie);

            //显示对话框
            INT HR = dialog.Show(所有者= NULL owner.Handle:IntPtr.Zero!?);

            //删除事件处理程序
            dialog.Unadvise(饼干);
            events.RestoreAttribute(); //需要的情况下取消

            //转换返回值的DialogResult
            返回小时= = S_OK? DialogResult.OK:DialogResult.Cancel;
        }

        ///<总结>
        ///事件处理程序,临时删除所选文件的只读标志。
        ///< /总结>
        类SaveFileDialogROEvents:FileDialogNative.IFileDialogEvents
        {
            FileInfo的lastReadOnlyFile = NULL;

            公众诠释OnFileOk(FileDialogNative.IFileDialog PFD)
            {
                //此方法是不叫的情况下取消
                RestoreAttribute();
                返回S_OK;
            }

            公众诠释OnFolderChanging(FileDialogNative.IFileDialog PFD,FileDialogNative.IShellItem psiFolder)
            {
                返回S_OK;
            }

            公共无效OnFolderChange(FileDialogNative.IFileDialog PFD)
            {
                RestoreAttribute();
            }

            公共无效OnSelectionChange(FileDialogNative.IFileDialog PFD)
            {
                //获取选定的文件
                字符串名称;
                尝试
                {
                    FileDialogNative.IShellItem项目;
                    pfd.GetCurrentSelection(出项);
                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH,出姓名);
                }
                赶上(例外)
                {
                    //选择暂时还没有文件
                    返回;
                }

                //有变化?
                如果(lastReadOnlyFile = NULL和放大器;!&安培; lastReadOnlyFile.FullName ==名)
                    返回;

                //恢复previous文件的只读属性,如果有必要
                RestoreAttribute();

                //删除选定的文件的只读属性,如果有必要
                FileInfo的F =新的FileInfo(名称);
                如果(f.Exists&安培;及(f.Attributes&安培;!FileAttributes.ReadOnly)= 0)
                {
                    尝试
                    {
                        f.Attributes和放大器; =〜FileAttributes.ReadOnly;
                        lastReadOnlyFile = F;
                    }
                    赶上(例外)
                    {
                        //没有足够的权利,我们无能为力
                        返回;
                    }
                }
            }

            公共无效OnShareViolation(FileDialogNative.IFileDialog PFD,FileDialogNative.IShellItem磅,出FileDialogNative.FDE_SHAREVIOLATION_RESPONSE presponse)
            {
                presponse = FileDialogNative.FDE_SHAREVIOLATION_RESPONSE.FDESVR_DEFAULT;
            }

            公共无效OnTypeChange(FileDialogNative.IFileDialog PFD)
            {
            }

            公共无效OnOverwrite(FileDialogNative.IFileDialog PFD,FileDialogNative.IShellItem磅,出FileDialogNative.FDE_OVERWRITE_RESPONSE presponse)
            {
                //删除只读属性在这里,遗憾的是不工作
                presponse = FileDialogNative.FDE_OVERWRITE_RESPONSE.FDEOR_DEFAULT;
            }

            ///<总结>
            ///恢复pviously所选文件的$ P $的只读属性。
            ///< /总结>
            公共无效RestoreAttribute()
            {
                如果(lastReadOnlyFile!= NULL)
                {
                    lastReadOnlyFile.Attributes | = FileAttributes.ReadOnly;
                    lastReadOnlyFile = NULL;
                }
            }
        }

        [的DllImport(Shell32.dll中)
        私人静态外部INT SHILCreateFromPath([的MarshalAs(UnmanagedType.LPWStr)字符串pszPath,出来的IntPtr ppIdl,裁判UINT rgflnOut);

        [的DllImport(Shell32.dll中)
        私人静态外部INT SHCreateShellItem(IntPtr的pidlParent,IntPtr的psfParent,IntPtr的PIDL,出FileDialogNative.IShellItem PPSI);

        [的DllImport(OLE32.DLL)
        公共静态外部无效CoTaskMemFree(IntPtr的PTR);
    }
}
 

您还需要这些绑定:

http://www.dotnetframework.org/default.aspx/DotNET/DotNET/8@0/untmp/whidbey/REDBITS/ndp/fx/src/WinForms/Managed/System/WinForms/FileDialog_Vista_Interop@cs/1/FileDialog_Vista_Interop@cs

I'm using Microsoft.Win32.SaveFileDialog in an app where all files are saved as readonly but the user needs to be able to choose existing files. The existing files being replaced are renamed eg: blah.png becomes blah.png-0.bak, blah.png-1.bak and so on.

Thus, the language for the OverwritePrompt is inappropriate - we are not allowing them to overwrite files - so I'm setting dlog.OverwritePrompt = false;

The initial filenames for the dialog are generated based on document values so for them, it's easy - we rename the candidate file in advance and if the user cancels or chooses a different name, rename it back again.

When I delivered the feature, testers swiftly complained because they wanted to repeatedly save files with names different from the agreed workflow (those goofy, playful guys!).

I can't figure out a way to do this with the standard dialogs, in a way that will run safely on both XP (for now) and Windows 7.

I was hoping to hook into the FileOK event but that is called after I get a warning dialog:

|-----------------------------------------|
| blah.png                                |
| This file is set to read-only.          |
| Try again with a different file name.   |
|-----------------------------------------|

解决方案

Had the same problem when trying to select files from a Perforce workspace, which are read-only until checked out. Solved it by calling the native SaveFileDialog.

You can remove the FOS_NOREADONLYRETURN in the native version, but it is the same as in .NET. The Windows API simply adds the flag automatically.

First, I tried to use the OpenFileDialog and change the text of the OK button with the native SetOkButtonLabel method. But this gives you a problem with localization and you also lose the overwrite check.

I ended up using the OnSelectionChange event (which is not exposed in the .NET version) in order to temporarily remove the read-only flag of selected files. This trick works quite well, except for folders where elevated rights are needed (like the root folder of C:). But I can live with that.

using System.IO;
using System.Runtime.InteropServices;

namespace System.Windows.Forms
{
    /// <summary>
    /// Same as .NETs SaveFileDialog, except that it also allows you to select read-only files.
    /// 
    /// Based on the native Common Item Dialog, which is also used internally by SaveFileDialog. Uses
    /// the OnSelectionChange event to temporarily remove the read-only flag of selected files to
    /// trick the dialog. Unfortunately, this event is not exponsed in the .NET version.
    /// 
    /// Since the Common Item Dialog was not available until Windows Vista, call the static IsSupported()
    /// method first. If it returns false, use the regular SaveFileDialog instead. On XP, the regular 
    /// dialog works also for read-only files.
    /// 
    /// Note that this trick won't work where elevated rights are needed (e.g. in the root folder of C:).
    /// </summary>
    public class SaveFileDialogRO : IDisposable
    {
        private const int S_OK = 0;

        private FileDialogNative.IFileSaveDialog dialog;
        private string defaultExt = string.Empty;
        private FileDialogNative.FOS options;
        private string filter = string.Empty;
        private string initialDirectory = string.Empty;
        private string title = string.Empty;

        /// <summary>
        /// Returns true, if Common Item Dialog is supported, which SaveFileDialogRO uses internally.
        /// If not, just use the regular SaveFileDialog.
        /// </summary>
        public static bool IsSupported()
        {
            return Environment.OSVersion.Version.Major > 5;
        }

        public SaveFileDialogRO()
        {
             dialog = (FileDialogNative.IFileSaveDialog)new FileDialogNative.FileSaveDialogRCW();
             dialog.GetOptions(out options);
        }

        ~SaveFileDialogRO()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected void Dispose(bool disposing)
        {
            Marshal.ReleaseComObject(dialog);
        }

        /// <summary>
        /// Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a file name 
        /// that does not exist.
        /// </summary>
        public bool CheckFileExists 
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_FILEMUSTEXIST) != 0;
            }
            set
            {
                if (value)
                    options |= FileDialogNative.FOS.FOS_FILEMUSTEXIST;
                else
                    options &= ~FileDialogNative.FOS.FOS_FILEMUSTEXIST;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a 
        /// path that does not exist.
        /// </summary>
        public bool CheckPathExists
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_PATHMUSTEXIST) != 0;
            }
            set
            {
                if (value)
                    options |= FileDialogNative.FOS.FOS_PATHMUSTEXIST;
                else
                    options &= ~FileDialogNative.FOS.FOS_PATHMUSTEXIST;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the dialog box prompts the user for permission to create a 
        /// file if the user specifies a file that does not exist.
        /// </summary>
        public bool CreatePrompt 
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_CREATEPROMPT) != 0;
            }
            set
            {
                if (value)
                    options |= FileDialogNative.FOS.FOS_CREATEPROMPT;
                else
                    options &= ~FileDialogNative.FOS.FOS_CREATEPROMPT;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets the default file name extension.
        /// </summary>
        public string DefaultExt
        {
            get
            {
                return defaultExt;
            }
            set
            {
                dialog.SetDefaultExtension(value);
                defaultExt = value;
            }
        }

        /// <summary>
        /// Gets or sets the default file name extension.
        /// </summary>
        public bool DereferenceLinks
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_NODEREFERENCELINKS) == 0;
            }
            set
            {
                if (!value)
                    options |= FileDialogNative.FOS.FOS_NODEREFERENCELINKS;
                else
                    options &= ~FileDialogNative.FOS.FOS_NODEREFERENCELINKS;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets a string containing the file name selected in the file dialog box.
        /// </summary>
        public string FileName 
        {
            get
            {
                // Get the selected file name (fails if the dialog has been cancelled or not yet been shown)
                string fileName;
                try
                {
                    FileDialogNative.IShellItem item;
                    dialog.GetResult(out item);

                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH, out fileName);
                }
                catch (Exception)
                {
                    // Return the name that was set via SetFileName (fails if none has been set)
                    try
                    {
                        dialog.GetFileName(out fileName);
                    }
                    catch (Exception)
                    {
                        fileName = string.Empty;
                    }
                }
                return fileName;
            }
            set
            {
                dialog.SetFileName(value);
            }
        }

        /// <summary>
        /// Gets the file names of all selected files in the dialog box.
        /// For the SaveFileDialog, this will always be at most a single file.
        /// </summary>
        public string[] FileNames
        {
            get
            {
                // Get the selected file name (fails if the dialog has been cancelled or not yet been shown)
                try
                {
                    string fileName;
                    FileDialogNative.IShellItem item;
                    dialog.GetResult(out item);

                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH, out fileName);
                    return new string[] { fileName };
                }
                catch (Exception)
                {
                    return new string[0];
                }
            }
        }

        /// <summary>
        /// Gets or sets the current file name filter string, which determines the choices that appear 
        /// in the "Save as file type" or "Files of type" box in the dialog box.
        /// </summary>
        /// <remarks>
        /// For each filtering option, the filter string contains a description of the filter, followed 
        /// by the vertical bar (|) and the filter pattern. The strings for different filtering options are 
        /// separated by the vertical bar.</br>
        /// The following is an example of a filter string:</br>
        /// Text files (*.txt)|*.txt|All files (*.*)|*.*
        /// </remarks>
        public string Filter 
        {
            get
            {
                return filter;
            }
            set
            {
                // Split at vertical bars
                string[] types = value.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                if (types.Length == 0 || types.Length % 2 != 0)
                    throw new ArgumentException("Invalid filter: " + value);

                // Convert to COMDLG_FILTERSPEC array
                int numTypes = types.Length / 2;
                FileDialogNative.COMDLG_FILTERSPEC[] specs = new FileDialogNative.COMDLG_FILTERSPEC[numTypes];
                for (int i = 0; i < numTypes; ++i)
                {
                    specs[i] = new FileDialogNative.COMDLG_FILTERSPEC
                    {
                        pszName = types[i * 2 + 0],
                        pszSpec = types[i * 2 + 1],
                    };
                }

                // Set new filter
                dialog.SetFileTypes((uint)numTypes, specs);
                filter = value;
            }
        }

        /// <summary>
        /// Gets or sets the index of the filter currently selected in the file dialog box.
        /// Note: The index value of the first filter entry is 1!
        /// </summary>
        public int FilterIndex 
        {
            get
            {
                uint index;
                dialog.GetFileTypeIndex(out index);
                return (int)index;
            }
            set
            {
                dialog.SetFileTypeIndex((uint)value);
            }
        }

        /// <summary>
        /// Gets or sets the initial directory displayed by the file dialog box.
        /// </summary>
        public string InitialDirectory 
        {
            get
            {
                return initialDirectory;
            }
            set
            {
                FileDialogNative.IShellItem item;
                IntPtr idl;
                uint atts = 0;
                if (SHILCreateFromPath(value, out idl, ref atts) == S_OK)
                {
                    if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == S_OK)
                    {
                        dialog.SetFolder(item);
                        initialDirectory = value;
                    }

                    CoTaskMemFree(idl);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the Save As dialog box displays a warning if the user 
        /// specifies a file name that already exists.
        /// </summary>
        public bool OverwritePrompt
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_OVERWRITEPROMPT) != 0;
            }
            set
            {
                if (value)
                    options |= FileDialogNative.FOS.FOS_OVERWRITEPROMPT;
                else
                    options &= ~FileDialogNative.FOS.FOS_OVERWRITEPROMPT;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the dialog box restores the current directory before closing.
        /// </summary>
        public bool RestoreDirectory 
        {
            get
            {
                return (options & FileDialogNative.FOS.FOS_NOCHANGEDIR) != 0;
            }
            set
            {
                if (value)
                    options |= FileDialogNative.FOS.FOS_NOCHANGEDIR;
                else
                    options &= ~FileDialogNative.FOS.FOS_NOCHANGEDIR;
                dialog.SetOptions(options);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the Help button is displayed in the file dialog box.
        /// </summary>
        public bool ShowHelp
        {
            get
            {
                return true;
            }
            set
            {
                // seems to be always true in case of the Common Item Dialog
            }
        }

        /// <summary>
        /// Gets or sets whether the dialog box supports displaying and saving files that have multiple file name extensions.
        /// </summary>
        public bool SupportMultiDottedExtensions
        {
            get
            {
                return true;
            }
            set
            {
                // seems to be always true in case of the Common Item Dialog
            }
        }

        /// <summary>
        /// Gets or sets the file dialog box title.
        /// </summary>
        public string Title 
        {
            get
            {
                return title;
            }
            set
            {
                dialog.SetTitle(value);
                title = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the dialog box accepts only valid Win32 file names.
        /// </summary>
        public bool ValidateNames
        {
            get
            {
                return true;
            }
            set
            {
                // seems to be always true in case of the Common Item Dialog
            }
        }

        /// <summary>
        /// Runs the dialog box with a default owner.
        /// </summary>
        public DialogResult ShowDialog()
        {
            return ShowDialog(null);
        }

        /// <summary>
        /// Runs the dialog box with the specified owner.
        /// </summary>
        public DialogResult ShowDialog(IWin32Window owner)
        {
            // Set event handler
            SaveFileDialogROEvents events = new SaveFileDialogROEvents();
            uint cookie;
            dialog.Advise(events, out cookie);

            // Show dialog
            int hr = dialog.Show(owner != null ? owner.Handle : IntPtr.Zero);

            // Remove event handler
            dialog.Unadvise(cookie);
            events.RestoreAttribute();      // needed in case of cancel

            // Convert return value to DialogResult
            return hr == S_OK ? DialogResult.OK : DialogResult.Cancel;
        }

        /// <summary>
        /// Event handler, which temporarily removes the read-only flag of selected files.
        /// </summary>
        class SaveFileDialogROEvents : FileDialogNative.IFileDialogEvents
        {
            FileInfo lastReadOnlyFile = null;

            public int OnFileOk(FileDialogNative.IFileDialog pfd)
            {
                // This method is not called in case of cancel
                RestoreAttribute();
                return S_OK;
            }

            public int OnFolderChanging(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psiFolder)
            {
                return S_OK;
            }

            public void OnFolderChange(FileDialogNative.IFileDialog pfd)
            {
                RestoreAttribute();
            }

            public void OnSelectionChange(FileDialogNative.IFileDialog pfd)
            {
                // Get selected file
                string name;
                try
                {
                    FileDialogNative.IShellItem item;
                    pfd.GetCurrentSelection(out item);
                    item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_FILESYSPATH, out name);
                }
                catch (Exception)
                {
                    // No file selected yet
                    return;
                }

                // Has it changed?
                if (lastReadOnlyFile != null && lastReadOnlyFile.FullName == name)
                    return;

                // Restore read-only attribute of the previous file, if necessary
                RestoreAttribute();

                // Remove read-only attribute of the selected file, if necessary
                FileInfo f = new FileInfo(name);
                if (f.Exists && (f.Attributes & FileAttributes.ReadOnly) != 0)
                {
                    try
                    {
                        f.Attributes &= ~FileAttributes.ReadOnly;
                        lastReadOnlyFile = f;
                    }
                    catch (Exception)
                    {
                        // Not enough rights, nothing we can do
                        return;
                    }
                }
            }

            public void OnShareViolation(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_SHAREVIOLATION_RESPONSE pResponse)
            {
                pResponse = FileDialogNative.FDE_SHAREVIOLATION_RESPONSE.FDESVR_DEFAULT;
            }

            public void OnTypeChange(FileDialogNative.IFileDialog pfd)
            {
            }

            public void OnOverwrite(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_OVERWRITE_RESPONSE pResponse)
            {
                // Removing the read-only attribute in here, unfortunately does not work
                pResponse = FileDialogNative.FDE_OVERWRITE_RESPONSE.FDEOR_DEFAULT;
            }

            /// <summary>
            /// Restores the read-only attribute of the previously selected file.
            /// </summary>
            public void RestoreAttribute()
            {
                if (lastReadOnlyFile != null)
                {
                    lastReadOnlyFile.Attributes |= FileAttributes.ReadOnly;
                    lastReadOnlyFile = null;
                }
            }
        }

        [DllImport("shell32.dll")]
        private static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut);

        [DllImport("shell32.dll")]
        private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out FileDialogNative.IShellItem ppsi);

        [DllImport("ole32.dll")]
        public static extern void CoTaskMemFree(IntPtr ptr);
    }
}

You also need these bindings:

http://www.dotnetframework.org/default.aspx/DotNET/DotNET/8@0/untmp/whidbey/REDBITS/ndp/fx/src/WinForms/Managed/System/WinForms/FileDialog_Vista_Interop@cs/1/FileDialog_Vista_Interop@cs

 
精彩推荐