<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      VB調用C#寫的WinForm.NET控件

      點擊下載本文配套的演示程序代碼https://files.cnblogs.com/xdesigner/VB-CS-WinformControl.zip

      一.前言:

        雖然IT開發技術日新月異,不過業界仍然運行著大量的VB系統,這些系統凝聚了不少客戶的投資,應當要一定程度的保護和利用。因此也就產生了一種需求,也就是使用舊的開發技術仍然可以使用新技術的產出。本文就討論如何在VB6.0開發中使用上WinForm.NET控件。[袁永福版權所有]

       

      二.軟件原理:

        運行VB IDE,打開或創建一個EXE工程,打開窗體設計器,如下圖所示: 

        為了能在窗體上添加控件,需要往窗體左邊的工具箱上添加項目,需要點擊菜單項目“Project-Components”,此時會彈出如下圖所示的對話框: 

         點擊“Browse”按鈕,彈出文件選擇對話框,這個對話框中優先選擇OCX文件,而C#編譯結果絕不可能是OCX文件的,此時即使選擇一個.NET程序集DLL文件,無論如何必然會報錯“This file not registerable as an ActiveX Component”。[袁永福版權所有]

        因此也就是說,使用C#開發的WinForm.NET控件是不可能直接通過傳統的模式放置在VB窗體上。

        不過VB仍然可以通過COM方式調用.NET程序集中的對COM公開的類型。此時就可以想出一種曲線實現方式,那就是VB創建C#組件,該組件是一個WinForm.NET控件,然后調用Win32API SetParent函數,將WinForm.NET控件硬塞入VB窗體中。這樣在用戶界面上,用戶能看到和使用WinForm.NET控件;在后臺,VB代碼能訪問.NET組件提供的公開的屬性、方法和事件,實現了VB全方位的調用WinForm.NET控件。 

      三.C#開發

      C#控件開發

        根據上述的軟件原理,筆者開發一個WinForm.NET控件并成功的應用于VB6.0的開發中,現對軟件進行說明。

        這個WinForm.NET控件名為MyWinFormControl,派生自System.Windows.Forms.UserControl類型,它包含在一個名為DCWinFormControlLib的C#項目中,項目輸出類型為類庫,目標框架為.NET2.0,添加了對System.Windows.Forms.dll的引用。

      界面設計:

      MyWinFormControl控件的用戶界面設計如下: 

        在界面上放置一個名為“btnAction”的按鈕,一個名為“myTextBox”的文本框。

      定義公開屬性和方法:

        打開該控件的C#代碼文件,可以看到聲明該類型的C#代碼如下:

      [System.Runtime.InteropServices.ComVisible(true)]
      [System.Runtime.InteropServices.Guid("60550064-C97F-4306-A8B2-6908F50780E3")]
      [System.Runtime.InteropServices.ComSourceInterfaces(typeof(IComMyEvent))]
      public partial class MyWinFormControl : UserControl
      {
      }

        這段代碼中,第一行代碼的ComVisible標記類型為COM公開的;第二行代碼Guid標記了類型在COM中的唯一編號;第三行代碼的ComSourceInterfaces指明該類型實現了名為IComMyEvent的事件接口。[袁永福版權所有]

        VB中無法直接綁定編譯階段未知的控件事件,同時也無法直接感應C#中的事件,為此需要編寫一個接口通知VB存在若干事件,使得VB能綁定事件。因此在此定義了IComMyEvent接口,聲明了C#控件中的事件,IComMyEvent接口定義如下

      using System.Runtime.InteropServices;
      
      [Guid("096EF9A6-24CB-4091-A18F-34DA38C9A6F1")]
      [ComVisible( true )]
      [InterfaceType( ComInterfaceType.InterfaceIsIDispatch )]
      public interface IComMyEvent
      {
          /// <summary>
          /// 按鈕按下事件
          /// </summary>
          [DispId(12340)]
          void ComButtonClick();
          /// <summary>
          /// 文本內容修改事件t
          /// </summary>
          [DispId(12350)]
          void ComTextChanged();
      }
      
      
      /// <summary>
      /// 無參數無返回值委托類型
      /// </summary>
      public delegate void VoidEventHandler();

      而后在控件的C#代碼中添加以下代碼:

      #region 實現 IComMyEvent 中的成員
      /// <summary>
      /// 按鈕按下事件
      /// </summary>
      public event VoidEventHandler ComButtonClick = null;
      /// <summary>
      /// 文本內容修改事件
      /// </summary>
      public event VoidEventHandler ComTextChanged = null;
      
      #endregion

        這樣C#中定義的事件在VB中就能綁定了,以下代碼就是觸發這些事件的:

      private void btnAction_Click(object sender, EventArgs e)
      {
          if (ComButtonClick != null)
          {
              // 觸發ComButtonClick事件
              ComButtonClick();
          }
      }
      
      private void myTextBox_TextChanged(object sender, EventArgs e)
      {
          if (ComTextChanged != null)
          {
              // 觸發ComTextChanged事件
              ComTextChanged();
          }
      }

        對控件實現了COM公開的事件后,就可以編寫COM公開的屬性和方法,其代碼如下:

      /// <summary>
      /// 公開的屬性
      /// </summary>
      public string UserText
      {
          get
          {
              return myTextBox.Text;
          }
          set
          {
              myTextBox.Text = value;
          }
      }
      
      /// <summary>
      /// 公開的方法
      /// </summary>
      public double Calcute(double p)
      {
          return Math.Sin(p);
      }

        這個用戶控件雖然能在VB代碼中創建和訪問,但還不能直接拖放到VB窗體上,此時還需要使用代碼將C#控件添加到VB窗體上:

      /// <summary>
      /// 將控件添加到指定句柄的窗體中
      /// </summary>
      /// <param name="containerHandle">指定的窗體句柄對象</param>
      /// <returns>操作是否成功</returns>
      public bool AppendToContainerControl(int containerHandle)
      {
          CrossPlatformControlHostManager man = new CrossPlatformControlHostManager();
          man.ContainerHandle = new IntPtr(containerHandle);
          man.ControlHandle = this.Handle;
          man.Dock = this.Dock;
          return man.UpdateLayout();
      }

        在這個函數中,參數為VB窗體中某個控件的句柄,該控件用于承載C#控件。這段代碼使用了筆者編寫的一個CrossPlatformControlHostManager類型,該類型專業用于執行跨應用程序的控件承載,實現“乾坤大挪移”,該類型首先定義了幾個屬性:[袁永福版權所有]

      private IntPtr _ControlHandle = IntPtr.Zero;
      /// <summary>
      /// 操作的控件句柄對象
      /// </summary>
      public IntPtr ControlHandle
      {
          get{ return _ControlHandle; }
          set{ _ControlHandle = value; }
      }
      
      private IntPtr _ContainerHandle = IntPtr.Zero;
      /// <summary>
      /// 容器元素對象
      /// </summary>
      public IntPtr ContainerHandle
      {
          get{ return _ContainerHandle; }
          set{ _ContainerHandle = value; }
      }
      
      private DockStyle _Dock = DockStyle.Fill;
      /// <summary>
      /// 停靠樣式
      /// </summary>
      public DockStyle Dock
      {
          get{ return _Dock; }
          set{ _Dock = value; }
      }

       

      此外還定義了一個方法,其代碼如下:

      /// <summary>
      /// 更新排版
      /// </summary>
      /// <returns>操作是否成功</returns>
      public bool UpdateLayout()
      {
          WindowInformation info = new WindowInformation(this.ControlHandle);
          WindowInformation container = new WindowInformation(this.ContainerHandle);
          if (info.CheckHandle() == false
              || container.CheckHandle() == false)
          {
              return false;
          }
          if (info.ParentHandle != container.Handle)
          {
              if (info.SetParent(container.Handle) == false)
              {
                  return false;
              }
          }
          Rectangle clientRect = container.ClientBounds;
          Rectangle bounds = info.Bounds;
          Rectangle descBounds = bounds;
          switch (this.Dock)
          {
              case DockStyle.Fill:
                  descBounds = clientRect;
                  break;
              case DockStyle.Bottom:
                  descBounds = new Rectangle(
                      0,
                      clientRect.Height - bounds.Height, 
                      clientRect.Width,
                      bounds.Height);
                  break;
              case DockStyle.Left:
                  descBounds = new Rectangle(
                      0,
                      0,
                      bounds.Width, 
                      clientRect.Height);
                  break;
              case DockStyle.Right:
                  descBounds = new Rectangle(
                      clientRect.Width - bounds.Width,
                      0,
                      bounds.Width,
                      clientRect.Height);
                  break;
              case DockStyle.Top:
                  descBounds = new Rectangle(
                      0,
                      0,
                      clientRect.Width,
                      bounds.Height);
                  break;
          }
          if (descBounds != bounds)
          {
              info.Bounds = descBounds;
          }
          return true;
      }

        這段代碼中,用到了一個WindowInformation的類型,這個筆者編寫的另外一個類型,實現了一些窗體相關的Win32API的封裝。

        在UpdateLayout中,首先判斷雙方的窗體句柄是否有效,然后判斷兩個窗體控件的父子關系,若不是則設置控件的父子關系,底層調用的是Win32API函數SetParent。[袁永福版權所有]

        然后根據對象的Dock屬性值和父控件的客戶區大小來計算出子控件應有的位置和大小,然后設置子控件的位置和大小。

        這樣這個控件本身開發完畢了。

      設置程序集

      另外還需要對C#項目進行一些設置來完成公開COM開發接口的工作。

      打開工程文件中的AssemblyInfo.cs文件,該文件默認在Properties目錄下,設置其代碼內容為:

      using System.Reflection;
      using System.Runtime.CompilerServices;
      using System.Runtime.InteropServices;
      
      // 有關程序集的常規信息通過以下
      // 特性集控制。更改這些特性值可修改
      // 與程序集關聯的信息。
      [assembly: AssemblyTitle("袁永福的COM測試")]
      [assembly: AssemblyDescription("")]
      [assembly: AssemblyConfiguration("")]
      [assembly: AssemblyCompany("")]
      [assembly: AssemblyProduct("DCWinFormControlLib")]
      [assembly: AssemblyCopyright("Copyright ?  2012")]
      [assembly: AssemblyTrademark("")]
      [assembly: AssemblyCulture("")]
      
      // 將 ComVisible 設置為 false 使此程序集中的類型
      // 對 COM 組件不可見。如果需要從 COM 訪問此程序集中的類型,
      // 則將該類型上的 ComVisible 特性設置為 true。
      [assembly: ComVisible(true)]
      
      // 如果此項目向 COM 公開,則下列 GUID 用于類型庫的 ID
      [assembly: Guid("c241537f-dfe8-4502-b43a-967f9437ee7c")]
      [assembly: AssemblyVersion("1.0.0.*")]

        這段代碼中,重點之一是“ComVisible”,這是程序集允許向COM公開;還有一個“Guid”,這是為類型庫提供一個編號;還有“AssemblyVersion”設置程序集的版本號,這里有一個*表示這個版本號是累計的,[袁永福版權所有]每次編譯生成的程序集的版本號都會增加的。

      強名稱

      要提供COM開發接口,.NET程序集必須是強名稱的。

      要對.NET程序集強名稱,首先是要有一個SNK簽名文件,若沒有,則可以使用.NET SDK中的SN.EXE工具生成一個SNK文件。

      打開VS.NET自帶的.NET SDK命令行界面,執行命令行“SN.EXE –k d:\dctest.snk”,即可生成一個強名稱使用的SNK文件。則命令行輸出文本為:

      Microsoft(R) .NET Framework 強名稱實用工具 版本 4.0.30319.1
      版權所有(C) Microsoft Corporation。保留所有權利。

      密鑰對寫入到 d:\dctest.snk

       

      在VS.NET中打開項目的屬性頁面,切換到“簽名”頁面,如下圖所示:

       

      勾選“為程序集簽名”,然后在強名稱秘鑰文件下列列表中點擊“瀏覽”項目,選擇SNK文件。然后重新編譯,即可生成強名稱的.NET程序集DLL文件了。

      注冊和生成類型庫文件

      當成功的編譯生成.NET程序集DLL文件后,需要對該程序集進行注冊和生成COM開發使用的類型庫文件。[袁永福版權所有]

      在VS.NET命令行環境,執行命令“regasm  編譯輸出目錄\DCWinFormControlLib.dll /tlb /codebase”,則該命令行輸出的內容如下:

      Microsoft(R) .NET Framework 程序集注冊實用工具 4.0.30319.1
      版權所有(C) Microsoft Corporation 1998-2004。保留所有權利。

      成功注冊了類型
      成功注冊了導出到“編譯輸出目錄\dcwinformcontrollib.tlb”的程序集和類型庫

      特別要注意,對于Win7或更高系統中,必須以管理員的模式啟動VS.NET命令行環境,否則程序會爆無權限的錯誤。

       

      四.VB程序開發

        當成功的執行完上述操作后,我們就已經生成了可用于VB開發的.NET程序組件了。

         筆者使用VB6.0開發了一個程序能應用MyWinFormControl控件,程序已經寫好,現對其進行說明。[袁永福版權所有]

        首先該VB工程名為DCVBExe,它是 一個普通的EXE的VB工程。在VB6.0 IDE中點擊菜單“工程-引用”,顯示如下圖所示的添加引用對話框:

       

      在該對話框中就可以看到電腦系統中已經有了DCWinFormControlLib的引用了,VB工程就包含了該引用。

      本VB工程的主窗體設計如下:

       

      這個VB窗體中放置了一個名為“cmdSetText”的按鈕控件,還有一個名為“picContainer”的PictureBox控件。

      該窗體的VB代碼如下:

      Option Explicit
      
      ' 定義控件變量
      Private WithEvents myWinFormControl As DCWinFormControlLib.myWinFormControl
      
      ' VB按鈕點擊事件處理
      Private Sub cmdSetText_Click()
          myWinFormControl.UserText = "袁永福到此一游"
      End Sub
      
      ' 窗體加載事件
      Private Sub Form_Load()
          Set myWinFormControl = New DCWinFormControlLib.myWinFormControl
          myWinFormControl.Dock = 4
          myWinFormControl.AppendToContainerControl Me.picContainer.hWnd
      End Sub
      
      ' 窗體大小改變事件
      Private Sub Form_Resize()
          If Me.ScaleWidth > 3000 And Me.ScaleHeight > 3000 Then
              Me.picContainer.Width = Me.ScaleWidth - Me.picContainer.Left - 50
              Me.picContainer.Height = Me.ScaleHeight - Me.picContainer.Top - 50
              If Not myWinFormControl Is Nothing Then
                  myWinFormControl.AppendToContainerControl Me.picContainer.hWnd
              End If
          End If
      End Sub
      
      ' 窗體卸載事件
      Private Sub Form_Unload(Cancel As Integer)
          Set myWinFormControl = Nothing
      End Sub
      
      ' 響應控件ComButtonClick事件
      Private Sub myWinFormControl_ComButtonClick()
          MsgBox "用戶點擊了控件中的按鈕"
      End Sub
      
      ' 響應控件ComTextChanged事件
      Private Sub myWinFormControl_ComTextChanged()
          Me.Caption = "用戶修改了控件中的文字"
      End Sub

        在這段代碼中“Private WithEvents myWinFormControl As DCWinFormControlLib.myWinFormControl”定義了C#控件的變量,此處使用了WithEvents關鍵字聲明變量可以綁定事件,此時就可以添加myWinFormControl_ComButtonClick方法和myWinFormControl_ComTextChanged方法,這兩個方法就是控件的事件處理。[袁永福版權所有]

        在窗體加載代碼中調用了“myWinFormControl.Dock = 4”,實際上就是設置了C#中的System.Windows.Forms.Control類型的Dock屬性,由于VB中還不能識別System.Windows.Forms.DockStyle枚舉類型,因此只能設置整數,這里的整數4等價于DockStyle.Right值。

        由于MyWinFormControl類型只聲明了IComMyEvnet接口,沒實現其他接口,因此在開發階段,VB無法識別控件的所有的屬性和方法,因此在編寫代碼的時候無法彈出類型成員列表,因此只能利用VB的動態語言特性先硬編碼,在運行時VB會后期的識別和調用組件的公開的類型成員。

        在窗體加載時還運行了“myWinFormControl.AppendToContainerControl Me.picContainer.hWnd”,用于將控件作為圖片框的子控件硬塞入到VB窗體中。

        在窗體大小改變事件中,這行代碼還重新執行了一遍,用于根據作為容器的圖片框控件的大小來更新WinForm.NET控件的大小。

        這樣VB程序開發完畢,編譯執行EXE文件,該VB程序的運行界面如下圖所示:

       

        本程序有一個已知的BUG,那就是輸入焦點分配有點混亂。由于很多代碼繞過了VB運行庫,而是在.NET運行庫中運行,這是一種不多見的軟件運行架構。因此用戶界面上盡量少放置或不放置可以獲得焦點的VB控件。

       

      五.常見問題和調試

        VB調用.NET控件是一種異構軟件架構,很容易出現各種問題,最常見的是在VB中試圖創建控件對象實例時爆出“ActiveX component can't create object”的錯誤。

      解決方法如下

      1.       首先確定系統安裝了.NET框架。
      2.       執行命令行“regasm 程序集目錄\DCWinFormControlLib.dll   /tlb   /codebase”。
      3.       若還不行需要清理下系統注冊表,對于本控件,設置的GUID值為“60550064-C97F-4306-A8B2-6908F50780E3”,因此使用注冊表編輯器打開注冊項目路徑“HKEY_CLASSES_ROOT\CLSID\{60550064-C97F-4306-A8B2-6908F50780E3}\InprocServer32”,可以看到該組件歷次版本的注冊信息,刪掉這些注冊表項目,然后重新運行一遍regasm命令行。
      4.       可以使用gacutil工具將DCWinFormControlLib.dll放置在全局程序集緩存中。
      5.       還可以將VB編譯生成的EXE文件放在DCWinFormControlLib.dll和DCWinFormControl.tlb的文件目錄中運行。

        另外一個有可能遇到的情況就是VB程序不能運行,運行VB程序需要系統中有VB虛擬機,要檢查Windows系統目錄下的System32子目錄中是否存在MSVBVM60.DLL,若沒有則需要下載安裝VB6運行時,推薦下載地址為“http://download.microsoft.com/download/5/a/d/5ad868a0-8ecd-4bb0-a882-fe53eb7ef348/VB6.0-KB290887-X86.exe”。[袁永福版權所有]

       

      六。小結

        本文雖然討論的僅僅是VB中調用C#寫的WinForm控件,實際上可以擴展開來使用,比如可以以此為基礎,實現在VB\PB\DELPHI\VC窗體中嵌入WinForm.NET控件,由于WinForm.NET控件能承載WPF元素,因此也就能在VB中嵌入WPF程序了。

      posted on 2012-12-09 15:33  袁永福 電子病歷,醫療信息化  閱讀(9125)  評論(5)    收藏  舉報

      導航

      主站蜘蛛池模板: 国精产品自偷自偷ym使用方法| 丁香婷婷色综合激情五月| 久久精品一区二区东京热| 99RE6在线观看国产精品| 成年午夜性影院| 国产精品SM捆绑调教视频| 国产69精品久久久久99尤物| 亚洲欧美综合人成在线| 青春草在线视频观看| 国产亚洲999精品AA片在线爽| 亚洲av无码之国产精品网址蜜芽 | 白城市| 亚洲夜色噜噜av在线观看| 亚洲V天堂V手机在线 | 成人综合婷婷国产精品久久蜜臀| 九九热视频免费在线播放| 无码成人精品区在线观看| 亚洲女人天堂成人av在线 | 97se亚洲综合自在线| 中文字幕av一区二区| 国内精品无码一区二区三区| 亚洲久悠悠色悠在线播放| 亚洲 制服 丝袜 无码| 蜜臀精品一区二区三区四区| 伊人久久精品无码二区麻豆| 日韩美女一区二区三区视频| 四虎国产精品成人| 一区二区中文字幕久久| 日韩不卡一区二区三区四区| 国产成人女人在线观看| 欧美粗大| 亚洲国产良家在线观看| 四虎库影成人在线播放| 国产成人A在线视频免费| 视频一区二区三区四区不卡| 国产萌白酱喷水视频在线观看| 兰州市| 一区二区三区四区五区自拍| 日韩福利视频导航| 国产91精品调教在线播放| 亚洲精品漫画一二三区|