• C# Winform 界面开发[转]


    界面开发概述

    每个软件都要有自己的软件界面,对于软件开发来说,软件界面不一定是最重要的,但是也是相当重要的。一款软件要是可以在界面上做好,吸引了客户的眼球,那这款软件也就相对成功了一半。

    现在各行各业的软件都添加了自己的皮肤色彩,显示出了不同的特点。例如QQ,MSN,Foxmail等等,这些软件都修改了自己软件的界面,将自己的界面化做的很完善,很漂亮。使用起来感觉很舒服。我也是一个做软件的,于是也打算将自己的软件做的像那些软件产品一样漂亮,于是乎在网上寻求自己软件界面的解决方案。当然很多很多。下面列举几个:

    一、使用皮肤组件(IrisSkin2.dll)

    这个是东日软件公司开发的一套软件界面。IrisSkin 是为Microsoft Visual Studio.NET开发的最易用的界面增强.NET(WinForm)组件包。它能完全自动的为您的应用程序添加支持换肤功能,甚至不需要更改您的设计好的Form以及添加一行代码!您也不再需要花费很多时间来使得自己的应用程序更漂亮。当然他是收费的,但是网上很多具有破解版,当然我也下载了一套,使用起来相当的方便。

    下载地址:Irisskin.rar

    二、自定义开发

    这个就比较难了,要了解很多关于Windows开发的内容,其中有两种做法,一种就是使用自定义的皮肤Form和Button等所有的空间,继承.net Framework提供的控件中自己使用的大部分,然后扩展其中的WndProc方法或者其他的一些方法,这是市最普遍的一种方式。另外就是使用IrisSkin的方法,使用NativeWindow这个底层的类,进行Hook编程。主要的例子如下:

    1、苏飞的博客:

    http://www.cnblogs.com/sufei/archive/2010/03/13/1685236.html

    2、CsharpWin中的皮肤

    http://www.csharpwin.com/csharpresource/2992.shtml

    这两个写的都非常好,让我受益匪浅。

    总结了以上的皮肤开发,我了解的到,所谓的皮肤开发无非就是使用了图片、颜色等内容将窗体进行了重绘或者就是覆盖,也就是重载了WndProc中的Paint,NCPaint等等操作。对于Winform来说,一个窗体主要分为两块,即Non Client Area和Client Area,如下图:

    简单的说就是对Client Area和Non Client Area的绘制。而Form中的WndProc的Paint,NCPaint就是完成这样的操作。

    3、我的实现方式。

    我的实现方式主要研究了苏飞和CshaorpWindow的两个程序,还有就是查看了IrisSkin的反编译源代码,我觉得实现的方式还是使用IrisSkin的方式比较好,IrisSkin的实现是使用了本地的一个简单的类NativeWindow,这是是一个可以实现简单的窗体等的注册,也就是Hook。我按照了这种方法,写了一个简单的程序。基本上可以实现IrisSkin的内容,使用的方式和IrisSkin的使用方式一样,都是在窗体中添加一个SkinEngine的Component,这样窗体就会显示了Office2007的样式,如下图所示:

    现在主要实现了在窗体上的Label可以显示为皮肤的颜色,但是对于对话框中的颜色还没有进行处理。还需要很多的处理,包括皮肤的设计和窗体中各个控件的设计。我会将在后续专题中写相关的内容。

    源码如下:SkinEngines2010-03-17.rar

    (一)--- Hook所有的窗体

    上篇文章界面开发概述,讲了我开发界面的一些心得体会和一些基本的原理,从这篇开始,我开始讲解一下我的界面开发的全部过程,一步一步的讲解开发界面的过程,这篇主要讲解的是Hook编程,Hook所有的窗体。

    Hook,对于大多数程序员来说,这个词并不陌生。对于Windows系统来说,消息Message的传递贯穿了整个系统,Message简单来说就是一个整数,它具有相应的意义。在C++的winuser.h中可以看到我们常用的很多的Message。Hook与Message是密不可分的,它的中文解释就是“钩子”,就是监控系统中Message的传递,就是在Message传递到最终的Message处理前,对特定的消息进行处理。

    对于Hook来说,开发主要的有3个API函数,均放在User32.dll文件中,这三个函数是:

    Hook
    1
    ///<summary>
    2 /// SetWindowsHookEx
    3 ///</summary>
    4 ///<param name="idHook"></param>
    5 ///<param name="lpfn"></param>
    6 ///<param name="hMod"></param>
    7 ///<param name="dwThreadId"></param>
    8 ///<returns></returns>
    9 [DllImport("User32.dll", CharSet = CharSet.Auto)]
    10 publicstaticextern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, int hMod, int dwThreadId);
    11
    12 ///<summary>
    13 /// CallNextHookEx
    14 ///</summary>
    15 ///<param name="hhk"></param>
    16 ///<param name="nCode"></param>
    17 ///<param name="wParam"></param>
    18 ///<param name="lParam"></param>
    19 ///<returns></returns>
    20 [DllImport("User32.dll", CharSet = CharSet.Auto)]
    21 publicstaticextern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    22
    23 ///<summary>
    24 /// UnhookWindowsHookEx
    25 ///</summary>
    26 ///<param name="hhk"></param>
    27 ///<returns></returns>
    28 [DllImport("User32.dll", CharSet = CharSet.Auto)]
    29 publicstaticexternbool UnhookWindowsHookEx(IntPtr hhk);

    这三个方法分别就是添加Hook,释放Hook和执行下一个Hook。其中的参数就不必介绍了,网上有很多这方面的资料。

    接下来就是对Windows窗体的消息截取。

    我创建一个项目名称叫做SkinEngines,然后再NativeMethod中添加了这三个方法。其中SetWindowsHookEx中需要一个参数就是一个方面的名称,在C#中使用委托实现,所以也创建了一个专门保存委托的地点,放在Delegates.cs文件中。

    HookProc
    ///<summary>
    /// HookProc -- HookProc
    ///</summary>
    ///<param name="nCode"></param>
    ///<param name="wParam"></param>
    ///<param name="lParam"></param>
    ///<returns></returns>
    publicdelegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    然后创建一个名为SkinEngine的Component。在SkinEngine的构造函数中添加了自己的一些操作,对窗体的Hook。代码如下:

    1 ///<summary>
    2 /// SkinEngine -- Skin All Form,Dialog,Control
    3 ///</summary>
    4 publicpartialclass SkinEngine : Component
    5 {
    6 #region Field
    7 ///<summary>
    8 /// CBTHook -- Hook WH_CBT
    9 ///</summary>
    10 privatestatic HookProc _cbtHook;
    11
    12 ///<summary>
    13 /// Hook
    14 ///</summary>
    15 privatestatic IntPtr Hook;
    16
    17 ///<summary>
    18 /// Current SkinEngine
    19 ///</summary>
    20 internalstatic SkinEngine Engine;
    21
    22 ///<summary>
    23 /// Skinned handled
    24 ///</summary>
    25 internalstatic ArrayList SkinHandleList = new ArrayList();
    26 #endregion
    27
    28 #region Constructor
    29 ///<summary>
    30 /// Constructor
    31 ///</summary>
    32 public SkinEngine()
    33 {
    34 InitializeComponent();
    35 // Internal Constructor
    36 this.InternalConstructor();
    37 }
    38
    39 ///<summary>
    40 /// Constructor With container
    41 ///</summary>
    42 ///<param name="container"></param>
    43 public SkinEngine(IContainer container)
    44 {
    45 container.Add(this);
    46
    47 InitializeComponent();
    48
    49 // Internal Constructor
    50 this.InternalConstructor();
    51 }
    52 #endregion
    53
    54 #region InternalConstructor
    55 ///<summary>
    56 /// the Internal Constructor to Create Hook and Get List of all control to skin.
    57 ///</summary>
    58 privatevoid InternalConstructor()
    59 {
    60 // IsDesignMode == false
    61 if (!IsDesignMode)
    62 {
    63 // Check Engine
    64 if (Engine == null)
    65 {
    66 // Set Engine
    67 Engine = this;
    68
    69 // Hook Process
    70 if (Hook == IntPtr.Zero)
    71 {
    72 _cbtHook = new HookProc(SkinEngine.FnHookProc);
    73 Hook = NativeMethod.SetWindowsHookEx(5, _cbtHook, 0, AppDomain.GetCurrentThreadId());
    74 Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
    75 }
    76 }
    77 }
    78 }
    79 #endregion
    80
    81 #region FnHookProc
    82 ///<summary>
    83 /// FnHookProc
    84 ///</summary>
    85 ///<param name="nCode"></param>
    86 ///<param name="wParam"></param>
    87 ///<param name="lParam"></param>
    88 ///<returns></returns>
    89 privatestaticunsafe IntPtr FnHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    90 {
    91 if (Engine != null)
    92 {
    93 switch (nCode)
    94 {
    95 case5:
    96 // Get Skin Control
    97 Control control = Control.FromHandle(wParam);
    98 // Control is null it Can be Dialog
    99 if (control == null)
    100 {
    101 StringBuilder builder = new StringBuilder(260);
    102 NativeMethod.GetClassName(wParam, builder, 260);
    103 // #32770 is Dialog
    104 if (builder.Length == 6 && builder.ToString() == "#32770")
    105 {
    106 // Add to SkinHandleList
    107 SkinHandleList.Add(wParam);
    108 // Print
    109 Debug.WriteLine(builder.ToString());
    110 }
    111 break;
    112 }
    113 if (!SkinHandleList.Contains(wParam) && (control is Form))
    114 {
    115 // Add to SkinHandleList
    116 SkinHandleList.Add(wParam);
    117 // Print all control's Name
    118 Debug.WriteLine(control.Name);
    119 }
    120 break;
    121 default:
    122 break;
    123 }
    124 }
    125 return NativeMethod.CallNextHookEx(Hook, nCode, wParam, lParam);
    126 }
    127 #endregion
    128
    129 #region Application_ApplicationExit
    130 ///<summary>
    131 /// Application_ApplicationExit
    132 ///</summary>
    133 ///<param name="sender"></param>
    134 ///<param name="e"></param>
    135 privatevoid Application_ApplicationExit(object sender, EventArgs e)
    136 {
    137 Engine.Dispose(false);
    138 }
    139 #endregion
    140
    141 #region Property
    142 ///<summary>
    143 /// Gets or sets a value indicating whether we are currently in design mode.
    144 ///</summary>
    145 ///<value>
    146 ///<c>true</c> if this we are in design mode; otherwise, <c>false</c>.
    147 ///</value>
    148 internalstaticbool IsDesignMode { get; set; }
    149 #endregion
    150 }

    这样整个窗体就可以进行Hook,其中NativeMethod.SetWindowsHookEx设置了WM_CBT的消息,这个消息用来对Windows窗体的激活,显示。这样,窗体就会被我们记录下来,现在仅仅是将记录下来的窗体显示出来,写出了窗体的名称。结果如下:

    这篇就暂时写道这里,下一篇将对Form的消息进行Hook并绘制。

    代码:SkinEngines20100318.rar

    (二)--- NativeWindow

    NativeWindow是.net Framework提供的一个底层的类。微软官方的解释为:NativeWindow Provides a low-level encapsulation of a window handle and a window procedure.说道这些也相当于没有说,因为NativeWindow到底是干什么的也没有说清楚,意思就是一个低级别的对窗体句柄和窗体过程的一个封装。后来查询了很多的资料才明白了其中的意义。

    资料来源:

    http://www.diybl.com/course/4_webprogram/asp.net/netjs/2007921/72804.html

    这个讲解的是《利用NativeWindow监视WndProc消息》,这篇内容比较长,而且页数比较多,经过自己努力和耐心,终于完成了其中的例子,代码下载地址为:NativeWindowApplication.rar

    http://www.codeproject.com/KB/dialog/OpenFileDialogEx.aspx

    这篇来自CodeProject,老外写的代码就是比较有水平。将Windows 的OpenFileDialog子类化,显示成了自定义的OpenFileDialog,而且添加了很多的事件。相当的佩服。

    经过这两篇的细心阅读,终于了解了NativeWindow的作用,NativeWindow就是提供了底层的封装,提供了AssignHandle和RealeseHandle这两个方法,主要就是对注册了窗体句柄的窗体进行Hook,将窗体的事件经过自己处理后交给Windows处理。使用Refector.net查看了NativeWindow中AssignHandle这个方法,也验证了我这个说法。在AssignHandle中有这样一段代码:

    userDefWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null,UnsafeNativeMethods.GetModuleHandle("user32.dll")), lpProcName);

    对Win32开发的也许都知道这段代码的意义,他也是Hook的一种。不过是更加高级的Hook方式。这里就不在细说,我们现在只要知道他可以对我们进行注册的窗体进行Hook,可以将自己的代码加载到我们想要的位置就可以了。

    然后我就使用NativeWindow获取所有的窗体的所有事件,然后输出Hook到的时间,结果图如下:

    下载代码如下:SkinEngines20100319.rar

    来源:http://www.uml.org.cn/jmshj/201004074.asp

  • 相关阅读:
    Python学习笔记(10):异常
    SharePoint中RichTextBox的Required验证
    Python学习笔记(6):模块
    Python学习笔记(4):控制流
    Python学习笔记(5):函数
    解决SharePoint中GridView导出Excel按钮的问题
    Python学习笔记(8):面向对象
    如何在SharePoint中创建Custom Master Page
    main cannot be resolved or is not a field
    c# 格式化输出字符串
  • 原文地址:https://www.cnblogs.com/saptechnique/p/2293403.html
Copyright © 2020-2023  润新知