2006年11月16日

在C++类中实现Windows窗口的创建

一个很简单的在类中实现窗口的创建的例子. 编译测试通过环境: EVC4.0 + WINCE4.2

//========================================================================
//TITLE:
//    在C++类中实现Windows窗口的创建
//AUTHOR:
//    norains
//DATE:
//    Thursday  9-November-2006
//========================================================================
  在面向过程的方法中实现窗口的创建很简单,但有个非常明显的缺点,就是封装不好。如果是自写自用,倒不是一个很大的问题,但如果是写给用户的,可能用户在包含头文件之后,看到那一大堆函数以及变量声明就已经晕掉。最好的方法当然是使用类,只给使用者留出应该使用的接口。但这会有个问题,就是消息处理函数必须为static的CALLBACK,否则编译会出错;但如果消息处理函数为static,其就归属于类,就根本无法调用对象的成员函数,而这个在经典的《windows 程序设计》中也没有涉及到。
  难道我们就只能束手无策,只能采用面向过程的方法了么?那倒未必,毕竟在MFC框架中已经做到,那么相信我们也可以做到。经过一番摸索,终于找到在类中创建窗口的方法,在此不敢独享,放出代码和大家探讨。
  
  为便于理解,代码的作用很简单,仅仅是完成了类的声明和对话框的创建,唯一多余的是在接收到WM_INITDIALOG消息时弹出一个消息框,以证明private函数能够正常调用。
  代码首先是声明一个CMainDlg类:
  ///////////////////////////
  //MainDlg.h
  ///////////////////////////
  class CMainDlg 
  {
   
   //--------------------------------------------------------------------------------------------   
   public:
    //静态的回调函数
    static BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
   
   //-------------------------------------------------------------------------------------------
   //Function
   public:
    //构造函数
    CMainDlg(HINSTANCE hInst);
    //析构函数
    virtual ~CMainDlg();
    //创建对话框
    BOOL CreateMainDlg();
    //消息调用函数,具体意义请参见函数的实现
    BOOL CallDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
   
   private:
    //临时测试调用函数,仅作为调试用
    void TempTestFuncion(){MessageBox(NULL,L"test",L"",NULL);};
   
   //---------------------------------------------------------------------------------------------
   //数据成员
   private:
    HINSTANCE m_hInst;
    HWND m_hDlg;
   
  };
  
  
  
  然后是MainDlg类的实现:
  /////////////////////////////////
  //MainDlg.cpp
  ////////////////////////////////
                                                                                     
    #include "stdafx.h"                                                                
    #include "MainDlg.h"                                                               
    #include "resource.h"                                                              
                                                                                   
    //全局指针,用来指向所创建的对象,即this的指向对象                                                              
    CMainDlg *g_pDlg = NULL;                         
                 
    //消息的回调函数的实现                                                  
    BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {                                                                                  
     if(g_pDlg != NULL)                                                               
     {
      //如果this指针不为空,则调用CallDlgProc函数                                                                               
      return g_pDlg->CallDlgProc(hWnd,wMsg,wParam,lParam);                           
     }                                                                                
     return FALSE;                                                                    
    }                                                                                  
   
    //构造函数                                                                               
    CMainDlg::CMainDlg(HINSTANCE hInst)                                                
    {                                                                                  
     g_pDlg = this; //存储this对象到全局变量中                                                                   
     m_hInst = hInst;                                                                 
    }                                                                                  
   
    //析构函数                                                                               
    CMainDlg::~CMainDlg()                                                              
    {                                                                                  
                                                                                   
    }                                                                                  
       
    //创建对话框                                                                           
    BOOL CMainDlg::CreateMainDlg()                                                     
    {                                                                                  
     m_hDlg = CreateDialog(m_hInst,MAKEINTRESOURCE(IDD_MAIN_DLG),NULL,MainDlgProc);   
     if(m_hDlg == NULL)                                                               
     {                                                                                
      return FALSE;                                                                  
     }                                                                                
     ShowWindow(m_hDlg,TRUE);//显示窗口                                                         
                                                                                      
     return TRUE;                                                                     
    }                                                                                  
        
    //真正的消息处理函数,在这里可以任意调用对象的成员函数                                                                          
    BOOL CMainDlg::CallDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)        
    {                                                                                  
     switch(wMsg)                                                                     
     {                                                                                
      case WM_INITDIALOG:                                                            
       TempTestFuncion(); //norains:测试的私有函数                                      
       break;                                                                       
     }                                                                                
     return FALSE;                                                                    
    }                          
   
   
   
    这是在主程序中对类的的调用:   
    //////////////////
    //MainApp.cpp
    //////////////////
    #include "stdafx.h"
    #include "MainDlg.h"
  
  int WINAPI WinMain( HINSTANCE hInstance,
       HINSTANCE hPrevInstance,
       LPTSTR    lpCmdLine,
       int       nCmdShow)
  {
   //声明一个对象 
   CMainDlg mainDlg(hInstance);
   //创建并显示窗口
   if(mainDlg.CreateMainDlg() == FALSE)
   {
    return 0x05;
   }
   
   //消息循环
   MSG msg;
   while(GetMessage(&msg,NULL,0,0))
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
   return 0;
  }
  
  这段代码的关键点在于MainDlgProc声明为类的static函数,以及使用g_pDlg来存储this对象指针,并在MainDlgProc中调用。只要注意到这两点,在类中创建一个窗口就不是一件非常困难的事情。
  
  对于CallDlgProc()函数,可能有不少人觉得是鸡肋,因为相同的功能完全可以在MainDlgProc()中实现,比如这程序的代码完全可以撇开CallDlgProc()写成这样:
  
  BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {                                                                                  
     if(g_pDlg != NULL)                                                               
     {
     switch(wMsg)                                                                     
      {                                                                                
       case WM_INITDIALOG:                                                            
        g_pDlg->TempTestFuncion();                                       
        break;                                                                       
      }                                 
     }                                                                                
     return FALSE;                                                                    
    }               
    所完成的功能和之前的代码相同。不过如果要更改之后的代码正常工作的话,还需要把TempTestFuncion()函数声明为public。也许有的读者已经发现问题之所在:如果对消息相应的处理函数不多,这样倒是可以;但如果很多的话,那么那些消息相应函数都需要声明为public,并且这些消息响应函数都是用户不可能用到的!用户看到如此之多用不到的public函数,即使没头晕,也需要好一阵子才能回神过来。
    所以呢,CallDlgProc()函数只是起到一个缓冲作用,不必要把所有的消息相应函数都暴露给用户。虽然这样做也暴露了CallDlgProc()函数,但毕竟相对于上十个甚至上百个消息响应函数而言,仅仅只是暴露一个没什么作用的CallDlgProc()函数函数,应该能让用户减轻不少负担才是。               
   
    在网上看到有一个方法不是采用全局变量,而是直接设置窗口数据,然后再获取。这方法比较有意思,在此将更改的代码贴出:           
    BOOL CMainDlg::CreateMainDlg()                                                     
    {                                                                                  
     m_hDlg = CreateDialog(m_hInst,MAKEINTRESOURCE(IDD_MAIN_DLG),NULL,MainDlgProc);   
     if(m_hDlg == NULL)                                                               
     {                                                                                
      return FALSE;                                                                  
     }                                                                                
     ShowWindow(m_hDlg,TRUE);//显示窗口                                                      
     SetWindowLong(m_hDlg,GWL_USERDATA,(LONG)this); //将this指针传递给窗口
                                                                                  
     return TRUE;                                                                     
    }   
   
    BOOL CALLBACK CMainDlg::MainDlgProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
    {   
     static CMainDlg * pDlg = NULL;
   if(pDlg == NULL)
   {
    pDlg = (CMainDlg *)GetWindowLong(hWnd,GWL_USERDATA);
   } 
                                                                              
     if(pDlg != NULL)                                                               
     {
     switch(wMsg)                                                                     
      {                                                                                
       case WM_INITDIALOG:                                                            
        pDlg->TempTestFuncion();                                       
        break;                                                                       
      }                                 
     }                                                                                
     return FALSE;                                                                    
    }  
    这段代码关键就只在于两个函数:SetWindowLong()和GetWindowLong().很巧妙地让窗口通过这两个函数来传输this对象,的确是一个很好的方法.

 

