【问题标题】:Customize Save file dialog to be similar to underlying OS save dialog C#自定义保存文件对话框以类似于底层操作系统保存对话框 C#
【发布时间】:2015-06-24 20:20:47
【问题描述】:

我一直在用这个例子来自定义保存对话框,

http://www.codeproject.com/Articles/19566/Extend-OpenFileDialog-and-SaveFileDialog-the-easy

这很好用,我也可以自定义对话框。但是,我看到自定义对话框不遵循底层的 windows 样式。例如,如果我在 Windows 7 中,对话框将如下所示,

这是一个来自 word 的保存对话框,它确实有一些选项,如标签和东西。但外观与操作系统保存对话框相同。 但是,带有上述链接的自定义保存对话框看起来像这样,

为什么它不遵循操作系统提供的内容?有什么办法可以处理吗?


好的,我研究了一下,我可以使用 Microsoft.WindowsAPICodePack.Dialogs 中的 CommonSaveFileDialog 并创建底层的保存对话框(与 Windows 7 样式匹配。)。我安装了 WindowsAPI shell 包并使用 CommonSaveFileDialog 控件创建了这样的东西,

标记为红色的控件实际上是这些 API 中提供的 CommonFileDialogLabel / CommonFileDialogTextBox / CommonFileDialogComboBox 等。但现在我的问题是如何向其中添加用户控件/自定义控件?我需要完全控制我添加的内容,以便它可以成为用户控件。任何想法..请帮助谢谢。

【问题讨论】:

  • 那是Windows XP常用的对话框样式。如果你想使用新的 Visma/7/etc。样式,那么您必须使用新的公共项目对话框msdn.microsoft.com/en-us/library/windows/desktop/…。另见这篇文章codeproject.com/Articles/16678/…
  • 是的,看起来像。它在远景后发生了变化。但是您提供的链接有 C++ / VC++ 代码,我正在寻找 C#.net 还有其他链接吗?谢谢!
  • 有人可以帮忙吗?我试图访问 IFileDialogCustomize 示例 C#,但我无法使用其中任何一个。一个示例或示例将非常有帮助!

标签: c# windows winforms


【解决方案1】:

建议的解决方案如前所述:

另存为文件对话框(在此示例中使用)与一个名为 CustomSaveFileDialog 的用户控件相关联。它的优点是它存在于工具箱中,并且它自动实现了 IDisposable 接口。但是,它也可以是一个简单的 C# 类。

这个控件有一个构造函数,它接受一个任意应用程序特定的用户控件,它承载了所有要显示在文件对话框中的元素。当我答对问题时,这就是必需的。

CustomSaveFileDialog 具有以下属性:

  • 接受停靠在文件底部的任意用户控件 对话框,即它们遵循文件对话框的大小调整
  • 其他元素(按钮、图像、 复选框等)是必要的。他们的行为很正常,就像在其他 窗户。

