共用方式為


逐步解說:在WPF中裝載 Win32 控制項

Windows Presentation Foundation (WPF) 提供一個用來建立應用程式的豐富環境。 不過,如果您在 Win32 程式碼上已經有相當大的投資,在WPF應用程式中重複使用的方式至少一部分程式碼可能比完全重寫程式碼更有效率。 WPF提供了一個直觀的機制,可以在WPF頁面上承載 Win32 視窗。

本主題將逐步引導您完成在WPF樣本中裝載 Win32 ListBox 控制項的應用程式範例,該應用程式裝載了一個Win32清單方塊的控制項。 此一般程序可以延伸到裝載任何 Win32 的視窗。

需求

本主題假設您對WPF和Windows API 程式設計已有基本的熟悉度。 若需WPF程式設計的基本簡介,請參閱開始使用。 如需Windows API 程式設計的簡介,請參閱有關該主旨的任何書籍,特別參考Charles Petzold的《Programming Windows》(Windows 程式設計)

因為本主題隨附的範例是在 C# 中實作的,所以它會使用平台調用服務(PInvoke)來存取Windows API。 熟悉PInvoke會有幫助,但並非必要。

注意

本主題包含一些來自相關聯範例的程式碼範例。 不過,為了方便閱讀,並未包含完整的範例程式碼。 您可以從 Hosting a Win32 ListBox Control in WPF Sample (在 WPF 中裝載 Win32 ListBox 控制項的範例) 取得或檢視完整程式碼。

基本程序

本節概述在WPF頁面上裝載一個 Win32 視窗的基本程序。 其餘各節將說明每個步驟的詳細資料。

基本裝載程序如下︰

  1. 實作一個WPF頁面來裝載視窗。 其中一種技術是建立一個Border元件,以保留頁面中用於裝載視窗的區域。

  2. 實作一個類別,以裝載此元件並繼承自HwndHost控制項。

  3. 在此HwndHost類別中,覆寫該類成員BuildWindowCore

  4. 將裝載的視窗建立為包含WPF頁面之視窗的子系。 雖然傳統的WPF程式設計不需要明確使用它,但是裝載頁面是一個具有控制代碼 (HWND) 的視窗。 您會透過BuildWindowCore方法的hwndParent參數以接收頁面HWND。 裝載的視窗應該建立為此 HWND 的子系。

  5. 建立主視窗之後,即會傳回裝載之視窗的 HWND。 如果您想要裝載一或多個 Win32 控制項,通常需要建立一個主視窗作為HWND的子系,並將控制項設定為該主視窗的子系。 在主視窗中包裝控制項可提供簡單方式,讓WPF頁面接收來自控制項的通知,以處理跨HWND界限之通知的部分特定 Win32 問題。

  6. 處理傳送至主視窗的已選取訊息,例如來自子控制項的通知。 做法有二種。

    • 如果您想在您的主控類別中處理訊息,請覆寫該WndProc裝載HwndHost類別的方法。

    • 如果您想要讓WPF處理訊息,請在您的後端程式代碼中處理該HwndHost類別的MessageHook事件。 針對裝載的視窗接收到的每個訊息,都會發生此事件。 如果您選擇此選項,則仍然必須覆寫WndProc但是您只需要進行最少的實作。

  7. 覆寫該DestroyWindowCoreWndProc命令方法HwndHost。 您必須覆寫這些方法才能滿足此HwndHost合約,但您可能只需要提供一個最少的實作。

  8. 在程式碼後置檔案中,建立一個控制項裝載類別的實例,並將其設定成要裝載視窗的Border元素的子項。

  9. 與裝載的視窗進行通訊,方法是將Microsoft Windows訊息傳送給它,並處理來自其子視窗的訊息,例如控制項所傳送的通知。

實作版面配置

裝載 ListBox 控制項之WPF頁面的版面配置包含兩個區域。 在頁面左側裝載數個WPF控制項,以提供一個用戶介面 (UI) 讓您管理 Win32 控制項。 頁面的右上角具有 ListBox 託管控制項的方形區域。