posted @ 2006-11-16 14:48 holym 阅读(452) 评论(0) 编辑

2006年4月19日

自己写的一个JSP上传文件和下载文件的JavaBean

天看了一些关于JSP,Servlet方面的资料,写了简单的两个JavaBean。一个是UpLoad,一个是DownLoad。写得很简单,没有使用其它任何组件,自己做的。大家可以来看看。

1.RunningUpLoader上传Bean

  首先是RunningUpLoader.java,代码有些多,因为我是自己来解析输入流的。

package testupload;
import java.io.*;
import javax.servlet.http.HttpServletRequest;


public class RunningUpLoader {
    public RunningUpLoader() {
    }

    /**
     * 上传文件
     * @param request HttpServletRequest Servlet的请求对象
     * @param name String 要上传的文件在表单中的参数名
     * @param fileName String 保存在服务器上的文件名
     * @throws IOException
     */
    public void doUpload(HttpServletRequest request, String name,String fileName) throws
            IOException {
        InputStream in = request.getInputStream();
        byte[] buffer = getFileContent(name,in);
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(buffer,0,buffer.length-1);
        fos.close();
    }

    /**
     * 获取上传的文件buffer,结尾处将多一个换行符
     * @param name String 文件上传参数的名字
     * @param is InputStream 服务器对话的输入流
     * @return byte[] 返回获取的文件的buffer,结尾处多一个换行符
     * @throws IOException
     */
    private byte[] getFileContent(String name, InputStream is) throws IOException{
        int index;
        boolean isEnd = false;
        byte[] lineSeparatorByte;
        byte[] lineData;
        String content_disposition;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BufferedInputStream bis = new BufferedInputStream(is);

        lineSeparatorByte = readStreamLine(bis);
        while(!isEnd) {
            lineData = readStreamLine(bis);
            if(lineData == null) {
                break;
            }
            content_disposition = new String(lineData,"ASCII");
            index = content_disposition.indexOf("name=\"" + name + "\"");
            if (index >= 0 && index < content_disposition.length()) {
                System.out.println(readStreamLineAsString(bis)); // skip a line
                System.out.println(readStreamLineAsString(bis)); // skip a line

                while ((lineData = readStreamLine(bis)) != null) {
                    System.out.println(new String(lineData));
                    if (isByteArraystartWith(lineData, lineSeparatorByte)) { // end
                        isEnd = true;
                        break;
                    } else {
                        bos.write(lineData);
                    }
                }
            }else {
                lineData = readStreamLine(bis);
                if(lineData == null)
                    return null;
                System.out.println(new String(lineData,"ASCII"));
                while(!isByteArraystartWith(lineData, lineSeparatorByte)) {
                    lineData = readStreamLine(bis);
                    if(lineData == null)
                        return null;
                    System.out.println(new String(lineData,"ASCII"));
                }
            }
        }
        return bos.toByteArray();
    }

