c# 自定义文件对话框,根据选择添加额外的控件

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

我想自定义一个 window 10 样式的文件对话框(打开),在文件名下方添加额外的控件(见附图)。如果可以的话,根据选择更新附加控件。

Click here to see image

我该怎么做?

- 在对话框上添加我的附加控件

- 钩子回调

我尝试像下面的代码一样挂钩文件对话框,但是,似乎只有经典对话框可用。

请帮我解决这个问题。


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 [=10=]*.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)
{
    ...
}

我自己找到了解决方案。 请尝试在 Windows7APICodePack 中使用 CommonOpenFileDialog 或 CommonSaveFileDialog。 https://www.nuget.org/packages/Windows7APICodePack-Shell/

参考

用包来实现真的很容易。 下面是一个添加控件的例子。

    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);
    }