1
// Copyright (c) 2006, Gustavo Franco
2
// Email: gustavo_franco@hotmail.com
3
// All rights reserved.
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
8
// Redistributions of source code must retain the above copyright notice,
9
// this list of conditions and the following disclaimer.
10
// Redistributions in binary form must reproduce the above copyright notice,
11
// this list of conditions and the following disclaimer in the documentation
12
// and/or other materials provided with the distribution.
14
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
15
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
16
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
17
// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
25
using System.Threading;
26
using System.Windows.Forms;
27
using System.ComponentModel;
28
using System.Collections.Generic;
29
using System.Runtime.InteropServices;
31
using CustomControls.OS;
33
namespace CustomControls.Controls
35
[Author("Franco, Gustavo")]
36
public partial class OpenFileDialogEx : UserControl
39
public delegate void FileNameChangedHandler(OpenFileDialogEx sender, string filePath);
43
public event FileNameChangedHandler FileNameChanged;
44
public event FileNameChangedHandler FolderNameChanged;
45
public event EventHandler ClosingDialog;
48
#region Constants Declaration
49
internal const SetWindowPosFlags UFLAGSHIDE =
50
SetWindowPosFlags.SWP_NOACTIVATE |
51
SetWindowPosFlags.SWP_NOOWNERZORDER |
52
SetWindowPosFlags.SWP_NOMOVE |
53
SetWindowPosFlags.SWP_NOSIZE |
54
SetWindowPosFlags.SWP_HIDEWINDOW;
57
#region Variables Declaration
58
private AddonWindowLocation mStartLocation = AddonWindowLocation.Right;
59
private FolderViewMode mDefaultViewMode= FolderViewMode.Default;
60
private FileDialog fileDialog;
61
private DummyForm form;
65
public OpenFileDialogEx () : this (new OpenFileDialog ())
69
public OpenFileDialogEx (FileDialog fileDialog)
71
if (fileDialog == null)
72
throw new ArgumentNullException ("fileDialog");
74
InitializeComponent();
75
// dlgOpen.AutoUpgradeEnabled = false;
76
//SetStyle(ControlStyles.SupportsTransparentBackColor, true);
78
this.fileDialog = fileDialog;
83
public FileDialog FileDialog
85
get {return fileDialog;}
88
[DefaultValue(AddonWindowLocation.Right)]
89
public AddonWindowLocation StartLocation
91
get {return mStartLocation;}
92
set {mStartLocation = value;}
95
[DefaultValue(FolderViewMode.Default)]
96
public FolderViewMode DefaultViewMode
98
get {return mDefaultViewMode;}
99
set {mDefaultViewMode = value;}
102
protected Rectangle FileNameLabelRect
105
if (form == null || form.NativeDialog == null)
106
return Rectangle.Empty;
108
return form.NativeDialog.FileNameLabelRect;
112
protected Rectangle FileNameComboRect
115
if (form == null || form.NativeDialog == null)
116
return Rectangle.Empty;
118
return form.NativeDialog.FileNameComboRect;
124
public virtual void OnFileNameChanged(string fileName)
126
if (FileNameChanged != null)
127
FileNameChanged(this, fileName);
130
public virtual void OnFolderNameChanged(string folderName)
132
if (FolderNameChanged != null)
133
FolderNameChanged(this, folderName);
136
public virtual void OnClosingDialog()
138
if (ClosingDialog != null)
139
ClosingDialog(this, new EventArgs());
142
protected virtual void OnShow(EventArgs args)
148
public DialogResult ShowDialog()
150
return ShowDialog(null);
153
public DialogResult ShowDialog(IWin32Window owner)
155
form = new DummyForm(this);
157
form.Icon = ((Form)owner).Icon; // Inherit the app/window icon
160
Win32.SetWindowPos(form.Handle, IntPtr.Zero, 0, 0, 0, 0, UFLAGSHIDE);
161
form.WatchForActivate = true;
164
return fileDialog.ShowDialog(form);
168
MessageBox.Show(ex.Message);
169
return DialogResult.Cancel;
179
#region Helper Classes
180
[Author("Franco, Gustavo")]
181
private class OpenDialogNative : NativeWindow, IDisposable
183
#region Constants Declaration
184
private SetWindowPosFlags UFLAGSSIZE =
185
SetWindowPosFlags.SWP_NOACTIVATE |
186
SetWindowPosFlags.SWP_NOOWNERZORDER |
187
SetWindowPosFlags.SWP_NOMOVE;
188
private SetWindowPosFlags UFLAGSHIDE =
189
SetWindowPosFlags.SWP_NOACTIVATE |
190
SetWindowPosFlags.SWP_NOOWNERZORDER |
191
SetWindowPosFlags.SWP_NOMOVE |
192
SetWindowPosFlags.SWP_NOSIZE |
193
SetWindowPosFlags.SWP_HIDEWINDOW;
194
private SetWindowPosFlags UFLAGSZORDER =
195
SetWindowPosFlags.SWP_NOACTIVATE |
196
SetWindowPosFlags.SWP_NOMOVE |
197
SetWindowPosFlags.SWP_NOSIZE;
200
#region Variables Declaration
201
private Size mOriginalSize;
202
private IntPtr mOpenDialogHandle;
203
private IntPtr mListViewPtr;
204
private WINDOWINFO mListViewInfo;
205
private BaseDialogNative mBaseDialogNative;
206
private IntPtr mComboFolders;
207
private WINDOWINFO mComboFoldersInfo;
208
private IntPtr mGroupButtons;
209
private WINDOWINFO mGroupButtonsInfo;
210
private IntPtr mComboFileName;
211
private WINDOWINFO mComboFileNameInfo;
212
private IntPtr mComboExtensions;
213
private WINDOWINFO mComboExtensionsInfo;
214
private IntPtr mOpenButton;
215
private WINDOWINFO mOpenButtonInfo;
216
private IntPtr mCancelButton;
217
private WINDOWINFO mCancelButtonInfo;
218
private IntPtr mHelpButton;
219
private WINDOWINFO mHelpButtonInfo;
220
private OpenFileDialogEx mSourceControl;
221
private IntPtr mToolBarFolders;
222
private WINDOWINFO mToolBarFoldersInfo;
223
private IntPtr mLabelFileName;
224
private WINDOWINFO mLabelFileNameInfo;
225
private IntPtr mLabelFileType;
226
private WINDOWINFO mLabelFileTypeInfo;
227
private IntPtr mChkReadOnly;
228
private WINDOWINFO mChkReadOnlyInfo;
229
private bool mIsClosing = false;
230
private bool mInitializated = false;
231
private WINDOWINFO mOpenDialogWindowInfo;
232
private RECT mOpenDialogWindowRect = new RECT();
233
private RECT mOpenDialogClientRect = new RECT();
237
public OpenDialogNative(IntPtr handle, OpenFileDialogEx sourceControl)
239
mOpenDialogHandle = handle;
240
mSourceControl = sourceControl;
241
AssignHandle(mOpenDialogHandle);
246
private void BaseDialogNative_FileNameChanged(BaseDialogNative sender, string filePath)
248
if (mSourceControl != null)
249
mSourceControl.OnFileNameChanged(filePath);
252
private void BaseDialogNative_FolderNameChanged(BaseDialogNative sender, string folderName)
254
if (mSourceControl != null)
255
mSourceControl.OnFolderNameChanged(folderName);
260
public bool IsClosing
262
get {return mIsClosing;}
263
set {mIsClosing = value;}
266
public Rectangle FileNameLabelRect
269
return RectToDialogClient(mLabelFileNameInfo.rcWindow);
273
public Rectangle FileNameComboRect
276
return RectToDialogClient(mComboFileNameInfo.rcWindow);
282
public void Dispose()
285
if (mBaseDialogNative != null)
287
mBaseDialogNative.FileNameChanged -= new BaseDialogNative.FileNameChangedHandler(BaseDialogNative_FileNameChanged);
288
mBaseDialogNative.FolderNameChanged -= new BaseDialogNative.FileNameChangedHandler(BaseDialogNative_FolderNameChanged);
289
mBaseDialogNative.Dispose();
294
#region Private Methods
295
private Rectangle RectToDialogClient(RECT rect)
297
uint locX = mOpenDialogWindowRect.left + mOpenDialogWindowInfo.cxWindowBorders;
298
uint locY = mOpenDialogWindowRect.top + mOpenDialogWindowInfo.cyWindowBorders;
305
return new Rectangle ((int)rect.left, (int)rect.top,
306
(int)(rect.right - rect.left), (int)(rect.bottom - rect.top));
309
private void PopulateWindowsHandlers()
311
Win32.EnumChildWindows(mOpenDialogHandle, new Win32.EnumWindowsCallBack(OpenFileDialogEnumWindowCallBack), 0);
314
private bool OpenFileDialogEnumWindowCallBack(IntPtr hwnd, int lParam)
316
StringBuilder className = new StringBuilder(256);
317
Win32.GetClassName(hwnd, className, className.Capacity);
318
int controlID = Win32.GetDlgCtrlID(hwnd);
319
WINDOWINFO windowInfo;
320
Win32.GetWindowInfo(hwnd, out windowInfo);
323
if (className.ToString().StartsWith("#32770"))
325
mBaseDialogNative = new BaseDialogNative(hwnd);
326
mBaseDialogNative.FileNameChanged += new BaseDialogNative.FileNameChangedHandler(BaseDialogNative_FileNameChanged);
327
mBaseDialogNative.FolderNameChanged += new BaseDialogNative.FileNameChangedHandler(BaseDialogNative_FolderNameChanged);
331
switch((ControlsID) controlID)
333
case ControlsID.DefaultView:
335
Win32.GetWindowInfo(hwnd, out mListViewInfo);
336
if (mSourceControl.DefaultViewMode != FolderViewMode.Default)
337
Win32.SendMessage(mListViewPtr, (int) Msg.WM_COMMAND, (int) mSourceControl.DefaultViewMode, 0);
339
case ControlsID.ComboFolder:
340
mComboFolders = hwnd;
341
mComboFoldersInfo = windowInfo;
343
case ControlsID.ComboFileType:
344
mComboExtensions = hwnd;
345
mComboExtensionsInfo = windowInfo;
347
case ControlsID.ComboFileName:
348
if (className.ToString().ToLower() == "comboboxex32")
350
mComboFileName = hwnd;
351
mComboFileNameInfo = windowInfo;
354
case ControlsID.GroupFolder:
355
mGroupButtons = hwnd;
356
mGroupButtonsInfo = windowInfo;
358
case ControlsID.LeftToolBar:
359
mToolBarFolders = hwnd;
360
mToolBarFoldersInfo = windowInfo;
362
case ControlsID.ButtonOpen:
364
mOpenButtonInfo = windowInfo;
366
case ControlsID.ButtonCancel:
367
mCancelButton = hwnd;
368
mCancelButtonInfo = windowInfo;
370
case ControlsID.ButtonHelp:
372
mHelpButtonInfo = windowInfo;
374
case ControlsID.CheckBoxReadOnly:
376
mChkReadOnlyInfo = windowInfo;
378
case ControlsID.LabelFileName:
379
mLabelFileName = hwnd;
380
mLabelFileNameInfo = windowInfo;
382
case ControlsID.LabelFileType:
383
mLabelFileType = hwnd;
384
mLabelFileTypeInfo = windowInfo;
391
private void InitControls()
393
mInitializated = true;
395
// Lets get information about the current open dialog
396
Win32.GetClientRect(mOpenDialogHandle, ref mOpenDialogClientRect);
397
Win32.GetWindowInfo(mOpenDialogHandle, out mOpenDialogWindowInfo);
398
mOpenDialogWindowRect = mOpenDialogWindowInfo.rcWindow;
400
// Lets borrow the Handles from the open dialog control
401
PopulateWindowsHandlers();
403
// Resize OpenDialog to make fit our extra form
404
switch(mSourceControl.StartLocation)
406
case AddonWindowLocation.Right:
407
// Now we transfer the control to the open dialog
408
mSourceControl.Location = new Point((int) (mOpenDialogClientRect.Width - mSourceControl.Width), 0);
410
// Everything is ready, now lets change the parent
411
Win32.SetParent(mSourceControl.Handle, mOpenDialogHandle);
413
// Send the control to the back
414
Win32.SetWindowPos(mSourceControl.Handle, (IntPtr) ZOrderPos.HWND_BOTTOM, 0, 0, 0, 0, UFLAGSZORDER);
416
case AddonWindowLocation.Bottom:
417
// Now we transfer the control to the open dialog
418
mSourceControl.Location = new Point(0, (int) (mOpenDialogClientRect.Height - mSourceControl.Height));
420
// Everything is ready, now lets change the parent
421
Win32.SetParent(mSourceControl.Handle, mOpenDialogHandle);
423
// Send the control to the back
424
Win32.SetWindowPos(mSourceControl.Handle, (IntPtr) ZOrderPos.HWND_BOTTOM, 0, 0, 0, 0, UFLAGSZORDER);
426
case AddonWindowLocation.None:
427
// We don't have to do too much in this case, but set parent must be the first call
428
// because else ZOrder won't worl
429
Win32.SetParent(mSourceControl.Handle, mOpenDialogHandle);
431
// Send the control to the back
432
Win32.SetWindowPos(mSourceControl.Handle, (IntPtr) ZOrderPos.HWND_BOTTOM, 0, 0, 0, 0, UFLAGSZORDER);
439
protected override void WndProc(ref Message m)
443
case (int) Msg.WM_SHOWWINDOW:
444
mInitializated = true;
447
mSourceControl.OnShow(EventArgs.Empty);
449
case (int) Msg.WM_WINDOWPOSCHANGING:
454
WINDOWPOS pos = (WINDOWPOS) Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
455
if (mSourceControl.StartLocation == AddonWindowLocation.Right)
457
if (pos.flags != 0 && ((pos.flags & (int) SWP_Flags.SWP_NOSIZE) != (int) SWP_Flags.SWP_NOSIZE))
459
mOriginalSize = new Size(pos.cx, pos.cy);
461
pos.cx += mSourceControl.Width;
462
Marshal.StructureToPtr(pos, m.LParam, true);
466
if (mSourceControl.StartLocation == AddonWindowLocation.Bottom)
468
if (pos.flags != 0 && ((pos.flags & (int) SWP_Flags.SWP_NOSIZE) != (int) SWP_Flags.SWP_NOSIZE))
470
mOriginalSize = new Size(pos.cx, pos.cy);
472
pos.cy += mSourceControl.Height;
473
Marshal.StructureToPtr(pos, m.LParam, true);
478
RECT currentSize = new RECT();
479
Win32.GetClientRect(mOpenDialogHandle, ref currentSize);
481
switch(mSourceControl.StartLocation)
483
case AddonWindowLocation.Right: {
484
var loc = new Point((int)(currentSize.Width - mSourceControl.Width), 0);
485
if (mSourceControl.Location != loc)
486
mSourceControl.Location = loc;
487
if (mSourceControl.Height != (int) currentSize.Height)
488
mSourceControl.Height = (int) currentSize.Height;
491
case AddonWindowLocation.Bottom: {
492
var loc = new Point(0, (int)(currentSize.Height - mSourceControl.Height));
493
if (mSourceControl.Location != loc)
494
mSourceControl.Location = loc;
495
if (mSourceControl.Width != (int) currentSize.Width)
496
mSourceControl.Width = (int) currentSize.Width;
499
case AddonWindowLocation.None: {
500
if (mSourceControl.Width != (int) currentSize.Width)
501
mSourceControl.Width = (int) currentSize.Width;
502
if (mSourceControl.Height != (int) currentSize.Height)
503
mSourceControl.Height = (int) currentSize.Height;
509
case (int) Msg.WM_IME_NOTIFY:
510
if (m.WParam == (IntPtr) ImeNotify.IMN_CLOSESTATUSWINDOW)
513
mSourceControl.OnClosingDialog();
515
Win32.SetWindowPos(mOpenDialogHandle, IntPtr.Zero, 0, 0, 0, 0, UFLAGSHIDE);
516
Win32.GetWindowRect(mOpenDialogHandle, ref mOpenDialogWindowRect);
517
Win32.SetWindowPos(mOpenDialogHandle, IntPtr.Zero,
518
(int) (mOpenDialogWindowRect.left),
519
(int) (mOpenDialogWindowRect.top),
520
(int) (mOriginalSize.Width),
521
(int) (mOriginalSize.Height),
531
[Author("Franco, Gustavo")]
532
private class BaseDialogNative : NativeWindow, IDisposable
535
public delegate void FileNameChangedHandler(BaseDialogNative sender, string filePath);
539
public event FileNameChangedHandler FileNameChanged;
540
public event FileNameChangedHandler FolderNameChanged;
543
#region Variables Declaration
544
private IntPtr mhandle;
548
public BaseDialogNative(IntPtr handle)
551
AssignHandle(handle);
556
public void Dispose()
563
protected override void WndProc(ref Message m)
567
case (int) Msg.WM_NOTIFY:
568
OFNOTIFY ofNotify = (OFNOTIFY) Marshal.PtrToStructure(m.LParam, typeof(OFNOTIFY));
569
if (ofNotify.hdr.code == (uint) DialogChangeStatus.CDN_SELCHANGE)
571
StringBuilder filePath = new StringBuilder(256);
572
Win32.SendMessage(Win32.GetParent(mhandle), (int) DialogChangeProperties.CDM_GETFILEPATH, (int) 256, filePath);
573
if (FileNameChanged != null)
574
FileNameChanged(this, filePath.ToString());
576
else if (ofNotify.hdr.code == (uint) DialogChangeStatus.CDN_FOLDERCHANGE)
578
StringBuilder folderPath = new StringBuilder(256);
579
Win32.SendMessage(Win32.GetParent(mhandle), (int) DialogChangeProperties.CDM_GETFOLDERPATH, (int) 256, folderPath);
580
if (FolderNameChanged != null)
581
FolderNameChanged(this, folderPath.ToString());
590
[Author("Franco, Gustavo")]
591
private class DummyForm : Form
593
#region Variables Declaration
594
private OpenDialogNative mNativeDialog = null;
595
private OpenFileDialogEx mFileDialogEx = null;
596
private bool mWatchForActivate = false;
597
private IntPtr mOpenDialogHandle = IntPtr.Zero;
601
public DummyForm(OpenFileDialogEx fileDialogEx)
603
mFileDialogEx = fileDialogEx;
605
this.StartPosition = FormStartPosition.Manual;
606
this.Location = new Point(-32000, -32000);
607
this.ShowInTaskbar = false;
612
public bool WatchForActivate
614
get {return mWatchForActivate;}
615
set {mWatchForActivate = value;}
618
public OpenDialogNative NativeDialog
620
get {return mNativeDialog;}
625
protected override void OnClosing(CancelEventArgs e)
627
if (mNativeDialog != null)
628
mNativeDialog.Dispose();
632
protected override void WndProc(ref Message m)
634
if (mWatchForActivate && m.Msg == (int) Msg.WM_ACTIVATE)
636
mWatchForActivate = false;
637
mOpenDialogHandle = m.LParam;
638
mNativeDialog = new OpenDialogNative(m.LParam, mFileDialogEx);
648
public enum AddonWindowLocation
655
public enum ControlsID
661
LabelFileType = 0x441,
662
LabelFileName = 0x442,
666
ComboFileName = 0x47c,
667
ComboFileType = 0x470,
669
CheckBoxReadOnly= 0x410
674
[AttributeUsage(AttributeTargets.Class |
675
AttributeTargets.Enum |
676
AttributeTargets.Interface |
677
AttributeTargets.Struct,
678
AllowMultiple = true)]
679
[Author("Franco, Gustavo")]
680
internal class AuthorAttribute : Attribute
683
public AuthorAttribute(string authorName)