實作此版面配置的程式碼相當簡單。 根元件是一個DockPanel具有兩個子元件的容器。 第一個是用於裝載ListBox控制件的Border元素。 它會佔用頁面右上角的 200x200 方形。 第二個是包含一StackPanel組WPF控制項的元素,這組控制項顯示資訊並允許您透過設定已公開的互操通作屬性來操作ListBox控制項。 對於所有屬於該元素的子元素StackPanel,請參閱參考資料以了解各種元件的詳細資訊,包括這些元件是什麼或是它們的功能。這些元件在下面的範例程式碼中列出,但在此不作解釋 (基本的互操作模型並不需要其中任何一個,它們是為了增強範例的互動性而提供的)。

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="WPF_Hosting_Win32_Control.HostWindow"
  Name="mainWindow"
  Loaded="On_UIReady">

  <DockPanel Background="LightGreen">
    <Border Name="ControlHostElement"
    Width="200"
    Height="200"
    HorizontalAlignment="Right"
    VerticalAlignment="Top"
    BorderBrush="LightGray"
    BorderThickness="3"
    DockPanel.Dock="Right"/>
    <StackPanel>
      <Label HorizontalAlignment="Center"
        Margin="0,10,0,0"
        FontSize="14"
        FontWeight="Bold">Control the Control</Label>
      <TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock  Name="selectedText"/></TextBlock>
      <TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock  Name="numItems"/></TextBlock>
  
      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>
  
      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Append an Item to the List</Label>
      <StackPanel Orientation="Horizontal">
        <Label HorizontalAlignment="Left"
          Margin="10,10,10,10">Item Text</Label>
        <TextBox HorizontalAlignment="Left"
          Name="txtAppend"
          Width="200"
          Margin="10,10,10,10"></TextBox>
      </StackPanel>
  
      <Button HorizontalAlignment="Left"
        Click="AppendText"
        Width="75"
        Margin="10,10,10,10">Append</Button>

      <Line X1="0" X2="200"
        Stroke="LightYellow"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="0,20,0,0"/>
  
      <Label HorizontalAlignment="Center"
        Margin="10,10,10,10">Delete the Selected Item</Label>
  
      <Button Click="DeleteText"
        Width="125"
        Margin="10,10,10,10"
        HorizontalAlignment="Left">Delete</Button>
    </StackPanel>
  </DockPanel>
</Window>  

實作類別以裝載 Microsoft Win32 控制項

此範例的核心是實際裝載 ControlHost.cs 控制項的類別。 它繼承自 HwndHost。 此建構函式接受兩個參數:高度和寬度,而這對應至裝載ListBox控制項之Border項目的高度和寬度。 稍後會使用這些數值,確保控制項的大小符合該Border項目。

public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }
Public Class ControlHost
    Inherits HwndHost
  Private hwndControl As IntPtr
  Private hwndHost As IntPtr
  Private hostHeight, hostWidth As Integer

  Public Sub New(ByVal height As Double, ByVal width As Double)
          hostHeight = CInt(height)
          hostWidth = CInt(width)
  End Sub

還有一組常數。 這些常數主要取自Winuser.h,並可讓您在呼叫 Win32 函式時使用傳統名稱。

internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;
Friend Const WS_CHILD As Integer = &H40000000, WS_VISIBLE As Integer = &H10000000, LBS_NOTIFY As Integer = &H00000001, HOST_ID As Integer = &H00000002, LISTBOX_ID As Integer = &H00000001, WS_VSCROLL As Integer = &H00200000, WS_BORDER As Integer = &H00800000

覆寫 BuildWindowCore 以建立 Microsoft Win32 視窗

您可以覆寫這個方法來建立將透過頁面裝載的 Win32 視窗,並且建立視窗與頁面之間的連結。 因為此範例包含裝載 ListBox 控制項,所以會建立兩個視窗。 第一個是 WPF 頁面實際裝載的視窗。 ListBox 控制項會建立為該視窗的子系。