    private byte[] readStreamLine(BufferedInputStream in) throws IOException{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int b = in.read();
        if(b== -1)
            return null;
        while(b != -1 && b != '\n') {
            bos.write(b);
            b = in.read();
        }
        return bos.toByteArray();
    }

    private String readStreamLineAsString(BufferedInputStream in) throws IOException {
        return new String(readStreamLine(in),"ASCII");
    }

    private boolean isByteArraystartWith(byte[] arr,byte[] pat) {
        int i;
        if(arr == null || pat == null)
            return false;
        if(arr.length < pat.length)
            return false;
        for(i=0;i<pat.length;i++) {
            if(arr[i] != pat[i])
                return false;
        }
        return true;
    }
}

上传文件文件的时候,需要在之前的html中增加一个form表单,里面需要有个<input type="file" name="fileUpload" >的输入按钮。这样,浏览器才会将要上传的文件递交给服务器。 其中name="fileUpload"的名字,就是RunnningUpLoader.doUpload函数中的name参数。

使用的时候直接用bean就OK了。比如upload.jsp如下:

<jsp:useBean id="upBean" scope="page" class="testupload.RunningUpLoader" />

<%

upBean.doUpload(request,"fileupload","runningUpLoad.txt");

%>

 

2. RunningDownLoader下载Bean

