手机
当前位置:查字典教程网 >编程开发 >C#教程 >.Net WInform开发笔记(三)谈谈自制控件(自定义控件)
.Net WInform开发笔记(三)谈谈自制控件(自定义控件)
摘要:末日这天写篇博客吧,既然没来,那就纪念一下。这次谈谈自制控件,也就是自定义控件,先上图,再说1.扩展OpenFileDialog,在Open...

末日这天写篇博客吧,既然没来,那就纪念一下。

这次谈谈自制控件,也就是自定义控件,先上图,再说

1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能

.Net WInform开发笔记(三)谈谈自制控件(自定义控件)1

2.重写ListBox,增加折叠、鼠标背影、分类等功能

-----------------------------分割线--------------------------------------------------------------

一、扩展OpenFileDialog

许多软件的打开对话框都有预览功能,最常见的就是图片预览,用鼠标选择一个图片文件后,右侧或者下侧就会有该图片的缩略图(photoshop中属于后者)。在winform编程中,有专门的打开文件对话框的类OpenFileDialog,但是他不提供文件预览功能,封装得实在太好。看看它公开那些接口

.Net WInform开发笔记(三)谈谈自制控件(自定义控件)2

提到扩展,很多人可能想到继承它就可以扩展它,可惜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 WInform开发笔记(三)谈谈自制控件(自定义控件)3

上图中,虚线框可以看做是一个.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++工具。

完了,剩下那个下次再写了,太多了。希望有帮助~

【.Net WInform开发笔记(三)谈谈自制控件(自定义控件)】相关文章:

.Net WInform开发笔记(二)Winform程序运行结构图及TCP协议在Winform中的应用

DirectoryInfo引用一个相对目录的实例

使用VS2010 C#开发ActiveX控件(上)

C#.NET学习笔记5 C#中的条件编译

c#中合并DataTable重复行的值

.Net WInform开发笔记(五)关于事件Event

深入c# GDI+简单绘图的具体操作步骤(一)

C#中将字符串转换为整型的三种解决方法总结

C# 实现阶乘 (递归,非递归) 实现代码

在Winform动态启动、控制台命令行的方法

精品推荐
分类导航