views:

514

answers:

1

I'd like to change the file name of the SaveFileDialog in the event handler attached to the FileOk event, in order to replace the file name typed in by the user with another file name in some cases, while keeping the dialog open:

var dialog = new SaveFileDialog();
...
dialog.FileOk +=
    delegate (object sender, CancelEventArgs e)
    {
        if (dialog.FileName.EndsWith (".foo"))
        {
            dialog.FileName = "xyz.bar";
            e.Cancel = true;
        }
    };

Stepping through the code shows that the FileName gets indeed properly updated, but when the event handler returns, the file name displayed in the dialog does not change. I've seen that I could theoretically use Win32 code like the following to change the file name in the dialog itself:

class Win32
{
   [DllImport("User32")]
   public static extern IntPtr GetParent(IntPtr);

   [DllImport("User32")]
   public static extern int SetDlgItemText(IntPtr, int string, int);

   public const int FileTitleCntrlID = 0x47c;
}

void SetFileName(IntPtr hdlg, string name)
{
    Win32.SetDlgItemText (Win32.GetParent (hdlg), Win32.FileTitleCntrlID, name);
}

However, I've no idea where I can get the HDLG associated to the SaveFileDialog instance from. I know I can rewrite the whole SaveFileDialog wrapper myself (or use code like NuffSaveFileDialog or the CodeProject extension of SaveFileDialog), but I'd prefer to use the standard WinForms classes for technical reasons.

+2  A: 

to get the dialog handle I used reflection, then called SetFileName with that handle:

dialog.FileOk +=
    delegate (object sender, CancelEventArgs e)
    {
        if (dialog.FileName.EndsWith (".foo"))
        {
            Type type = typeof(FileDialog);
            FieldInfo info = type.GetField("dialogHWnd", BindingFlags.NonPublic 
                                                       | BindingFlags.Instance);
            IntPtr fileDialogHandle = (IntPtr)info.GetValue(dialog);

            SetFileName(fileDialogHandle, "xyz.bar");
            e.Cancel = true;
        }
    };

N.B.: in your Win32 class you only need to define SetDlgItemText function (there is no need to GetParent) and pass to it the dialog handle:

    [DllImport("User32")]
    public static extern int SetDlgItemText(IntPtr hwnd, int id, string title);

    public const int FileTitleCntrlID = 0x47c;

    void SetFileName(IntPtr hdlg, string name)
    {
        SetDlgItemText(hdlg, FileTitleCntrlID, name);
    }

EDIT:

To have the previous code working on Windows 7 (Vista also I think?), set the dialog's property ShowHelp to true:

dialog.ShowHelp = true;

the appearance will change a little bit, but I don't think it's a big deal.

najmeddine
Sorry, I was not clear : I want to replace the file name in the dialog and keep the dialog open, hence the e.Cancel = true. Of course, you can change the FileName once the dialog was successfully closed, but that's not what I need.
Pierre
ok, see my edit.
najmeddine
Great piece of code; however, with Vista, nothing happens if I try this with an OpenFileDialog. Is the FileTitleCntrlID different?
Pierre
And with the SaveFileDialog, the file name is not updated either, but the dialog reacts as if the "Hide folders" button had been pressed.
Pierre
(I tested this on Windows 7, I think it's the same behavior as Vista). I found out that 'dialogHWnd' is always set to 0. A way to get around this it to set the dialog's `ShowHelp` property to true. See my edit.
najmeddine
The dialog.ShowHelp = true did the trick; however, I lose the new functionality added since Vista and revert back to the XP-like dialogs. Thanks a lot!
Pierre