此方法的原因是要簡化接收來自控制項之通知的程序。 該HwndHost類別可讓您處理傳送至它正在裝載之視窗的訊息。 如果您直接裝載 Win32 控制項,則會收到傳送至控制項之內部訊息迴圈的訊息。 您可以顯示控制項並將訊息傳送給它,但收不到控制項傳送至其父視窗的通知。 這表示,除此之外,您偵測不到使用者何時與控制項互動。 相反地,會建立主視窗,並將控制項設定為該視窗的子系。 這可讓您處理主視窗的訊息,包括控制項傳送給它的通知。 為了方便起見,因為主視窗略高於控制項的簡單包裝函式,所以此套件將稱為 ListBox 控制項。

建立主視窗和 ListBox 控制項

您可以使用PInvoke來建立控制項的主視窗,方法是建立和註冊視窗類別等等。 不過,比較簡單的方法是使用預先定義的「靜態」視窗類別來建立視窗。 這提供接收來自控制項之通知所需的視窗程序,而且需要最少的程式碼。

控制項的 HWND 是透過唯讀屬性所公開;因此,主頁面可以使用它,以將訊息傳送至控制項。

public IntPtr hwndListBox
{
  get { return hwndControl; }
}
Public ReadOnly Property hwndListBox() As IntPtr
  Get
      Return hwndControl
  End Get
End Property

ListBox 控制項會建立為主視窗的子系。 如上面所討論,這兩個視窗的高度和寬度都會設定為傳遞至建構函式的值。 這確保主視窗和控制項的大小與頁面上的保留區域相同。 在建立視窗之後,此範例會傳回一個物件,而此HandleRef物件包含該主視窗的HWND。

protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
  hwndControl = IntPtr.Zero;
  hwndHost = IntPtr.Zero;

  hwndHost = CreateWindowEx(0, "static", "",
                            WS_CHILD | WS_VISIBLE,
                            0, 0,
                            hostWidth, hostHeight,
                            hwndParent.Handle,
                            (IntPtr)HOST_ID,
                            IntPtr.Zero,
                            0);

  hwndControl = CreateWindowEx(0, "listbox", "",
                                WS_CHILD | WS_VISIBLE | LBS_NOTIFY
                                  | WS_VSCROLL | WS_BORDER,
                                0, 0,
                                hostWidth, hostHeight,
                                hwndHost,
                                (IntPtr) LISTBOX_ID,
                                IntPtr.Zero,
                                0);

  return new HandleRef(this, hwndHost);
}
Protected Overrides Function BuildWindowCore(ByVal hwndParent As HandleRef) As HandleRef
  hwndControl = IntPtr.Zero
  hwndHost = IntPtr.Zero

  hwndHost = CreateWindowEx(0, "static", "", WS_CHILD Or WS_VISIBLE, 0, 0, hostWidth, hostHeight, hwndParent.Handle, New IntPtr(HOST_ID), IntPtr.Zero, 0)

  hwndControl = CreateWindowEx(0, "listbox", "", WS_CHILD Or WS_VISIBLE Or LBS_NOTIFY Or WS_VSCROLL Or WS_BORDER, 0, 0, hostWidth, hostHeight, hwndHost, New IntPtr(LISTBOX_ID), IntPtr.Zero, 0)

  Return New HandleRef(Me, hwndHost)
End Function
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
                                              string lpszClassName,
                                              string lpszWindowName,
                                              int style,
                                              int x, int y,
                                              int width, int height,
                                              IntPtr hwndParent,
                                              IntPtr hMenu,
                                              IntPtr hInst,
                                              [MarshalAs(UnmanagedType.AsAny)] object pvParam);
'PInvoke declarations
<DllImport("user32.dll", EntryPoint := "CreateWindowEx", CharSet := CharSet.Unicode)>
Friend Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpszClassName As String, ByVal lpszWindowName As String, ByVal style As Integer, ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal hwndParent As IntPtr, ByVal hMenu As IntPtr, ByVal hInst As IntPtr, <MarshalAs(UnmanagedType.AsAny)> ByVal pvParam As Object) As IntPtr
End Function

實作 DestroyWindow 和 WndProc

除了BuildWindowCore之外,您也必須覆寫WndProcDestroyWindowCore方法的HwndHost。 在此範例中,控件的訊息是由該MessageHook處理程序處理,因此WndProcDestroyWindowCore的實作是最少的。 在WndProc的情況下,將handled設定為false,表示訊息未處理並傳回 0。 對於DestroyWindowCore來說,只須銷毀該視窗。

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  handled = false;
  return IntPtr.Zero;
}

