欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-27 08:55:52
末日这天写篇博客吧,既然没来,那就纪念一下。 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展openfiledialog,在openfiledialog中添...

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

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

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

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

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

-----------------------------分割线--------------------------------------------------------------
一、扩展openfiledialog

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

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

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

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

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