Search code examples
c#hookfiledialog

c# Customized file dialog to add additional control depending on selection


I would like to customize a window 10 style file dialog (open) adding additional control below the file name filed (see the attached image). And update the additional control depending on the selection if I can.

Click here to see image

How I could do it?

- Add my additional controls on the dialog

- Hook callback

I try to hook the file dialog like below code, however, it seems that the classic dialog is only available.

Please help me to figure out this.


public struct OpenFileName
{
    public Int32 lStructSize;
    public IntPtr hwndOwner;
    public IntPtr hInstance;
    public IntPtr lpstrFilter;
    public IntPtr lpstrCustomFilter;
    ...
    public OfnHookProc lpfnHook;
    ...
};
[return: MarshalAs(UnmanagedType.SysUInt)]
public delegate IntPtr OfnHookProc(IntPtr hdlg, [MarshalAs(UnmanagedType.U4)] int uiMsg, IntPtr wParam, IntPtr lParam);

public CustomizedDialog(string defaultExtension, string directoryName)
{
    // Need two buffers in unmanaged memory to hold the filename
    // Note: the multiplication by 2 is to allow for Unicode (16-bit) characters
    _fileNameBuffer = Marshal.AllocCoTaskMem(2 * _MAX_PATH);
    _fileTitleBuffer = Marshal.AllocCoTaskMem(2 * _MAX_PATH);
    _directoryBuffer = Marshal.AllocCoTaskMem(2 * _MAX_PATH);

    // Zero these two buffers
    byte[] zeroBuffer = new byte[2 * (_MAX_PATH + 1)];
    for (int i = 0; i < 2 * (_MAX_PATH + 1); i++) zeroBuffer[i] = 0;
    Marshal.Copy(zeroBuffer, 0, _fileNameBuffer, 2 * _MAX_PATH);
    Marshal.Copy(zeroBuffer, 0, _fileTitleBuffer, 2 * _MAX_PATH);
    Marshal.Copy(zeroBuffer, 0, _directoryBuffer, 2 * _MAX_PATH);

    // copy initial directory name into unmanaged memory buffer
    byte[] directoryBytes = Encoding.Unicode.GetBytes(directoryName);
    Marshal.Copy(directoryBytes, 0, _directoryBuffer, directoryBytes.Length);

    // Populate the OPENFILENAME structure
    // The flags specified are the minimal set to get the appearance and behaviour we need
    _ofn.lStructSize = Marshal.SizeOf(_ofn);
    _ofn.lpstrFile = _fileNameBuffer;
    _ofn.nMaxFile = _MAX_PATH + 1;
    _ofn.lpstrDefExt = Marshal.StringToCoTaskMemUni(defaultExtension);
    _ofn.lpstrFileTitle = _fileTitleBuffer;
    _ofn.nMaxFileTitle = _MAX_PATH + 1;
    _ofn.lpstrInitialDir = _directoryBuffer;
    _ofn.lpstrFilter = Marshal.StringToCoTaskMemUni(String.Format(CultureInfo.InvariantCulture, "txt \0*.txt"));

    string title = String.Format(CultureInfo.InvariantCulture, "title");
    _ofn.lpstrTitle = Marshal.StringToCoTaskMemUni(title);
    _ofn.lpfnHook = new OfnHookProc(MyHookProc);
}

public bool Show()
{
    User32.GetOpenFileName(ref _ofn);

    return true;
}

public IntPtr MyHookProc(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int msg, IntPtr wParam, IntPtr lParam)
{
    ...
}

Solution

  • I found the solution my self. Please try to use CommonOpenFileDialog or CommonSaveFileDialog in Windows7APICodePack. https://www.nuget.org/packages/Windows7APICodePack-Shell/

    refers

    It is really easy to implement with the package. Here is an example to add controls.

        public static CommonOpenFileDialog OpenFileDialog(string title, List<CommonFileDialogFilter> filters, string initialDirectory = "", bool multiselect = false)
        {
            var openFilerDialog = new CommonOpenFileDialog();
            openFilerDialog.EnsureReadOnly = true;
            openFilerDialog.IsFolderPicker = false;
            openFilerDialog.AllowNonFileSystemItems = false;
            openFilerDialog.Multiselect = multiselect;
            openFilerDialog.Title = title;
    
            if (filters != null)
            {
                foreach (var filter in filters)
                {
                    openFilerDialog.Filters.Add(filter);
                }
            }
    
            if (!string.IsNullOrEmpty(initialDirectory))
            {
                openFilerDialog.InitialDirectory = initialDirectory; // Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
            }
    
            return openFilerDialog;
        }
    
        private void CustomOpenFileDialog_Click(object sender, RoutedEventArgs e)
        {
            var dialog = FileDialog.OpenFileDialog("Custom OpenFileDialog", new List<CommonFileDialogFilter>() { new CommonFileDialogFilter("stl", "*.stl") });
            AddOpenFileDialogCustomControls(dialog);
            var dialogResult = dialog.ShowDialog();
        }
    
        public static void AddOpenFileDialogCustomControls(CommonFileDialog openDialog)
        {
            // Add a RadioButtonList
            CommonFileDialogRadioButtonList list = new CommonFileDialogRadioButtonList("radioButtonOptions");
            list.Items.Add(new CommonFileDialogRadioButtonListItem("Option A"));
            list.Items.Add(new CommonFileDialogRadioButtonListItem("Option B"));
            list.SelectedIndex = 1;
            openDialog.Controls.Add(list);
        }