package testupload;
import java.io.*;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;

public class RunningDownLoader {
    public RunningDownLoader() {
    }

    public void doDownload(HttpServletResponse response, String filePath,String fileName) throws
            IOException {
        response.reset();//可以加也可以不加
     response.setContentType("application/x-download");//设置为下载application/x-download
     System.out.println(this.getClass().getClassLoader().getResource("/").getPath());
     String filenamedownload = filePath;
     String filenamedisplay = fileName;//系统解决方案.txt
     filenamedisplay = URLEncoder.encode(filenamedisplay,"UTF-8");
     response.addHeader("Content-Disposition","attachment;filename=" + filenamedisplay);

     OutputStream output = null;
     FileInputStream fis = null;
     try
     {
         output  = response.getOutputStream();
         fis = new FileInputStream(filenamedownload);

         byte[] b = new byte[1024];
         int i = 0;

         while((i = fis.read(b)) > 0)
         {
             output.write(b, 0, i);
         }
         output.flush();
     }
     catch(Exception e)
     {
         System.out.println("Error!");
         e.printStackTrace();
     }
     finally
     {
         if(fis != null)
         {
             fis.close();
             fis = null;
         }
         if(output != null)
         {
             output.close();
             output = null;
         }
     }

    }

 

}
 

  下载文件,最好是做到Servlet上。直接在Servlet的doGet下面增加如下代码即可

//Process the HTTP Get request
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws
            ServletException, IOException {
        RunningDownLoader downloader= new RunningDownLoader();
        downloader.doDownload(response,"E:\\MyProjects\\testupload.rar","upload.bin");
    }

posted @ 2006-04-19 11:26 holym 阅读(1530) 评论(2) 编辑

Java2下Applet数字签名具体实现方法

 

老文章,用到了就转贴一下,方便记忆。

 

我的项目是使用APPLET制作一个实时消息队列监控程序,由于涉及到了本地资源,对APPLET一定要进行数字签名和认证。我使用的环境是WINDOWS2000,应用服务器是WEBLOGIC6.0,开发环境是JBUILDER4.0。之前我提醒大家一定要注意服务器端和客户端的概念。那些文件应该在服务器端,那些文件应该在客户端。

  首先在客户端使用JRE1.3.0_01(JAVA运行环境1.3.0.1版本)以取代IE的JVM(JAVA虚拟机),可以到WWW.JAVA.SUN.COM网站上去下载,下载好了先在客户端安装好,安装过程非常简单。

  在服务器端的调用APPLET的HTML文件中也需要将它包含进来,以便没有事先安装JRE的客户端下载,具体的写法,请接着往下看;

  具体步骤如下:

  服务器端:

  1.将程序需要用到的各种包文件全部解压(我这儿要用到WEBLOGIC的JMS包使用命令jar xf weblogicc.jar),然后使用JDK的打包命令将编译好的监控程序.class和刚才解压的包一起打包到一个包中。(前提我已经将监控程序和解开的包都放在同一个目录下了),都是dos状态下的命令,具体命令见jdk1.3(1.2)的bin目录下,

  命令如下:

jar cvf monitor.jar *.class

  此命令生成一个名为monitor.jar的包

  2.为刚才创建的包文件(monitor.jar)创建keystore和keys。其中,keystore将用来存放密匙(private keys)和公共钥匙的认证,alias别名这儿取为monitor。

  命令如下:

keytool -genkey -keystore monitor.keystore –alias monitor

  此命令生成了一个名为monitor.keystore的keystore文件,接着这条命令,系统会问你好多问题,比如你的公司名称,你的地址,你要设定的密码等等,都由自己的随便写。

  3.使用刚才生成的钥匙来对jar文件进行签名

  命令如下:

jarsigner -keystore monitor.keystore monitor.jar monitor

  这个命令将对monitor.jar文件进行签名,不会生成新文件。

  4.将公共钥匙导入到一个cer文件中,这个cer文件就是要拷贝到客户端的唯一文件 。

  命令如下:

keytool -export -keystore monitor.keystore -alias monitor -file monitor.cer

  此条命令将生成monitor.cer认证文件,当然这几步都有可能问你刚才设置的密码。这样就完成了服务器端的设置。这时你就可以将jar文件和keystore文件以及cer文件(我这儿是monitor.jar,monitor.keystore,monitor.cer)拷贝到服务器的目录下了,我用的是weblogic6.0,所以就拷贝到C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver下的自己建的一个目录下了。

  客户端:

  1. 首先应该安装jre1.3.0_01,然后将服务器端生成的monitor.cer 文件拷贝到jre的特定目录下,我这儿是:
c:\program files\javasoft\jre\1.3.0_01\lib\security目录下。

  2. 将公共钥匙倒入到jre的cacerts(这是jre的默认keystore)

  命令如下:

keytool -import -alias monitor -file monitor.cer
-keystore cacerts

  注意这儿要你输入的是cacerts的密码,应该是changeit,而不是你自己设定的keystore的密码。

  3. 修改policy策略文件,在dos状态下使用命令 policytool

  系统会自动弹出一个policytool的对话框,如图4所示,在这里面首先选择file菜单的open项,打开c:\program files\javasoft\jre\1.3.0_01\lib\security目录下的java.poliy文件,然后在edit菜单中选择Change keystore ,在对话框中new keystore url:中输入
file:/c:/program files /javasoft/jre/1.3.0_01/lib/security/cacerts, 这儿要注意反斜杠,在new keystore type 中输入JKS,这是cacerts的固定格式,然后单击Add Policy Entry,在出现的对话框中CodeBase中输入:

http://URL:7001/*

  其中的URL是服务器的IP地址,7001是我的weblogic的端口,如果你是在别的应用服务器上比如说是apache,那端口号就可以省略掉。

  在SignedBy中输入(别名alias):这儿是Monitor然后单击add peimission按钮,在出现的对话框中permission中选择你想给这个applet的权限,这儿具体有许多权限,读者可以自己找资料看看。我这儿就选用allpeimission,右边的signedBy中输入别名:monitor
最后保存,在file菜单的save项。

  当然你可以看见我已经对多个包实现了签名认证。

  这样客户端的设置就完成了。在客户端用ie运行该applet程序时,会询问你是不是对该签名授权,选择授权后,包会自动从服务器下载到本地计算机,而且ie会自动启动jre,在右下栏中可以看见,相当于ie的java控制台。

  4.调用applet的html文件  

  大家都知道由于java2的安全性,对applet的正常调用的html文件已经不能再使用了,而改为ActiveX类型的调用。具体的又分ie和nescape的不同写法,这一些在sun网上都能找到现成的教程。我就不多说了,只是将我的这个小程序为ie写的的html给大家看看。

