python winform开发_c#教程之.Net WInform开发笔记(三)谈谈自制控件(自定

末日这天写篇博客吧,既然没来,那就纪念一下。这次谈谈自制控件,也就是自定义控件,先上图,再说1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能2.重写ListBox,增加折叠、鼠标背影、分类等功能-----------------------------分割线-----------------------------...
摘要由CSDN通过智能技术生成

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

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

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

15a977696ee2e3f3724cbe580aa20520.png

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

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

一、扩展OpenFileDialog

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

389c702d4e547b02300dd2fbc12758f3.png

提到扩展,很多人可能想到继承它就可以扩展它,可惜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开发笔记(二)中基本提到了一些。为了配合本次讲解,我再次画了一张图

eec154203be164241bdf7cf2492b2bd7.png

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值