演练:在 WPF 中托管 Win32 控件

Windows Presentation Foundation (WPF)提供了用于创建应用程序的丰富环境。 但是,当你对 Win32 代码进行大量投资时,在 WPF 应用程序中至少重用部分代码而不是完全重写代码可能更有效。 WPF 提供了在 WPF 页面上托管 Win32 窗口的简单机制。

本主题将引导你了解应用程序在 WPF 示例中托管 Win32 列表框控件。 此常规过程可以扩展到托管任何 Win32 窗口。

要求

本主题假定基本熟悉 WPF 和 Windows API 编程。 有关 WPF 编程的基本简介,请参阅 入门。 有关 Windows API 编程的简介,请参阅有关该主题的任何书籍,特别是 Charles Petzold 的 Windows 编程

由于本主题附带的示例在 C# 中实现,因此它利用平台调用服务(PInvoke)来访问 Windows API。 对 PInvoke 的熟悉很有帮助,但不是必需的。

注释

本主题包含关联示例中的许多代码示例。 但是,为了提高可读性,它不包括完整的示例代码。 可以从 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. 处理发送到主机窗口的选定消息,例如来自子控件的通知。 可通过两种方式来执行此操作。

    • 如果您希望在宿主类中处理消息,就请重写 HwndHost 类的 WndProc 方法。

    • 如果希望 WPF 处理消息,请在后置代码中处理 HwndHostMessageHook 事件。 对于托管窗口收到的每条消息,都会发生此事件。 如果选择此选项,则仍必须重写 WndProc,但只需要最少的实现。

  7. 重写HwndHostDestroyWindowCoreWndProc方法。 必须重写这些方法才能满足 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,你还必须重写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 的大小正确。 然后,通过将对象Child分配给ControlHost主机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 消息来操控它。 控件通过向主机窗口发送通知来通知您用户何时与其交互。 托管 Win32 ListBox 控件的 WPF 实例包括一个用户界面,它提供了几个展示该功能如何实现的示例。

  • 将项追加到列表中。

  • 从列表中删除所选项

  • 显示当前选中项的文本。

  • 显示列表中的项数。

用户还可以单击列表框中的项目,就像常规 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页面的事件。 处理程序接收的信息与主机窗口的主窗口过程相同。 它还传递对布尔值的引用。 handledhandled设置为true以指示消息已处理,无需进一步处理。

WM_COMMAND 是出于各种原因发送的,因此您必须检查通知 ID,以确定它是否是您希望处理的事件。 ID 包含在参数的高 wParam 字中。 此示例使用按位运算符提取 ID。 如果用户已做出或更改其选择,则 ID 将为 LBN_SELCHANGE

当收到LBN_SELCHANGE时,该示例通过发送LB_GETCURSEL消息给控件来获取选定项的索引。 若要获取文本,请先创建一个 StringBuilder。 然后,向控件发送LB_GETTEXT消息。 将空 StringBuilder 对象作为 wParam 参数传递。 SendMessage 返回后,StringBuilder 将包含所选项的文本。 要使用 SendMessage 还需要一个额外的 PInvoke 声明。

最后,将handled设置为true以指示消息已处理。

另请参阅