这是所描述类的代码:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace customSaveFileDialog
{
    public partial class CustomSaveFileDialog : UserControl
    {
        //https://stackoverflow.com/questions/9665579/setting-up-hook-on-windows-messages
        delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
            IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        const uint WINEVENT_OUTOFCONTEXT = 0;

        [DllImport("user32.dll")]
        private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
           hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
           uint idThread, uint dwFlags);

        [DllImport("user32.dll")]
        private static extern bool UnhookWinEvent(IntPtr hWinEventHook);

        [DllImport("user32.dll")]
        private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
        private struct RECT { public int Left; public int Top; public int Right; public int Bottom; }

        [DllImport("user32.dll")]
        private static extern bool GetClientRect(IntPtr hWnd, out RECT rc);

        [DllImport("kernel32.dll")]
        private static extern uint GetLastError();

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetParent(IntPtr hwndChild, IntPtr hwndNewParent);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr GetParent(IntPtr hWnd);

        private IntPtr hDlg;        // Save As dialog handle
        private IntPtr hHook;       // Event hook
        private IntPtr hCtrl;       // App. specific user control handle

        UserControl ctrl;           // App. specific user control

        //Static variable containing the instance object
        private static CustomSaveFileDialog customSaveFileDialog;

        //public property for the user
        //theSaveFileDialog has been added to the control in the designer from the Toolbox
        public SaveFileDialog Dlg { get { return theSaveFileDialog; } }

        //Event hook delegate
        private static WinEventDelegate procDelegate = new WinEventDelegate(WinEventProc);

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ctrl">The User Control to be displayed in the file dialog</param>
        public CustomSaveFileDialog(UserControl ctrl)
        {
            InitializeComponent();

            customSaveFileDialog = this;
            this.ctrl = ctrl;
            hCtrl = ctrl.Handle;

            //Setup Hook; for simplicity, hook all possible events from the current process
            hHook = SetWinEventHook(1, 0x7fffffff, IntPtr.Zero,
                    procDelegate, (uint)Process.GetCurrentProcess().Id, 0, WINEVENT_OUTOFCONTEXT);
        }


        // Hook function
        static void WinEventProc(IntPtr hWinEventHook, uint eventType,
            IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            CustomSaveFileDialog csfdg = customSaveFileDialog;
            if (csfdg.hDlg == IntPtr.Zero)
                csfdg.hDlg = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "Save As");

            if (hwnd == csfdg.hDlg) 
            {
                IntPtr hParent = GetParent(csfdg.hCtrl);

                //this is done only once
                if (!(hParent == csfdg.hDlg))
                    SetParent(csfdg.hCtrl, csfdg.hDlg);   //Bind the user control to the Common Dialog

                RECT cliRect;
                GetClientRect(csfdg.hDlg, out cliRect);

                //Position the button in the file dialog
                MoveWindow(csfdg.hCtrl, cliRect.Left + 130, cliRect.Bottom - 55, 500, 60, true);
            }
        }
    }
}

最重要的部分是windows事件的挂钩。这取自that post

可能需要注意的是,“FindWindowEx”函数(在 WinEventProc 中)会查找所有标题为“另存为”的通用对话框(可能还有更多)。如果这应该是一个问题,则需要进行更多过滤,例如仅在当前线程中搜索。可以在here找到这样的搜索功能。

此外(上述代码中未显示)CustormSaveFileDialog.desinger.cs 中的“Dispose”方法包含以 hHook 句柄为参数的 Unhook 函数。

该软件已在 Windows7 的调试模式下进行了测试。作为测试,实现了一个带有按钮的简单表单窗口:

        //Test for the customized "Save As" dialog
        private void button1_Click(object sender, EventArgs e)
        {
            //Arbitrary User Control
            myUserControl ctrl = new myUserControl();

            using (CustomSaveFileDialog csfdg = new CustomSaveFileDialog(ctrl))
            {
                csfdg.Dlg.FileName = "test";

                //Show the Save As dialog associated to the CustomFileDialog control
                DialogResult res = csfdg.Dlg.ShowDialog();
                if (res == System.Windows.Forms.DialogResult.OK)
                    MessageBox.Show("Save Dialog Finished");
            }
        }

并且 - 以及测试 - 特定于应用程序的用户控件处理以下事件:

using System;
using System.Windows.Forms;

namespace CustomFile
{
    public partial class myUserControl : UserControl
    {
        public myUserControl()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Button Clicked");
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Image Clicked");

        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (!checkBox1.Checked)
                pictureBox1.Visible = false;
            else
                pictureBox1.Visible = true;
        }
    }
}

产生以下输出:

下一张图是另一个截图,文件对话框调整了大小,显示图片的复选框没有选中。

【讨论】:

  • 这可行,但用户控件不能大于您在图片中显示的大小。 “另存为类型”和“浏览/隐藏文件夹”链接之间的空间很小。我的用户控制可能更大,我需要额外的空间。这可能吗?
  • 是的,你是对的。所提出的解决方案仅适用于对当前空间内的公共对话框进行少量添加。您可以咨询codeproject.com/Articles/16276/…,这是您在初始帖子中引用的解决方案的基础,并且比此处介绍的要复杂得多。简而言之,IMO 让我们修改各种 pinvoke 来自定义这样的对话框,这对 MS 来说是荒谬的。
  • 说得好..谢谢乔希。我会看看什么是满足我需求的最佳解决方案。
  • 有没有办法处理这个窗口的resize事件?当我们调整保存对话框的大小时,它会越过我的用户控件。我希望用户控件留在一个地方(可能带有滚动条)。我尝试了正常的停靠/锚属性,但它不起作用。
  • 有没有办法改变对话框的背景颜色?还有最喜欢的..desktop.. 列表吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
  • 2013-03-29
相关资源
最近更新 更多