<html>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=gb2312">
<center>
<h3>消息中心实时监控平台</h3>
<hr>
<OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
width="900" height="520" align="baseline" codebase="http://192.168.2.217:7001/j2re-1_3_0_01-win-i.exe#Version=1,3,0,0">
<PARAM NAME="java_code" VALUE="wise.monitor.applet.monitorApplet">
<PARAM NAME="java_codebase" VALUE="monitor/classes">
<PARAM NAME="java_type" VALUE="application/x-java-applet;version=1.3">
<PARAM NAME="ARCHIVE" VALUE="monitor.jar" >
<PARAM NAME="scriptable" VALUE="true">
</OBJECT>
</center>
</html>

  其中我要强调一点,因为applet每一次的改动都需要重新打包签名,手续非常繁琐,所以在具体的实现中要将一些会变化参数放到html文件中来,传到applet中去,这一点网上文章好多,自己去看吧。

  另外一个就是有朋友问我,那这样不是太麻烦了,每一个客户端都要进行复杂的dos命令操作,我只能说一目前我的水平只能将一个已经做好的客户端文件cer文件和java.policy以及cacerts文件直接拷贝到客户端,当然这也有缺陷,如果别人的计算机已经有了认证,就会丢失。就这些问题我们可以一起探讨。

  另外还有一点优化,就是在打包的时候,我这儿只讲了把所有要用的涉及到安全性的包和源程序到要打到一个包中。这样如果包非常大的话,会非常影响下载的速度,如果可以使用本地计算机的包就好了,这一点jre也做到了,具体的要到控制面板的jre控制台上去设置。这个就留着读者自己去摸索吧。

posted @ 2006-04-19 11:25 holym 阅读(97) 评论(0) 编辑

2006年3月28日

Transact SQL 常用语句以及函数

摘要: Transact SQL语句功能========================================================================  --数据操作  SELECT--从数据库表中检索数据行和列      INSERT--向数据库表添加新数据行      DELETE--从数据库表中删除数据行      UPDATE--更新数据库表中的数据  --数据定...阅读全文

posted @ 2006-03-28 15:31 holym 阅读(774) 评论(0) 编辑

如何在MDI程序中把子窗体菜单合并到主窗体上

这里只讲在MDI中,子菜单与父菜单合并的简单方法。
首先在MDI的主窗体中:

1. 在主窗体构造函数中,添加如下代码,注意要在InitializeComponent调用之后,如:

            //Add File Menu

            MenuItem miFile = mnuMain.MenuItems.Add("&File");

            miFile.MergeOrder=1;

            miFile.MergeType = MenuMerge.MergeItems;

 

            MenuItem miAddDoc = new MenuItem( "&Add Document",

                new EventHandler(this.FileAdd_Clicked),

                Shortcut.CtrlA );

            miAddDoc.MergeOrder=100;

 

            MenuItem miExit = new MenuItem( "E&xit",

                new EventHandler(this.FileExit_Clicked),

                Shortcut.CtrlX );

            miExit.MergeOrder=110;

 

            miFile.MenuItems.Add(miAddDoc);

            miFile.MenuItems.Add("-");     // Give a seperator

            miFile.MenuItems.Add(miExit);

 

            //Add Window Menu

            MenuItem miWindow = mnuMain.MenuItems.Add("&Window");

            miWindow.MergeOrder = 10;

            miWindow.MenuItems.Add( "&Cascade",

                new EventHandler(this.WindowCascade_Clicked));

            miWindow.MenuItems.Add( "Tile &Horizontal",

                new EventHandler(this.WindowTileH_Clicked));

            miWindow.MenuItems.Add( "Tile &Vertical",

                new EventHandler(this.WindowTileV_Clicked));

            miWindow.MdiList = true ; //Adds the MDI Window List to the bottom of the menu

 

