.Net WInform开发笔记(三)谈谈自制控件(自定义控件)
末日这天写篇博客吧,既然没来,那就纪念一下。 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能 2.重写ListBox,增加折叠、鼠标背影、分类等功能 -----------------------------分割线-------------------------------------------------------------- 许多软件的打开对话框都有预览功能,最常见的就是图片预览,用鼠标选择一个图片文件后,右侧或者下侧就会有该图片的缩略图(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,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,(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,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,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",int msg,int wParam,int lParam); [DllImport("user32.dll",CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd,StringBuilder param); [DllImport("user32.dll",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,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_NOREDRAW = 0x0008, SWP_SHOWWINDOW = 0x0040, SWP_NOCOPYBITS = 0x0100, 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++工具。 完了,剩下那个下次再写了,太多了。希望有帮助~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |