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

Windows Presentation Foundation (WPF) 提供豐富的環境來建立應用程式。 不過,當您對 Win32 程式碼進行大量投資時,至少在 WPF 應用程式中重複使用部分程式碼,而不是完全重寫程式碼可能更有效率。 WPF 提供直接的機制,可在 WPF 頁面上裝載 Win32 視窗。

本主題會逐步引導您完成裝載 Win32 清單方塊控制項的 WPF 範例 中裝載 Win32 ListBox 控制項的應用程式 。 此一般程式可以延伸至裝載任何 Win32 視窗。

需求

本主題假設您已熟悉 WPF 和 Windows API 程式設計。 如需 WPF 程式設計的基本簡介,請參閱 使用者入門 。 如需 Windows API 程式設計的簡介,請參閱有關該主題的任何書籍,特別是 Charles Petzold 的程式設計 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)。 您會透過 hwndParent 方法的 BuildWindowCore 參數接收頁面 HWND。 裝載的視窗應該建立為此 HWND 的子系。

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

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

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

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

  7. DestroyWindowCore覆寫 的 HwndHostWndProc 方法。 您必須覆寫這些方法來滿足 HwndHost 合約,但您可能只需要提供最少的實作。

  8. 在您的程式碼後置檔案中,建立控制項主控類別的實例,並使它成為用來裝載視窗之 Border 專案的子系。

  9. 藉由傳送 Microsoft Windows 訊息並處理其子視窗的訊息,例如控制項所傳送的通知,與託管視窗通訊。

實作版面配置

主控 ListBox 控制項之 WPF 頁面的配置包含兩個區域。 頁面左側裝載數個 WPF 控制項,提供使用者介面 (UI), 可讓您操作 Win32 控制項。 頁面的右上角具有 ListBox 託管控制項的方形區域。

實作此版面配置的程式碼相當簡單。 根項目是具有兩個 DockPanel 子項目的 。 第一個 Border 是裝載 ListBox 控制項的專案。 它會佔用頁面右上角的 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 之外,您也必須覆寫 WndProcHwndHostDestroyWindowCore 方法。 在此範例中,控制項的訊息是由 MessageHook 處理常式處理,因此 的 WndProc 實作和 DestroyWindowCore 是最小的。 在 的案例中 WndProc ,將 設定 handledfalse ,表示訊息未處理並傳回 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 的大小正確。 然後將 物件指派 ControlHostChild 主機 Border 的 屬性,以在頁面上裝載 控制項。

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

注意

請注意,SendMessage 有兩個 PInvoke 宣告。 這是必要的,因為其中一個使用 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 控制項範例包含 UI,提供數個運作方式的範例:

  • 將項目附加至清單。

  • 刪除清單中選取的項目

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

  • 顯示清單中的項目數。

使用者也可以按一下清單方塊中的專案,就像傳統 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。 您設定 handledtrue ,表示您已處理訊息,而且不需要進一步處理。

WM_COMMAND 會因為各種原因而傳送,因此您必須檢查通知識別碼,以判斷它是否為您想要處理的事件。 識別碼包含在 參數的高字 wParam 中。 此範例會使用位運算子來擷取識別碼。 如果使用者已進行或變更其選取專案,識別碼會是 LBN_SELCHANGE

收到 時 LBN_SELCHANGE ,此範例會藉由傳送控制項 訊息 LB_GETCURSEL 來取得選取專案的索引。 若要取得文字,請先建立 StringBuilder 。 接著,您會傳送訊息 給控制項。 LB_GETTEXT 傳遞空 StringBuilder 的 物件做為 wParam 參數。 傳回時 SendMessageStringBuilder 會包含所選項目的文字。 這種用法 SendMessage 需要另一個 PInvoke 宣告。

最後,將 設定 handledtrue ,表示已處理訊息。

另請參閱