protected override void DestroyWindowCore(HandleRef hwnd)
{
  DestroyWindow(hwnd.Handle);
}
Protected Overrides Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
  handled = False
  Return IntPtr.Zero
End Function

Protected Overrides Sub DestroyWindowCore(ByVal hwnd As HandleRef)
  DestroyWindow(hwnd.Handle)
End Sub
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
<DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
End Function

在頁面上裝載控制項

為了要在頁面上裝載控制項,請先建立一個ControlHost類別的新執行個體。 將包含控制項 (ControlHostElement) 之邊框元素的高度和寬度傳遞給 ControlHost 建構函式。 這確保 ListBox 的大小正確。 然後將ControlHost物件指派給主機的Child屬性,以在頁面上裝載控制項Border

此範例將處理程序附加至MessageHook的事件ControlHost,以接收來自控制項的訊息。 針對傳送至裝載之視窗的每個訊息,都會引發此事件。 在此情況下,這些是傳送至包裝實際 ListBox 控制項之視窗的訊息,包括來自控制項的通知。 此範例會呼叫 SendMessage 以從控制項取得資訊,並修改其內容。 下節討論頁面如何與控制項進行通訊的詳細資料。

注意

請注意,這裡有兩個PInvoke給SendMessage的公告。 這是必要的,因為其中一個使用wParam參數來傳遞字串,另一個則使用它來傳遞整數。 每個簽章都必須要有不同的宣告,確保正確地封送處理資料。

public partial class HostWindow : Window
{
int selectedItem;
IntPtr hwndListBox;
ControlHost listControl;
Application app;
Window myWindow;
int itemCount;

private void On_UIReady(object sender, EventArgs e)
{
  app = System.Windows.Application.Current;
  myWindow = app.MainWindow;
  myWindow.SizeToContent = SizeToContent.WidthAndHeight;
  listControl = new ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth);
  ControlHostElement.Child = listControl;
  listControl.MessageHook += new HwndSourceHook(ControlMsgFilter);
  hwndListBox = listControl.hwndListBox;
  for (int i = 0; i < 15; i++) //populate listbox
  {
    string itemText = "Item" + i.ToString();
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" +  itemCount.ToString();
}
Partial Public Class HostWindow
    Inherits Window
    Private selectedItem As Integer
    Private hwndListBox As IntPtr
    Private listControl As ControlHost
    Private app As Application
    Private myWindow As Window
    Private itemCount As Integer

    Private Sub On_UIReady(ByVal sender As Object, ByVal e As EventArgs)
        app = System.Windows.Application.Current
        myWindow = app.MainWindow
        myWindow.SizeToContent = SizeToContent.WidthAndHeight
        listControl = New ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth)
        ControlHostElement.Child = listControl
        AddHandler listControl.MessageHook, AddressOf ControlMsgFilter
        hwndListBox = listControl.hwndListBox
        For i As Integer = 0 To 14 'populate listbox
            Dim itemText As String = "Item" & i.ToString()
            SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText)
        Next i
        itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
        numItems.Text = "" & itemCount.ToString()
    End Sub

private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  int textLength;

  handled = false;
  if (msg == WM_COMMAND)
  {
    switch ((uint)wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
    {
      case LBN_SELCHANGE : //Get the item text and display it
        selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
        textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero);
        StringBuilder itemText = new StringBuilder();
        SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText);
        selectedText.Text = itemText.ToString();
        handled = true;
        break;
    }
  }
  return IntPtr.Zero;
}
internal const int
  LBN_SELCHANGE = 0x00000001,
  WM_COMMAND = 0x00000111,
  LB_GETCURSEL = 0x00000188,
  LB_GETTEXTLEN = 0x0000018A,
  LB_ADDSTRING = 0x00000180,
  LB_GETTEXT = 0x00000189,
  LB_DELETESTRING = 0x00000182,
  LB_GETCOUNT = 0x0000018B;

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       IntPtr wParam,
                                       IntPtr lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       int wParam,
                                       [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
                                          int msg,
                                          IntPtr wParam,
                                          String lParam);