2. 然后为相应的Menu_Click事件定义函数,如:

        #region MENU_CLICK_MESSAGE_HANDLE

        //File->Add Menu item handler

        protected void FileAdd_Clicked(object sender, System.EventArgs e)

        {

            AddMDIChild() ; //Create new MDI child window

        }

 

        //File->Exit Menu item handler

        protected void FileExit_Clicked(object sender, System.EventArgs e)

        {

            this.Close();

        }

 

        //Window->Cascade Menu item handler

        protected void WindowCascade_Clicked(object sender, System.EventArgs e)

        {

            this.LayoutMdi(MdiLayout.Cascade);

        }

 

        //Window->Tile Horizontally Menu item handler

        protected void WindowTileH_Clicked(object sender, System.EventArgs e)

        {

            this.LayoutMdi(MdiLayout.TileHorizontal);

        }

 

        //Window->Tile Vertically Menu item handler

        protected void WindowTileV_Clicked(object sender, System.EventArgs e)

        {

            this.LayoutMdi(MdiLayout.TileVertical);

        }

        #endregion {MENU_CLICK_MESSAGE_HANDLE}

 

3. 创建子窗体,如:

        //Add a new child window if it doesnt exist; else set focus on it

        private void AddMDIChild()

        {

            // MyChildForm is the one I'm looking for

            MDIChild myMDIChild = null;

            foreach(Form f in this.MdiChildren)

            {

                if(f is MDIChild)

                {

                    // found it

                    myMDIChild = (MDIChild) f;

                    break;

                }

            }

 

            if( myMDIChild != null)

            {

                myMDIChild.Show();

                myMDIChild.Focus();

            }

            else

            {

                myMDIChild = new MDIChild();

                myMDIChild.MdiParent = this;

                myMDIChild.Show();

                myMDIChild.Focus();

            }

        } 

       然后在子窗体中,具体操作如下:

1. 在子窗体构造函数中,添加如下代码,同样要在InitializeComponent调用之后,如:

            //Add File Menu

            MenuItem miFile = mnuChildMain.MenuItems.Add("&File");

            miFile.MergeType = MenuMerge.MergeItems;

            miFile.MergeOrder = 1;

 

            MenuItem miLoadDoc = miFile.MenuItems.Add( "&Load Document",

                new EventHandler(this.LoadDocument_Clicked) );

            miLoadDoc.MergeOrder = 105;

 

            //Add Formatting Menu

            MenuItem miFormat = mnuChildMain.MenuItems.Add("F&ormat");

            miFormat.MergeType = MenuMerge.Add;

            miFormat.MergeOrder = 5;

 

            //Font Face sub-menu

            miSansSerif = new MenuItem("&1." + sansSerifFontFamily.Name,

                new EventHandler(this.FormatFont_Clicked));

            miSerif = new MenuItem("&2." + serifFontFamily.Name,

                new EventHandler(this.FormatFont_Clicked));

            miMonoSpace = new MenuItem("&3." + monoSpaceFontFamily.Name,

                new EventHandler(this.FormatFont_Clicked));

            miSansSerif.Checked = true ;

            miFormatFontChecked = miSansSerif ;

            miSansSerif.DefaultItem = true ;

 

            miFormat.MenuItems.Add( "Font &Face"

                , (new MenuItem[]{ miSansSerif, miSerif, miMonoSpace })

                );

 

            //Font Size sub-menu

            miSmall = new MenuItem("&Small", new EventHandler(this.FormatSize_Clicked));

            miMedium = new MenuItem("&Medium", new EventHandler(this.FormatSize_Clicked));

            miLarge = new MenuItem("&Large", new EventHandler(this.FormatSize_Clicked));

            miMedium.Checked = true ;

            miMedium.DefaultItem = true ;

            miFormatSizeChecked = miMedium ;

 

            miFormat.MenuItems.Add( "Font &Size"

                , (new MenuItem[]{ miSmall, miMedium, miLarge })

                );

 

