程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> .Net WInform開發筆記(三)談談自制控件(自定義控件)

.Net WInform開發筆記(三)談談自制控件(自定義控件)

編輯:C#基礎知識

末日這天寫篇博客吧,既然沒來,那就紀念一下。

這次談談自制控件,也就是自定義控件,先上圖,再說

1.擴展OpenFileDialog,在OpenFileDialog中添加各種文件(.txt,.jpg,.excel等等)的預覽功能

2.重寫ListBox,增加折疊、鼠標背影、分類等功能

-----------------------------分割線--------------------------------------------------------------
一、擴展OpenFileDialog

許多軟件的打開對話框都有預覽功能,最常見的就是圖片預覽,用鼠標選擇一個圖片文件後,右側或者下側就會有該圖片的縮略圖(photoshop中屬於後者)。在winform編程中,有專門的打開文件對話框的類OpenFileDialog,但是他不提供文件預覽功能,封裝得實在太好。看看它公開那些接口

提到擴展,很多人可能想到繼承它就可以擴展它,可惜OpenFileDialog聲明為sealed,不允許從他繼承。稍微底層一點的,想到可以通過Win32 API來修改它的顯示方式,只可惜,如你所見,它根本沒提供Handle屬性,更別說HandleCreated、HandleDestroyed等事件了。那麼怎麼樣子搞呢?其實答案還是通過Win32 API,只是我們取得它的句柄的方式要復雜一點而且調用API的時機隱晦了一點。

提示:

1.Win32 API操作窗體需要知道窗體的句柄;

2.不熟悉Win32編程的同學可以先上網查查資料,特別是不知道SetParent、SetWindowPos等API是干嘛的,我覺得以下的看不懂。

為什麼說取得它的句柄復雜了一點?難道不是用“FindWindow”、“FindWindowEx”、“ EnumChildWindows”等獲取OpenFileDialog的句柄,再用“SetParent”、“SetWindowPos”等API將.net控件(本例中是DataGridView控件,當然可以使其他任何一種)添加到OpenFileDialog中去?沒錯,以上列舉出來的API都是基本要用到的,“只是用在什麼地方、什麼時候用”是個比較麻煩的問題,原因如下:

1)我們知道OpenfileDialog顯示的是模式對話框,也就是說,一旦它ShowDialog(),它以下的代碼是不會再執行的,具體原因是什麼(我以後的博客會專門講為什麼),你現在可以理解為OpenFileDialog()方法會阻塞調用線程,既然阻塞了調用線程,那麼我們再無法控制程序了(直到它返回),根本談不上再調用API獲取OpenFileDialog的句柄然後去操作它。如果有人會說,“我可以另開辟線程去取OpenFileDialog得句柄再操作它”,恩,我不否定這個方法,只是我想說,如果你真的按照這個方法去試,那麼肯定會陷入泥潭。因為你不僅要取它的句柄,你還要監視OpenFIleDialog的一舉一動,移動、縮放、用戶鼠標點擊選擇文件、更改目錄等,然後再操作.net控件(本例中是DataGridView控件,下同),讓.net控件去適應OpenFileDialog的大小等等,你會發現你忙死了,甚至有的你根本監視不了,比如用戶點擊選擇文件、更改目錄。

2)就算我們能夠在OpenFIleDialog顯示之後,取得它的句柄,那麼什麼時候再調用其他API呢?比如什麼時候調用SetWindowPos,讓.net控件適應OpenFileDialog的大小變化?什麼時候知道用戶選擇文件發生了變化?

所以,API方法什麼時候用?用在什麼地方?就是接下來要討論的東西。

我不知道各位在使用各種框架的時候,對“框架”的理解到什麼程度,我覺得可以總結成一句話“跟個2b似地注冊一些事件,然後苦逼地去寫好每一個回調方法,我們卻不知道為啥要這樣寫”,不是麼?既然這樣,那麼我們的API方法只要寫在了正確的回調方法中,我們就能到達想要的目的了。考慮幾個問題:

1)OpenFileDialog顯示,我們向其中添加.net控件。我們什麼時候知道它顯示?

2)OpenFileDialog大小發生變化時,我們要更新.net控件以適應新的大小。我們什麼時候知道OpenFileDialog的大小發生了變化?

3)OpenFileDialog中用戶選擇的文件發生了變化,我們需要知道新選擇的文件路徑,用來顯示在.net控件中。我們怎麼知道選擇了什麼文件?(這裡選擇文件指用戶用鼠標在OpenFileDialog中單擊選取,不是點擊“確定”後。)

以上所有的問題,其實在一個地方都可以知道,那就是監聽OpenFileDialog窗體的Windows消息,因為一個窗體的任何一個動作都伴隨著一系列的Windows消息(這個可以用Spy++查看)。既然這樣,那麼我們可以在窗體處理Windows消息的回調方法中調用API方法了,也就是窗體的Control.WndProc方法中。之前已經說過了,OpenFileDialog聲明為Sealed,提供的接口少之又少,我們幾乎根本不可能接觸到OpenFileDialog的WndProc,更談不上監聽Windows消息,然後調用API去操作它。這時候,NativeWindow該出場了,先來引用一下MSDN上對NativeWindow的解釋:

“提供窗口句柄和窗口過程的低級封裝。”

說了像沒說一樣,其實就是說,將一個窗口句柄與NativeWindow對象綁定後,該NativeWindow對象就能接收到這個窗體的所有消息。說到Windows消息,我想說一下Windows桌面應用程序的運行流程,其實如果看了我前一篇博客的同學應該有些了解,.Net Winform開發筆記(二)中基本提到了一些。為了配合本次講解,我再次畫了一張圖

上圖中,虛線框可以看做是一個.net中的Control類對象(或者其派生類,下同,控件即窗體、窗體即控件),正常情況下,Winform程序會按照1->2->3的步驟運行,當我們將Control類對象的Handle(就是我們常說的窗口句柄,做了一下封裝)與一個NativeWIndow對象綁定後,程序不再按照1->2->3這樣的順序運行了,他會按照1->2-1->2-2->3這樣運行,也就是說,NativeWindow對象可以攔截Control類對象的WIndows消息,我們完全可以在NativeWIndow中重寫他的WndProc方法,像處理自己的Windows消息一樣去處理Control類對象的消息。所以,我們就可以在NativeWindow對象的WndProc中調用我們的API方法。

接下來,上代碼(代碼只提供大概思路)

1.擴展對話框
代碼如下:

public class MultiOpenFileDialog
{
#region fields
private const SetWindowPosFlags UFLAGSHIDE =
SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_NOOWNERZORDER |
SetWindowPosFlags.SWP_NOMOVE |
SetWindowPosFlags.SWP_NOSIZE |
SetWindowPosFlags.SWP_HIDEWINDOW;
#endregion
public string FileName;
#region public methods
public DialogResult ShowDialog()
{
return ShowDialog(null);
}
public DialogResult ShowDialog(IWin32Window owner)
{
using (OpenFileDialog open = new OpenFileDialog())
{
MedianForm median = new MedianForm(open);
median.Show(owner);
Win32.SetWindowPos(median.Handle, IntPtr.Zero, 0, 0, 0, 0, UFLAGSHIDE); //隱藏中間窗體
DialogResult dialogresult = open.ShowDialog(median); //將median作為openfileDialog的owner
median.Close();
if (dialogresult == DialogResult.OK)
{
FileName = open.FileName;
}
return dialogresult;
}
}
#endregion
}

2.監聽Dialog的NativeWindow
代碼如下:

View Code
class DialogNativeWindow : NativeWindow,IDisposable
{
IntPtr handle; //待擴展OpenFileDialog的句柄
OpenFileDialog openfiledialog; //待擴展OpenFileDialog
DataGridView addControl; //向窗體中添加新的控件
ChildControlNativeWindow childNative;
bool init = false;

public DialogNativeWindow(IntPtr handle, OpenFileDialog openfiledialog)
{
this.handle = handle;
this.openfiledialog = openfiledialog;
AssignHandle(handle);

//設置控件信息
addControl = new DataGridView();
addControl.Width = 600;
addControl.Height = 200;
addControl.DataSource = null;
}

#region override methods
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)Msg.WM_SHOWWINDOW: //窗體顯示
{
NativeChild();
AddControl();
break;
}
case (int)Msg.WM_SIZING: //窗體大小改變
{
UpdateSize();
break;
}
case (int)Msg.WM_WINDOWPOSCHANGING: //窗體位置變化
{
UpdateLocation(m);
break;
}
}
base.WndProc(ref m);
}
#endregion

#region event handlers
void childNative_SelectPathChanged(StringBuilder path)
{
//處理選擇目錄變化事件
//...
}

void childNative_SelectFileChanged(StringBuilder file)
{
//處理選擇文件變化事件
//如果是xls文件,將其顯示在datagridview控件中
string str = file.ToString();
if (str.ToLower().EndsWith(".xls"))
{
OledbManager manager = new OledbManager();
if (manager.Connect("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=\'" + str + "\'; Extended Properties='Excel 8.0;'"))
{
DataTable tb = manager.SearchTable();
if (tb != null)
{
addControl.Rows.Clear();
addControl.Columns.Clear();
foreach (DataColumn col in tb.Columns)
{
addControl.Columns.Add(col.ColumnName, col.Caption);
}
foreach (DataRow row in tb.Rows)
{
object[] objs = new object[tb.Columns.Count];
for (int i = 0; i < tb.Columns.Count; ++i)
{
objs[i] = row[i];
}
addControl.Rows.Add(objs);
}
}
}
}
else
{
addControl.Rows.Clear();
addControl.Columns.Clear();
}
}
#endregion

#region private methods
private void NativeChild()
{
//查找openfileDialog中的子控件
Win32.EnumChildWindows(handle, new Win32.EnumWindowsCallBack(WindowCallBack), 0);
}
private void AddControl()
{
//添加控件到OpenFileDialog界面
Win32.SetParent(addControl.Handle, handle);
RECT currentSize = new RECT();
Win32.GetClientRect(handle, ref currentSize);
addControl.Height = (int)currentSize.Height;
addControl.Location = new Point((int)(currentSize.Width - addControl.Width), 0);

init = true;
}
private void UpdateLocation(Message m)
{
if (!init) //只初始化openfileDialog的大小一次
{
WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
if (pos.flags != 0 && ((pos.flags & (int)SWP_Flags.SWP_NOSIZE) != (int)SWP_Flags.SWP_NOSIZE))
{
pos.cx += addControl.Width; //修改OpenfileDialog的寬度
Marshal.StructureToPtr(pos, m.LParam, true);

RECT currentSize = new RECT();
Win32.GetClientRect(handle, ref currentSize);
addControl.Height = (int)currentSize.Height;
}
}
}
private void UpdateSize()
{
RECT currentSize = new RECT();
Win32.GetClientRect(handle, ref currentSize);
Win32.SetWindowPos(addControl.Handle, (IntPtr)ZOrderPos.HWND_BOTTOM, 0, 0, (int)addControl.Width, (int)currentSize.Height, UFLAGSSIZEEX); //新添加的控件與openfileDialog大小一致
}
private bool WindowCallBack(IntPtr handle, int lparam)
{
StringBuilder wndClass = new StringBuilder(256);
Win32.GetClassName(handle, wndClass, wndClass.Capacity);//獲取控件類名

if (wndClass.ToString().StartsWith("#32770")) //找到目標控件
{
childNative = new ChildControlNativeWindow(handle);
childNative.SelectFileChanged += new ChildControlNativeWindow.SelectFileChangedEventHandler(childNative_SelectFileChanged);
childNative.SelectPathChanged += new ChildControlNativeWindow.SelectPathChangedEventHandler(childNative_SelectPathChanged);
return true;
}
return true;
}
#endregion