Private Function ControlMsgFilter(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    Dim textLength As Integer

    handled = False
    If msg = WM_COMMAND Then
        Select Case CUInt(wParam.ToInt32()) >> 16 And &HFFFF 'extract the HIWORD
            Case LBN_SELCHANGE 'Get the item text and display it
                selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
                textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero)
                Dim itemText As New StringBuilder()
                SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText)
                selectedText.Text = itemText.ToString()
                handled = True
        End Select
    End If
    Return IntPtr.Zero
End Function
Friend Const LBN_SELCHANGE As Integer = &H1, WM_COMMAND As Integer = &H111, LB_GETCURSEL As Integer = &H188, LB_GETTEXTLEN As Integer = &H18A, LB_ADDSTRING As Integer = &H180, LB_GETTEXT As Integer = &H189, LB_DELETESTRING As Integer = &H182, LB_GETCOUNT As Integer = &H18B

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As StringBuilder) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function

實作控制項與頁面之間的通訊

您可以通過發送 Windows 訊息來操作該控制項。 在使用者透過將通知傳送至其主視窗與控制項互動時,控制項就會通知您。 該在WPF中托管Win32 ListBox控制項範例包含一個用戶介面,提供了幾個這方面的運作示例:

  • 將項目附加至清單。

  • 刪除清單中選取的項目

  • 顯示目前所選取項目的文字。

  • 顯示清單中的項目數。

用戶也可以按一下清單方塊中的項目來選取項目,就像傳統 Win32 應用程式一樣。 每次使用者透過選取、新增或附加項目來變更清單方塊的狀態時,都會更新顯示的資料。

若要附加項目,請將一個訊息LB_ADDSTRING傳送給清單方塊。 若要刪除項目,請傳送LB_GETCURSEL以取得目前選取項目的索引,然後傳送LB_DELETESTRING以刪除項目。 此範例也會傳送LB_GETCOUNT,並使用所傳回的值來更新可顯示項目數的展示。 這兩個實例SendMessage均使用了前一節中討論的PInvoke聲明。

private void AppendText(object sender, EventArgs args)
{
  if (!string.IsNullOrEmpty(txtAppend.Text))
  {
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
private void DeleteText(object sender, EventArgs args)
{
  selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
  if (selectedItem != -1) //check for selected item
  {
    SendMessage(hwndListBox, LB_DELETESTRING, (IntPtr)selectedItem, IntPtr.Zero);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
Private Sub AppendText(ByVal sender As Object, ByVal args As EventArgs)
    If txtAppend.Text <> String.Empty Then
        SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub
Private Sub DeleteText(ByVal sender As Object, ByVal args As EventArgs)
    selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
    If selectedItem <> -1 Then 'check for selected item
        SendMessage(hwndListBox, LB_DELETESTRING, New IntPtr(selectedItem), IntPtr.Zero)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub

當用戶選擇一個項目或更改他的選擇時,控制項會通過發送一條WM_COMMAND訊息來通知主窗口,這會觸發該頁面的MessageHook事件。 處理常式會接收與主視窗的主要視窗程序相同的資訊。 它也會傳遞布林值的參考 handled。 您將handled設定成true,表示已經處理過訊息,不需要再進行更進一步的處理。

會基於各種原因傳送WM_COMMAND,因此,您必須檢查通知識別碼來判斷它是否為您要處理的事件。 此識別碼包含在wParam參數的高字中。 該範例會使用位元運算子來提取ID。 如果用戶已建立或變更其選取項目,則識別碼會是LBN_SELCHANGE

收到LBN_SELCHANGE時,此範例會將一個LB_GETCURSEL訊息傳送給控制項,以取得所選取項目的索引。 若要取得文字,請先建立一個StringBuilder。 您接著會將一個LB_GETTEXT訊息傳送給控制項。 將空的StringBuilder對象當做wParam的參數傳遞。 當SendMessage傳回時,該StringBuilder會包含選取項目的文字。 這項SendMessage的使用還需要另一個PInvoke宣告。

最後,將handled設定為true,指出已處理訊息。

另請參閱