自定义 OpenFileDialog 和 SaveFileDialog 使用用户控件
表的内容
1.介绍
2.此控件的最近更新
3.在设计时的改进
4.它是如何工作
5.如何发生的事件都挂接
6.如何添加新的属性
7.如何将位置栏添加到 OpenFileDialog 和 SaveFiledialog 对 Windows 2000 和 XP
8.使用控件
1).显示通过调用 ShowDialog 的常规方法
2).显示通过调用扩展方法 ShowDialog 的扩展类
9.历史
介绍
如果您使用 WinForms,很可能在某一时刻,你想要扩展 OpenFileDialog 或 SaveFileDialog,但你放弃了,因为那里是没有简单的方法做到这一点,尤其是如果你想要添加一些新的图形元素。甚至在 MFC 的日子里,这样一项任务不令人畏惧这是.NET 因为这些类密封的公开只有 16 属性、 5 方法和 3 事件可以用于自定义对话框。MSDN 上的马丁 · 帕里的文章可以给你洞察如何您可以自定义使用 OFNHookProc 函数和 PInvoke OpenFileDialog。它会看起来像我们要回滚到九十年代初的编程方式的时钟和添加一些 PInvoke 和编组,做任何事情。这可能是不足以放弃或看起来像第三方库的替代品。如果你正在开发 WPF,而不是 Windows 窗体,我建议,你直接跳转到我的 WPF 文章关于同一主题。然而,CastorTiu 的文章使很多更容易对那些选择要自定义这些对话框使用窗体的生活。使用本主题的他伟大的工作,试图把它甚至进一步,使这些更无痛的两个对话框中的自定义。我会只关注我的重构和他原始硬盘的改善工作环境,所以如果你需要的细节请查看 CastorTiu 的文章。虽然这篇文章使用只有 C# 代码片段,我已经为 VB 乡亲可下载 zip 文件中包含等效 VB.NET 代码。
新的这种控制是什么
若要使尽可能容易扩展,我已经向基地的控制,以及一些设计功能中添加一些额外的属性和事件。可能最赞赏的财产将会允许在设计时选择 OpenFileDialog 和 SaveFileDialog 之间的 FileDlgType。额外的属性和事件如下所示 ︰
属性 | 事件 |
它会很高兴有一些关于如何控制将看起来像的视觉线索。我不想挖太多设计时基础结构,因此用于简单的 OnPaint 覆盖绘制一条红线或点对话框在哪里触动扩展。它绘画仅在设计模式中的通知。
protected override void OnPaint(PaintEventArgs e)
{
if (DesignMode)
{
Graphics gr = e.Graphics;
{
HatchBrush hb = null;
Pen p = null;
try
{
switch (this.FileDlgStartLocation)
{
case AddonWindowLocation.Right:
hb = new System.Drawing.Drawing2D.HatchBrush
(HatchStyle.NarrowHorizontal, Color.Black, Color.Red);
p = new Pen(hb, 5);
gr.DrawLine(p, 0, 0, 0, this.Height);
break;
case AddonWindowLocation.Bottom:
hb = new System.Drawing.Drawing2D.HatchBrush
(HatchStyle.NarrowVertical, Color.Black, Color.Red);
p = new Pen(hb, 5);
gr.DrawLine(p, 0, 0, this.Width, 0);
break;
case AddonWindowLocation.BottomRight:
default:
hb = new System.Drawing.Drawing2D.HatchBrush
(HatchStyle.Sphere, Color.Black, Color.Red);
p = new Pen(hb, 5);
gr.DrawLine(p, 0, 0, 4, 4);
break;
}
}
finally
{
if (p != null)
p.Dispose();
if (hb != null)
hb.Dispose();
}
}
}
base.OnPaint(e);
}
它是如何工作
CastorTiu 的文章详细介绍了该控件的工作原理。我做了一些改进,但总的想法是一样的。
您需要获取对话框句柄之前模态,,所以你可以 '粘合在一起' 对话框与您自己的控件。
这里是流的此复合对话框的初始化 ︰
1.创建对话框中使用它的构造函数。 在这一点上仍然是没有用户界面,所以没有 Windows 消息,赶上
2.设置您想要使用虚拟 OnPrepareMSDialog() 对话框和控件本身的 Load 事件在运行时更改的属性
3.使用 DialogResult 返回和处置的控制
这里是在幕后的事情 ︰ 你开始通过创建具有正确的对话框类型作为泛型参数的帮助器类 DialogWrapper
public DialogResult ShowDialog(IWin32Window owner) { DialogResult returnDialogResult = DialogResult.Cancel; if (this.IsDisposed) return returnDialogResult; if (owner == null || owner.Handle == IntPtr.Zero) { WindowWrapper wr = new WindowWrapper (System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle); owner = wr; } OriginalCtrlSize = this.Size; _MSdialog = (FileDlgType == FileDialogType.OpenFileDlg)?new OpenFileDialog()as FileDialog:new SaveFileDialog() as FileDialog; _dlgWrapper = new WholeDialogWrapper(this); OnPrepareMSDialog(); if (!_hasRunInitMSDialog) InitMSDialog(); try { System.Reflection.PropertyInfo AutoUpgradeInfo = MSDialog.GetType().GetProperty("AutoUpgradeEnabled"); if (AutoUpgradeInfo != null) AutoUpgradeInfo.SetValue(MSDialog, false, null); returnDialogResult = _MSdialog.ShowDialog(owner); } catch (ObjectDisposedException) { } catch (Exception ex) { MessageBox.Show("unable to get the modal dialog handle", ex.Message); } return returnDialogResult; }当你调用 ShowDialog 的您的控件的公共方法时,邮件开始流动只后调用。NET 的 Open(Save)FileDialog.ShowDialog()。你得小心了 WM_ACTIVATE,和应用程序筛选器不会抓住它,因为你不得不依赖于父窗口 WndProc。发现而不是使用一种虚拟的形式,消息泵窗口会做一样精细开销更少。内部调用 AssignDummyWindow() 的 WholeDialogWrapper 的构造函数创建。
private void AssignDummyWindow()
{
_hDummyWnd = NativeMethods.CreateWindowEx(0, "Message",
null, WS_VISIBLE, 0, 0, 0, 0,HWND_MESSAGE, NULL, NULL, NULL);
if (_hDummyWnd == NULL || !NativeMethods.IsWindow(_hDummyWnd))
throw new ApplicationException("Unable to create a dummy window");
AssignHandle(_hDummyWnd);
}
因为我们有它的孩子们听的这个窗口,我们将抓住 WM_ACTIVATE 如下图所示 ︰
protected override void WndProc(ref Message m)
{
switch ((Msg)m.Msg)
{//.... code omitted
case Msg.WM_ACTIVATE:
if (_WatchForActivate && !mIsClosing && m.Msg == (int)Msg.WM_ACTIVATE)
//WM_NCACTIVATE works too
{ //Now the Open/Save Dialog is visible and about to enter the modal loop
_WatchForActivate = false;
//Now we save the real dialog window handle
_FileDialogHandle = m.LParam;
ReleaseHandle();//release the dummy window
AssignHandle(_FileDialogHandle);//assign the native open file handle
// to grab the messages
NativeMethods.GetWindowRect(_FileDialogHandle,
ref _CustomControl._DialogWindowRect);
_CustomControl._FileDialogHandle = _FileDialogHandle;
}
break;
//.... code omitted
}
base.WndProc(ref m)
一旦我们得到了真正的对话框句柄作为 _FileDialogHandle,我们可以
忘掉的虚拟窗口,开始倾听真正重要的事情。请注意如何我发布的虚拟
窗口句柄和分配新的一个。当同一 WndProc 捕获
WM_SHOWWINDOW 留言时,我们终于可以安排我们的控制和设置父 ︰
private void InitControls()
{
mInitializated = true;
// Lets get information about the current open dialog
NativeMethods.GetClientRect(new HandleRef(this,_hFileDialogHandle),
ref _DialogClientRect);
NativeMethods.GetWindowRect(new HandleRef(this,_hFileDialogHandle),
ref _DialogWindowRect);
// Lets borrow the Handles from the open dialog control
PopulateWindowsHandlers();
switch (_CustomControl.FileDlgStartLocation)
{
case AddonWindowLocation.Right:
// Now we transfer the control to the open dialog
_CustomControl.Location = new Point((int)
(_DialogClientRect.Width - _CustomControl.Width), 0);
break;
case AddonWindowLocation.Bottom:
// Now we transfer the control to the open dialog
_CustomControl.Location = new Point(0,
(int)(_DialogClientRect.Height - _CustomControl.Height));
break;
case AddonWindowLocation.BottomRight:
// We don't have to do too much in this case, just the default thing
_CustomControl.Location =
new Point((int)(_DialogClientRect.Width - _CustomControl.Width),
(int)(_DialogClientRect.Height - _CustomControl.Height));
break;
}
// Everything is ready, now lets change the parent
NativeMethods.SetParent(new HandleRef(_CustomControl,_CustomControl.Handle),
new HandleRef(this,_hFileDialogHandle));
// Send the control to the back
// NativeMethods.SetWindowPos(_CustomControl.Handle,
(IntPtr)ZOrderPos.HWND_BOTTOM, 0, 0, 0, 0, UFLAGSZORDER);
_CustomControl.MSDialog.Disposed += new EventHandler(DialogWrappper_Disposed);
}
如何的事件是上瘾了
你可能认为这段代码是详尽无遗的与有关的属性和事件,您可以使用的。嗯不完全 !我加几个属性和事件对原来的工作,但你可能还想要向它添加更多。 只是在上面的代码片段,你看看如何从开 (保存) FileDialog.Dispose 事件迷上了,但这是一个简单的。我将描述如何可以将所需的新事件添加到 FileDialogControlBase 的基础我已经添加了作为一个例子。︰ 有是还有一种称为监视 (保存) 通过 WndProc 如下图所示的对话框对象打开的 MSFileDialogWrapper 的帮助器类
protected override void WndProc(ref Message m)
{
switch ((Msg)m.Msg)
{
case Msg.WM_NOTIFY:
OFNOTIFY ofNotify = (OFNOTIFY)Marshal.PtrToStructure
(m.LParam, typeof(OFNOTIFY));
switch (ofNotify.hdr.code)
{
//.... code omitted
case (uint)DialogChangeStatus.CDN_TYPECHANGE:
{
OPENFILENAME ofn =
(OPENFILENAME)Marshal.PtrToStructure
(ofNotify.OpenFileName, typeof(OPENFILENAME));
int i = ofn.nFilterIndex;
if (_CustomCtrl != null && _filterIndex != i)
{
_filterIndex = i;
_CustomCtrl.OnFilterChanged
(this as IWin32Window, i);
}
}
break;
}
//.... code omitted
}
base.WndProc(ref m);
}
点击打开链接