2. 然后为相应的Menu_Click事件定义函数,如:

        #region MENU_CLICK_MESSAGE_HANDLE

        //File->Load Document Menu item handler

        protected void LoadDocument_Clicked(object sender, System.EventArgs e)

        {

            MessageBox.Show(this.Text) ;

        }

 

        //Format->Font Menu item handler

        protected void FormatFont_Clicked(object sender, System.EventArgs e)

        {

            MenuItem miClicked = (MenuItem)sender;

            miClicked.Checked = true;

            miFormatFontChecked.Checked = false;

            miFormatFontChecked = miClicked ;

 

            if (miClicked == miSansSerif)

            {

                currentFontFamily = sansSerifFontFamily ;

            }

            else if (miClicked == miSerif)

            {

                currentFontFamily = serifFontFamily ;

            }

            else

            {

                currentFontFamily = monoSpaceFontFamily ;

            }

 

            rtbDoc.Font = new Font(currentFontFamily, fontSize);

        }

 

        //Format->Size Menu item handler

        protected void FormatSize_Clicked(object sender, System.EventArgs e)

        {

            MenuItem miClicked = (MenuItem)sender;

            miClicked.Checked = true;

            miFormatSizeChecked.Checked = false;

            miFormatSizeChecked = miClicked;

 

            if (miClicked == miSmall)

            {

                fontSize = FontSizes.Small ;

            }

            else if (miClicked == miLarge)

            {

                fontSize = FontSizes.Large ;

            }

            else

            {

                fontSize = FontSizes.Medium ;

            }

 

            rtbDoc.Font = new Font(currentFontFamily, fontSize);

        }

        #endregion {MENU_CLICK_MESSAGE_HANDLE}

 

3. 窗体的私有成员:

        private struct FontSizes

        {

            public static float Small = 8f;

            public static float Medium = 12f;

            public static float Large = 24f;

        }

 

        private float fontSize = FontSizes.Medium;

 

        private MenuItem miFormatFontChecked;

        private MenuItem miFormatSizeChecked;

 

        private MenuItem miSmall ;

        private MenuItem miMedium ;

        private MenuItem miLarge ;

 

        private MenuItem miSansSerif ;

        private MenuItem miSerif ;

        private MenuItem miMonoSpace ;

 

        private FontFamily currentFontFamily ;

        private FontFamily monoSpaceFontFamily;

        private FontFamily sansSerifFontFamily;

        private System.Windows.Forms.MainMenu mnuChildMain;

        private FontFamily serifFontFamily;

 

       这样,子窗体的代码也完成了。

其实在做MDI子窗体上的菜单要和主窗体上的菜单合并,主要是设置每个菜单的MergeOrder,对于每个菜单的MergeOrder设置不同的值,从而达到合并后的效果。以下用表格说明:

主窗体MergeOrder

子窗体MergeOrder

File(1)

Window(10)

File(1)

Format(5)

AddDocument( 100)

 

Load Document(105)

 

Exit(110)

 

 

 

                     注:红色表示每个菜单的MergeOrder

合并后的效果如下:

合并后的菜单

File(1)

Format(5)

Window(10)

AddDocument( 100)

 

 

Load Document(105)

 

 

Exit(110)

 

 

                     注:红色表示每个菜单的MergeOrder

 

所以如果想在MDI子窗体上的菜单和主窗体上的菜单合并后,达到所预期的效果,就必须好好涉及它们之间的MergeOrder,大致如下:

1.             如果和主窗体原有的菜单列进行合并,首先把根菜单的MergeOrder设置与主窗体相应菜单一样的MergeOrder值,同时设置MenuItemMergeTypeMenuMerge.MergeItems,合并后在此列菜单中的位置,就看每个菜单的MergeOrder了。

2.             如果想在主窗体添加新的菜单列,MenuItemMergeTypeMenuMerge. Add,菜单列插入的位置,要看其MergeOrder了。

posted @ 2006-03-28 10:11 holym 阅读(1016) 评论(0) 编辑

导航

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

公告

昵称:holym
园龄:5年10个月
粉丝:0
关注:0

搜索

 
 

常用链接

随笔档案

文章分类

网友

最新评论