#region enums
private const SetWindowPosFlags UFLAGSSIZEEX =
SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_NOOWNERZORDER |
SetWindowPosFlags.SWP_NOMOVE |
SetWindowPosFlags.SWP_ASYNCWINDOWPOS |
SetWindowPosFlags.SWP_DEFERERASE;
private const SetWindowPosFlags UFLAGSSIZE =
SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_NOOWNERZORDER |
SetWindowPosFlags.SWP_NOMOVE;
private const SetWindowPosFlags UFLAGSHIDE =
SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_NOOWNERZORDER |
SetWindowPosFlags.SWP_NOMOVE |
SetWindowPosFlags.SWP_NOSIZE |
SetWindowPosFlags.SWP_HIDEWINDOW;
#endregion

#region IDisposable 成員

public void Dispose()
{
ReleaseHandle(); //釋放與openfileDialog的句柄關聯
if (childNative != null)
{
childNative.SelectFileChanged -= new ChildControlNativeWindow.SelectFileChangedEventHandler(childNative_SelectFileChanged);
childNative.SelectPathChanged -= new ChildControlNativeWindow.SelectPathChangedEventHandler(childNative_SelectPathChanged);
childNative.Dispose();
}

}

#endregion
}

3.監聽子控件的NativeWindow
代碼如下:

class ChildControlNativeWindow : NativeWindow,IDisposable
{
IntPtr handle; //需要被監聽消息的子控件句柄
public ChildControlNativeWindow(IntPtr handle)
{
this.handle = handle;
AssignHandle(handle);
}

#region override methods
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)Msg.WM_NOTIFY:
OFNOTIFY ofNotify = (OFNOTIFY)Marshal.PtrToStructure(m.LParam, typeof(OFNOTIFY));
if (ofNotify.hdr.code == (uint)DialogChangeStatus.CDN_SELCHANGE) //openfileDialog選擇文件發生變化
{
StringBuilder file = new StringBuilder(256);
Win32.SendMessage(Win32.GetParent(handle), (int)DialogChangeProperties.CDM_GETFILEPATH, (int)256, file);
if (SelectFileChanged != null)
SelectFileChanged(file); //通知注冊者
}
else if (ofNotify.hdr.code == (uint)DialogChangeStatus.CDN_FOLDERCHANGE) //openfileDialog選擇目錄發生變化
{
StringBuilder path = new StringBuilder(256);
Win32.SendMessage(Win32.GetParent(handle), (int)DialogChangeProperties.CDM_GETFOLDERPATH, (int)256, path);
if (SelectPathChanged != null)
SelectPathChanged(path); //通知注冊者
}
break;
}
base.WndProc(ref m);
}
#endregion

#region delegate
public delegate void SelectFileChangedEventHandler(StringBuilder file);
public delegate void SelectPathChangedEventHandler(StringBuilder path);
#endregion

#region events
public event SelectFileChangedEventHandler SelectFileChanged; //當openfileDialog的選擇文件發生變化時發生
public event SelectPathChangedEventHandler SelectPathChanged; //當openfileDialog的選擇目錄發生變化時發生
#endregion

#region IDisposable 成員

public void Dispose()
{
ReleaseHandle(); //終止與子控件句柄的關聯
}

#endregion
}

4.中間過渡窗體,用來獲取OpenFileDialog的句柄
代碼如下:
 
class MedianForm : Form
{
OpenFileDialog open = null;
DialogNativeWindow dialognative;

public MedianForm(OpenFileDialog open)
{
this.open = open;
StartPosition = FormStartPosition.Manual;
Location = new System.Drawing.Point(-1000, -1000); //避免界面閃爍
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
if (dialognative != null)
{
dialognative.Dispose(); //釋放資源
}
base.OnClosing(e);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int) Msg.WM_ACTIVATE)
{
dialognative = new DialogNativeWindow(m.LParam, open); //m.LParam為要打開的窗口句柄,開始監聽OpenFileDialog的Windows消息
}
base.WndProc(ref m);
}
}

5.訪問Excel文件的類
代碼如下:

class OledbManager
{
OleDbConnection conn;
/// <summary>
/// 連接excel文件
/// </summary>
/// <param name="connstr"></param>
/// <returns></returns>
public bool Connect(string connstr)
{
try
{
conn = new OleDbConnection(connstr);
conn.Open();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 查找第一張表中的數據
/// </summary>
/// <returns></returns>
public DataTable SearchTable()
{
try
{
DataTable tb = new DataTable();
string sql = "select * from [Sheet1$]";
OleDbCommand com = new OleDbCommand(sql, conn);
OleDbDataAdapter adp = new OleDbDataAdapter(com);
adp.Fill(tb);
return tb;
}
catch
{
return null;
}
}
}

6.幾個Win32結構體、枚舉類型以及API聲明(本代碼參考CodeProject上的一篇文章)
代碼如下:

public static class Win32
{
#region Delegates
public delegate bool EnumWindowsCallBack(IntPtr hWnd, int lParam);
#endregion

#region USER32
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
[DllImport("User32.Dll")]
public static extern int GetDlgCtrlID(IntPtr hWndCtl);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int MapWindowPoints(IntPtr hWnd, IntPtr hWndTo, ref POINT pt, int cPoints);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowInfo(IntPtr hwnd, out WINDOWINFO pwi);
[DllImport("User32.Dll")]
public static extern void GetWindowText(IntPtr hWnd, StringBuilder param, int length);
[DllImport("User32.Dll")]
public static extern void GetClassName(IntPtr hWnd, StringBuilder param, int length);
[DllImport("user32.Dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsCallBack lpEnumFunc, int lParam);
[DllImport("user32.Dll")]
public static extern bool EnumWindows(EnumWindowsCallBack lpEnumFunc, int lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool ReleaseCapture();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetCapture(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr ChildWindowFromPointEx(IntPtr hParent, POINT pt, ChildFromPointFlags flags);
[DllImport("user32.dll", EntryPoint = "FindWindowExA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, StringBuilder param);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, char[] chars);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, SetWindowPosFlags flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, SetWindowPosFlags flags);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, ref RECT rect);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hwnd, ref RECT rect);
#endregion
}
#region SWP_Flags
[Flags]
public enum SWP_Flags
{
SWP_NOSIZE = 0x0001,
SWP_NOMOVE = 0x0002,
SWP_NOZORDER = 0x0004,
SWP_NOACTIVATE = 0x0010,
SWP_FRAMECHANGED = 0x0020, /* The frame changed: send WM_NCCALCSIZE */
SWP_SHOWWINDOW = 0x0040,
SWP_HIDEWINDOW = 0x0080,
SWP_NOOWNERZORDER = 0x0200, /* Don't do owner Z ordering */

SWP_DRAWFRAME = SWP_FRAMECHANGED,
SWP_NOREPOSITION = SWP_NOOWNERZORDER
}
#endregion

#region DialogChangeStatus
public enum DialogChangeStatus : long
{
CDN_FIRST = 0xFFFFFDA7,
CDN_INITDONE = (CDN_FIRST - 0x0000),
CDN_SELCHANGE = (CDN_FIRST - 0x0001),
CDN_FOLDERCHANGE = (CDN_FIRST - 0x0002),
CDN_SHAREVIOLATION = (CDN_FIRST - 0x0003),
CDN_HELP = (CDN_FIRST - 0x0004),
CDN_FILEOK = (CDN_FIRST - 0x0005),
CDN_TYPECHANGE = (CDN_FIRST - 0x0006),
}
#endregion

#region DialogChangeProperties
public enum DialogChangeProperties
{
CDM_FIRST = (0x400 + 100),
CDM_GETSPEC = (CDM_FIRST + 0x0000),
CDM_GETFILEPATH = (CDM_FIRST + 0x0001),
CDM_GETFOLDERPATH = (CDM_FIRST + 0x0002),
CDM_GETFOLDERIDLIST = (CDM_FIRST + 0x0003),
CDM_SETCONTROLTEXT = (CDM_FIRST + 0x0004),
CDM_HIDECONTROL = (CDM_FIRST + 0x0005),
CDM_SETDEFEXT = (CDM_FIRST + 0x0006)
}
#endregion

#region ImeNotify
public enum ImeNotify
{
IMN_CLOSESTATUSWINDOW = 0x0001,
IMN_OPENSTATUSWINDOW = 0x0002,
IMN_CHANGECANDIDATE = 0x0003,
IMN_CLOSECANDIDATE = 0x0004,
IMN_OPENCANDIDATE = 0x0005,
IMN_SETCONVERSIONMODE = 0x0006,
IMN_SETSENTENCEMODE = 0x0007,
IMN_SETOPENSTATUS = 0x0008,
IMN_SETCANDIDATEPOS = 0x0009,
IMN_SETCOMPOSITIONFONT = 0x000A,
IMN_SETCOMPOSITIONWINDOW = 0x000B,
IMN_SETSTATUSWINDOWPOS = 0x000C,
IMN_GUIDELINE = 0x000D,
IMN_PRIVATE = 0x000E
}
#endregion

#region FolderViewMode
public enum FolderViewMode
{
Default = 0x7028,
Icon = Default + 1,
SmallIcon = Default + 2,
List = Default + 3,
Details = Default + 4,
Thumbnails = Default + 5,
Title = Default + 6,
Thumbstrip = Default + 7,
}
#endregion

#region Enum DialogViewProperty
public enum DefaultViewType
{
Icons = 0x7029,
List = 0x702b,
Details = 0x702c,
Thumbnails = 0x702d,
Tiles = 0x702e,
}
#endregion

#region ButtonStyle
public enum ButtonStyle : long
{
BS_PUSHBUTTON = 0x00000000,
BS_DEFPUSHBUTTON = 0x00000001,
BS_CHECKBOX = 0x00000002,
BS_AUTOCHECKBOX = 0x00000003,
BS_RADIOBUTTON = 0x00000004,
BS_3STATE = 0x00000005,
BS_AUTO3STATE = 0x00000006,
BS_GROUPBOX = 0x00000007,
BS_USERBUTTON = 0x00000008,
BS_AUTORADIOBUTTON = 0x00000009,
BS_PUSHBOX = 0x0000000A,
BS_OWNERDRAW = 0x0000000B,
BS_TYPEMASK = 0x0000000F,
BS_LEFTTEXT = 0x00000020,
BS_TEXT = 0x00000000,
BS_ICON = 0x00000040,
BS_BITMAP = 0x00000080,
BS_LEFT = 0x00000100,
BS_RIGHT = 0x00000200,
BS_CENTER = 0x00000300,
BS_TOP = 0x00000400,
BS_BOTTOM = 0x00000800,
BS_VCENTER = 0x00000C00,
BS_PUSHLIKE = 0x00001000,
BS_MULTILINE = 0x00002000,
BS_NOTIFY = 0x00004000,
BS_FLAT = 0x00008000,
BS_RIGHTBUTTON = BS_LEFTTEXT
}
#endregion

#region ZOrderPos
public enum ZOrderPos
{
HWND_TOP = 0,
HWND_BOTTOM = 1,
HWND_TOPMOST = -1,
HWND_NOTOPMOST = -2
}
#endregion

#region Static Control Styles
public enum StaticControlStyles : long
{
SS_LEFT = 0x00000000,
SS_CENTER = 0x00000001,
SS_RIGHT = 0x00000002,
SS_ICON = 0x00000003,
SS_BLACKRECT = 0x00000004,
SS_GRAYRECT = 0x00000005,
SS_WHITERECT = 0x00000006,
SS_BLACKFRAME = 0x00000007,
SS_GRAYFRAME = 0x00000008,
SS_WHITEFRAME = 0x00000009,
SS_USERITEM = 0x0000000A,
SS_SIMPLE = 0x0000000B,
SS_LEFTNOWORDWRAP = 0x0000000C,
SS_OWNERDRAW = 0x0000000D,
SS_BITMAP = 0x0000000E,
SS_ENHMETAFILE = 0x0000000F,
SS_ETCHEDHORZ = 0x00000010,
SS_ETCHEDVERT = 0x00000011,
SS_ETCHEDFRAME = 0x00000012,
SS_TYPEMASK = 0x0000001F,
SS_REALSIZECONTROL = 0x00000040,
SS_NOPREFIX = 0x00000080, /* Don't do "&" character translation */
SS_NOTIFY = 0x00000100,
SS_CENTERIMAGE = 0x00000200,
SS_RIGHTJUST = 0x00000400,
SS_REALSIZEIMAGE = 0x00000800,
SS_SUNKEN = 0x00001000,
SS_EDITCONTROL = 0x00002000,
SS_ENDELLIPSIS = 0x00004000,
SS_PATHELLIPSIS = 0x00008000,
SS_WORDELLIPSIS = 0x0000C000,
SS_ELLIPSISMASK = 0x0000C000
}
#endregion

#region Combo Box styles
public enum ComboBoxStyles : long
{
CBS_SIMPLE = 0x0001,
CBS_DROPDOWN = 0x0002,
CBS_DROPDOWNLIST = 0x0003,
CBS_OWNERDRAWFIXED = 0x0010,
CBS_OWNERDRAWVARIABLE = 0x0020,
CBS_AUTOHSCROLL = 0x0040,
CBS_OEMCONVERT = 0x0080,
CBS_SORT = 0x0100,
CBS_HASSTRINGS = 0x0200,
CBS_NOINTEGRALHEIGHT = 0x0400,
CBS_DISABLENOSCROLL = 0x0800,
CBS_UPPERCASE = 0x2000,
CBS_LOWERCASE = 0x4000
}
#endregion

#region Window Styles
public enum WindowStyles : long
{
WS_OVERLAPPED = 0x00000000,
WS_POPUP = 0x80000000,
WS_CHILD = 0x40000000,
WS_MINIMIZE = 0x20000000,
WS_VISIBLE = 0x10000000,
WS_DISABLED = 0x08000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_CLIPCHILDREN = 0x02000000,
WS_MAXIMIZE = 0x01000000,
WS_CAPTION = 0x00C00000,
WS_BORDER = 0x00800000,
WS_DLGFRAME = 0x00400000,
WS_VSCROLL = 0x00200000,
WS_HSCROLL = 0x00100000,
WS_SYSMENU = 0x00080000,
WS_THICKFRAME = 0x00040000,
WS_GROUP = 0x00020000,
WS_TABSTOP = 0x00010000,
WS_MINIMIZEBOX = 0x00020000,
WS_MAXIMIZEBOX = 0x00010000,
WS_TILED = 0x00000000,
WS_ICONIC = 0x20000000,
WS_SIZEBOX = 0x00040000,
WS_POPUPWINDOW = 0x80880000,
WS_OVERLAPPEDWINDOW = 0x00CF0000,
WS_TILEDWINDOW = 0x00CF0000,
WS_CHILDWINDOW = 0x40000000
}
#endregion

#region Window Extended Styles
public enum WindowExStyles
{
WS_EX_DLGMODALFRAME = 0x00000001,
WS_EX_NOPARENTNOTIFY = 0x00000004,
WS_EX_TOPMOST = 0x00000008,
WS_EX_ACCEPTFILES = 0x00000010,
WS_EX_TRANSPARENT = 0x00000020,
WS_EX_MDICHILD = 0x00000040,
WS_EX_TOOLWINDOW = 0x00000080,
WS_EX_WINDOWEDGE = 0x00000100,
WS_EX_CLIENTEDGE = 0x00000200,
WS_EX_CONTEXTHELP = 0x00000400,
WS_EX_RIGHT = 0x00001000,
WS_EX_LEFT = 0x00000000,
WS_EX_RTLREADING = 0x00002000,
WS_EX_LTRREADING = 0x00000000,
WS_EX_LEFTSCROLLBAR = 0x00004000,
WS_EX_RIGHTSCROLLBAR = 0x00000000,
WS_EX_CONTROLPARENT = 0x00010000,
WS_EX_STATICEDGE = 0x00020000,
WS_EX_APPWINDOW = 0x00040000,
WS_EX_OVERLAPPEDWINDOW = 0x00000300,
WS_EX_PALETTEWINDOW = 0x00000188,
WS_EX_LAYERED = 0x00080000
}
#endregion

#region ChildFromPointFlags
public enum ChildFromPointFlags
{
CWP_ALL = 0x0000,
CWP_SKIPINVISIBLE = 0x0001,
CWP_SKIPDISABLED = 0x0002,
CWP_SKIPTRANSPARENT = 0x0004
}
#endregion

#region HitTest
public enum HitTest
{
HTERROR = (-2),
HTTRANSPARENT = (-1),
HTNOWHERE = 0,
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTGROWBOX = 4,
HTSIZE = HTGROWBOX,
HTMENU = 5,
HTHSCROLL = 6,
HTVSCROLL = 7,
HTMINBUTTON = 8,
HTMAXBUTTON = 9,
HTLEFT = 10,
HTRIGHT = 11,
HTTOP = 12,
HTTOPLEFT = 13,
HTTOPRIGHT = 14,
HTBOTTOM = 15,
HTBOTTOMLEFT = 16,
HTBOTTOMRIGHT = 17,
HTBORDER = 18,
HTREDUCE = HTMINBUTTON,
HTZOOM = HTMAXBUTTON,
HTSIZEFIRST = HTLEFT,
HTSIZELAST = HTBOTTOMRIGHT,
HTOBJECT = 19,
HTCLOSE = 20,
HTHELP = 21
}
#endregion

#region Windows Messages
public enum Msg
{
WM_NULL = 0x0000,
WM_CREATE = 0x0001,
WM_DESTROY = 0x0002,
WM_MOVE = 0x0003,
WM_SIZE = 0x0005,
WM_ACTIVATE = 0x0006,
WM_SETFOCUS = 0x0007,
WM_KILLFOCUS = 0x0008,
WM_ENABLE = 0x000A,
WM_SETREDRAW = 0x000B,
WM_SETTEXT = 0x000C,
WM_GETTEXT = 0x000D,
WM_GETTEXTLENGTH = 0x000E,
WM_PAINT = 0x000F,
WM_CLOSE = 0x0010,
WM_QUERYENDSESSION = 0x0011,
WM_QUIT = 0x0012,
WM_QUERYOPEN = 0x0013,
WM_ERASEBKGND = 0x0014,
WM_SYSCOLORCHANGE = 0x0015,
WM_ENDSESSION = 0x0016,
WM_SHOWWINDOW = 0x0018,
WM_CTLCOLOR = 0x0019,
WM_WININICHANGE = 0x001A,
WM_SETTINGCHANGE = 0x001A,
WM_DEVMODECHANGE = 0x001B,
WM_ACTIVATEAPP = 0x001C,
WM_FONTCHANGE = 0x001D,
WM_TIMECHANGE = 0x001E,
WM_CANCELMODE = 0x001F,
WM_SETCURSOR = 0x0020,
WM_MOUSEACTIVATE = 0x0021,
WM_CHILDACTIVATE = 0x0022,
WM_QUEUESYNC = 0x0023,
WM_GETMINMAXINFO = 0x0024,
WM_PAINTICON = 0x0026,
WM_ICONERASEBKGND = 0x0027,
WM_NEXTDLGCTL = 0x0028,
WM_SPOOLERSTATUS = 0x002A,
WM_DRAWITEM = 0x002B,
WM_MEASUREITEM = 0x002C,
WM_DELETEITEM = 0x002D,
WM_VKEYTOITEM = 0x002E,
WM_CHARTOITEM = 0x002F,
WM_SETFONT = 0x0030,
WM_GETFONT = 0x0031,
WM_SETHOTKEY = 0x0032,
WM_GETHOTKEY = 0x0033,
WM_QUERYDRAGICON = 0x0037,
WM_COMPAREITEM = 0x0039,
WM_GETOBJECT = 0x003D,
WM_COMPACTING = 0x0041,
WM_COMMNOTIFY = 0x0044,
WM_WINDOWPOSCHANGING = 0x0046,
WM_WINDOWPOSCHANGED = 0x0047,
WM_POWER = 0x0048,
WM_COPYDATA = 0x004A,
WM_CANCELJOURNAL = 0x004B,
WM_NOTIFY = 0x004E,
WM_INPUTLANGCHANGEREQUEST = 0x0050,
WM_INPUTLANGCHANGE = 0x0051,
WM_TCARD = 0x0052,
WM_HELP = 0x0053,
WM_USERCHANGED = 0x0054,
WM_NOTIFYFORMAT = 0x0055,
WM_CONTEXTMENU = 0x007B,
WM_STYLECHANGING = 0x007C,
WM_STYLECHANGED = 0x007D,
WM_DISPLAYCHANGE = 0x007E,
WM_GETICON = 0x007F,
WM_SETICON = 0x0080,
WM_NCCREATE = 0x0081,
WM_NCDESTROY = 0x0082,
WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084,
WM_NCPAINT = 0x0085,
WM_NCACTIVATE = 0x0086,
WM_GETDLGCODE = 0x0087,
WM_SYNCPAINT = 0x0088,
WM_NCMOUSEMOVE = 0x00A0,
WM_NCLBUTTONDOWN = 0x00A1,
WM_NCLBUTTONUP = 0x00A2,
WM_NCLBUTTONDBLCLK = 0x00A3,
WM_NCRBUTTONDOWN = 0x00A4,
WM_NCRBUTTONUP = 0x00A5,
WM_NCRBUTTONDBLCLK = 0x00A6,
WM_NCMBUTTONDOWN = 0x00A7,
WM_NCMBUTTONUP = 0x00A8,
WM_NCMBUTTONDBLCLK = 0x00A9,
WM_NCXBUTTONDOWN = 0x00AB,
WM_NCXBUTTONUP = 0x00AC,
WM_NCXBUTTONDBLCLK = 0x00AD,
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_CHAR = 0x0102,
WM_DEADCHAR = 0x0103,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105,
WM_SYSCHAR = 0x0106,
WM_SYSDEADCHAR = 0x0107,
WM_KEYLAST = 0x0108,
WM_IME_STARTCOMPOSITION = 0x010D,
WM_IME_ENDCOMPOSITION = 0x010E,
WM_IME_COMPOSITION = 0x010F,
WM_IME_KEYLAST = 0x010F,
WM_INITDIALOG = 0x0110,
WM_COMMAND = 0x0111,
WM_SYSCOMMAND = 0x0112,
WM_TIMER = 0x0113,
WM_HSCROLL = 0x0114,
WM_VSCROLL = 0x0115,
WM_INITMENU = 0x0116,
WM_INITMENUPOPUP = 0x0117,
WM_MENUSELECT = 0x011F,
WM_MENUCHAR = 0x0120,
WM_ENTERIDLE = 0x0121,
WM_MENURBUTTONUP = 0x0122,
WM_MENUDRAG = 0x0123,
WM_MENUGETOBJECT = 0x0124,
WM_UNINITMENUPOPUP = 0x0125,
WM_MENUCOMMAND = 0x0126,
WM_CTLCOLORMSGBOX = 0x0132,
WM_CTLCOLOREDIT = 0x0133,
WM_CTLCOLORLISTBOX = 0x0134,
WM_CTLCOLORBTN = 0x0135,
WM_CTLCOLORDLG = 0x0136,
WM_CTLCOLORSCROLLBAR = 0x0137,
WM_CTLCOLORSTATIC = 0x0138,
WM_MOUSEMOVE = 0x0200,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_LBUTTONDBLCLK = 0x0203,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_RBUTTONDBLCLK = 0x0206,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MBUTTONDBLCLK = 0x0209,
WM_MOUSEWHEEL = 0x020A,
WM_XBUTTONDOWN = 0x020B,
WM_XBUTTONUP = 0x020C,
WM_XBUTTONDBLCLK = 0x020D,
WM_PARENTNOTIFY = 0x0210,
WM_ENTERMENULOOP = 0x0211,
WM_EXITMENULOOP = 0x0212,
WM_NEXTMENU = 0x0213,
WM_SIZING = 0x0214,
WM_CAPTURECHANGED = 0x0215,
WM_MOVING = 0x0216,
WM_DEVICECHANGE = 0x0219,
WM_MDICREATE = 0x0220,
WM_MDIDESTROY = 0x0221,
WM_MDIACTIVATE = 0x0222,
WM_MDIRESTORE = 0x0223,
WM_MDINEXT = 0x0224,
WM_MDIMAXIMIZE = 0x0225,
WM_MDITILE = 0x0226,
WM_MDICASCADE = 0x0227,
WM_MDIICONARRANGE = 0x0228,
WM_MDIGETACTIVE = 0x0229,
WM_MDISETMENU = 0x0230,
WM_ENTERSIZEMOVE = 0x0231,
WM_EXITSIZEMOVE = 0x0232,
WM_DROPFILES = 0x0233,
WM_MDIREFRESHMENU = 0x0234,
WM_IME_SETCONTEXT = 0x0281,
WM_IME_NOTIFY = 0x0282,
WM_IME_CONTROL = 0x0283,
WM_IME_COMPOSITIONFULL = 0x0284,
WM_IME_SELECT = 0x0285,
WM_IME_CHAR = 0x0286,
WM_IME_REQUEST = 0x0288,
WM_IME_KEYDOWN = 0x0290,
WM_IME_KEYUP = 0x0291,
WM_MOUSEHOVER = 0x02A1,
WM_MOUSELEAVE = 0x02A3,
WM_CUT = 0x0300,
WM_COPY = 0x0301,
WM_PASTE = 0x0302,
WM_CLEAR = 0x0303,
WM_UNDO = 0x0304,
WM_RENDERFORMAT = 0x0305,
WM_RENDERALLFORMATS = 0x0306,
WM_DESTROYCLIPBOARD = 0x0307,
WM_DRAWCLIPBOARD = 0x0308,
WM_PAINTCLIPBOARD = 0x0309,
WM_VSCROLLCLIPBOARD = 0x030A,
WM_SIZECLIPBOARD = 0x030B,
WM_ASKCBFORMATNAME = 0x030C,
WM_CHANGECBCHAIN = 0x030D,
WM_HSCROLLCLIPBOARD = 0x030E,
WM_QUERYNEWPALETTE = 0x030F,
WM_PALETTEISCHANGING = 0x0310,
WM_PALETTECHANGED = 0x0311,
WM_HOTKEY = 0x0312,
WM_PRINT = 0x0317,
WM_PRINTCLIENT = 0x0318,
WM_THEME_CHANGED = 0x031A,
WM_HANDHELDFIRST = 0x0358,
WM_HANDHELDLAST = 0x035F,
WM_AFXFIRST = 0x0360,
WM_AFXLAST = 0x037F,
WM_PENWINFIRST = 0x0380,
WM_PENWINLAST = 0x038F,
WM_APP = 0x8000,
WM_USER = 0x0400,
WM_REFLECT = WM_USER + 0x1c00
}
#endregion

#region SetWindowPosFlags
public enum SetWindowPosFlags
{
SWP_NOSIZE = 0x0001,
SWP_NOMOVE = 0x0002,
SWP_NOZORDER = 0x0004,
SWP_NOREDRAW = 0x0008,
SWP_NOACTIVATE = 0x0010,
SWP_FRAMECHANGED = 0x0020,
SWP_SHOWWINDOW = 0x0040,
SWP_HIDEWINDOW = 0x0080,
SWP_NOCOPYBITS = 0x0100,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_DRAWFRAME = 0x0020,
SWP_NOREPOSITION = 0x0200,
SWP_DEFERERASE = 0x2000,
SWP_ASYNCWINDOWPOS = 0x4000
}
#endregion
}

#region WINDOWINFO
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWINFO
{
public UInt32 cbSize;
public RECT rcWindow;
public RECT rcClient;
public UInt32 dwStyle;
public UInt32 dwExStyle;
public UInt32 dwWindowStatus;
public UInt32 cxWindowBorders;
public UInt32 cyWindowBorders;
public UInt16 atomWindowType;
public UInt16 wCreatorVersion;
}
#endregion

#region POINT
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;

#region Constructors
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}

public POINT(Point point)
{
x = point.X;
y = point.Y;
}
#endregion
}
#endregion

#region RECT
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public uint left;
public uint top;
public uint right;
public uint bottom;

#region Properties
public POINT Location
{
get { return new POINT((int)left, (int)top); }
set
{
right -= (left - (uint)value.x);
bottom -= (bottom - (uint)value.y);
left = (uint)value.x;
top = (uint)value.y;
}
}

public uint Width
{
get { return right - left; }
set { right = left + value; }
}

public uint Height
{
get { return bottom - top; }
set { bottom = top + value; }
}
#endregion

#region Overrides
public override string ToString()
{
return left + ":" + top + ":" + right + ":" + bottom;
}
#endregion
}
#endregion

#region WINDOWPOS
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;

#region Overrides
public override string ToString()
{
return x + ":" + y + ":" + cx + ":" + cy + ":" + ((SWP_Flags)flags).ToString();
}
#endregion
}
#endregion

#region NCCALCSIZE_PARAMS
[StructLayout(LayoutKind.Sequential)]
public struct NCCALCSIZE_PARAMS
{
public RECT rgrc1;
public RECT rgrc2;
public RECT rgrc3;
public IntPtr lppos;
}
#endregion

#region NMHDR
[StructLayout(LayoutKind.Sequential)]
public struct NMHDR
{
public IntPtr hwndFrom;
public uint idFrom;
public uint code;
}
#endregion

#region OFNOTIFY
[StructLayout(LayoutKind.Sequential)]
public struct OFNOTIFY
{
public NMHDR hdr;
public IntPtr OPENFILENAME;
public IntPtr fileNameShareViolation;
}
#endregion

補充一下
1.代碼只提供思路,不能拿來繼承一下,就能實現自己想要的功能。

2.可以自己將代碼中DialogNativeWindow類的addControl替換為其他控件,比如PictureBox用來預覽圖片、TextBox用來預覽txt文件、RichTextBox用來預覽代碼文件等等,還可自由組合。

3.可以自己將代碼中DialogNativeWindow類的兩個事件(SelectedFileChanged、SelectPathChanged)移到MultiOpenFileDialog中,並使其對外開放,外部程序可以監聽該事件。

4.如果想做成通用類,拿過來繼承一下就可以實現自己想要的效果,建議使MultiOpenFileDialog從UserControl,並將addControl設為自己。也就是說將自己添加到OpenFileDialog中去,MultiOpenFileDialog的派生類就可以在VS中設計自己想要的效果。

5.一個窗體新建顯示時,它的擁有者會接收許多消息,包括WM_ACTIVATE、WM_IDLE等等,並且Lparam參數為新建窗體的句柄。
代碼如下:
 
class Form1:Form
{
private void Form1_Load(Object sender,EventArgs e)
{
using(OpenFileDialog dia = new OpenFileDialog())
{
dia.ShowDialog(this);
}
}
protected override void WndProc(ref Message m)
{
//當dia顯示時,它的擁有者即為this,這裡會接受一連串的Window消息,並且它的Lparam參數為dia的句柄
base.WndProc(ref m);
}
}

6.Windows中窗體和所謂的控件(button、textbox)本質上沒有區別,任務欄與QQ聊天框或者Chrome浏覽器的地址欄對我們程序員來講,是同一個東西。

7.與窗體有關的Win32 API基本都需要窗體句柄,其實任何一個API幾乎都需要知道操作對象的句柄(不一定是窗體)。

8.Windows中任何一個窗體(控件)理論上都是平級的,不管是否同一進程,也就是說,我的winform應用程序只要知道了Chrome浏覽器窗體的句柄,就可以控制Chrome浏覽器,監聽Chrome窗體的Windows消息(除非Chrome程序本身禁止了此操作)。

9.Windows桌面應用程序開發中,(部分平台、語言)理解四個東西,即進程、線程 、窗體(已經說了,是廣義上的窗體)、消息。

10.查看系統中以上四個東西,可以使用Spy++工具。

完了,剩下那個下次再寫了,太多了。希望有幫助~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved