Поделиться через


Пошаговое руководство. Размещение элемента управления Win32 в WPF

Windows Presentation Foundation (WPF) предоставляет многофункциональную среду для создания приложений. Однако при наличии существенных инвестиций в код Win32 может быть более эффективным повторное использование по крайней мере некоторой части этого кода в приложении WPF, а не переписывание его полностью. WPF предоставляет простой механизм размещения окна Win32 на странице WPF.

В этом разделе вас познакомят с приложением Hosting a Win32 ListBox Control в примере WPF, которое размещает элемент управления списка Win32. Эта общая процедура может быть расширена для размещения любого окна Win32.

Требования

В этом разделе предполагается базовое знакомство с программированием WPF и Windows API. Чтобы получить базовое введение в программирование WPF, см. Приступая к работе. Общие сведения о программировании API Windows см. в любой из многочисленных книг по теме, в частности программирование Windows Чарльзом Петзольдом.

Так как пример, сопровождающий этот раздел, реализуется в C#, он использует службы вызова платформы (PInvoke) для доступа к API Windows. Некоторые знания с PInvoke полезны, но не являются важными.

Замечание

В этом разделе содержится ряд примеров кода из связанного примера. Однако для удобства чтения он не включает полный пример кода. Вы можете получить или просмотреть полный код из примера размещения элемента управления Win32 ListBox в WPF.

Базовая процедура

В этом разделе описана базовая процедура размещения окна Win32 на странице WPF. Остальные разделы содержат подробности каждого шага.

Базовая процедура размещения:

  1. Реализуйте страницу WPF для размещения окна. Одним из методов является создание Border элемента для резервирования места на странице для размещённого окна.

  2. Реализуйте класс для размещения элемента управления, наследуемого от HwndHost.

  3. В этом классе переопределите HwndHost член BuildWindowCore класса.

  4. Создайте размещенное окно в качестве дочернего окна, содержащего страницу WPF. Хотя в обычном программировании WPF нет необходимости явно использовать его, хостинговая страница представляет собой окно с дескриптором (HWND). HWND страницы вы получаете через параметр hwndParent метода BuildWindowCore. Размещенное окно должно быть создано как дочерний элемент этого HWND.

  5. После того, как вы создадите окно хоста, верните дескриптор (HWND) размещенного окна. Если вы хотите разместить один или несколько элементов управления Win32, обычно создается окно-контейнер в качестве дочернего элемента HWND и элементы управления делаются дочерними элементами этого окна-контейнера. Обёртывание элементов управления в окне хоста предоставляет простой способ для вашей страницы WPF получать уведомления от элементов управления, что решает некоторые конкретные проблемы Win32 с уведомлениями через границу HWND.

  6. Обработайте выбранные сообщения, отправленные в окно хоста, такие как уведомления от дочерних элементов управления. Это можно сделать двумя способами.

    • Если вы предпочитаете обрабатывать сообщения в классе размещения, переопределите WndProc метод HwndHost класса.

    • Если вы предпочитаете обрабатывать сообщения WPF, обработайте HwndHost событие класса MessageHook в коде программной части. Это событие происходит для каждого сообщения, полученного размещенным окном. Если вы выбрали этот параметр, вам всё равно придется переопределить WndProc, но потребуется только минимальная реализация.

  7. Переопределите методы DestroyWindowCore и WndProc класса HwndHost. Для выполнения договора HwndHost необходимо переопределить эти методы, но вам, возможно, потребуется предоставить лишь минимальную реализацию.

  8. В файле связанного кода создайте экземпляр класса размещающего элемента управления и сделайте его дочерним элементом Border, предназначенным для размещения окна.

  9. Обмен данными с размещенным окном путем отправки сообщений Microsoft Windows и обработки сообщений из дочерних окон, таких как уведомления, отправленные элементами управления.

Реализация макета страницы

Макет страницы WPF, на котором размещен элемент управления ListBox, состоит из двух регионов. В левой части страницы размещается несколько элементов управления WPF, которые предоставляют пользовательский интерфейс, который позволяет управлять элементом управления 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. Конструктор принимает два параметра, высоту и ширину, соответствующую высоте и ширине Border элемента, на котором размещен элемент управления ListBox. Эти значения используются позже, чтобы убедиться, что размер элемента управления соответствует элементу 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, необходимо также переопределить методы WndProc и DestroyWindowCore в HwndHost. В этом примере сообщения для элемента управления обрабатываются обработчиком MessageHook , поэтому реализация WndProc и DestroyWindowCore является минимальной. В случае 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 для получения информации из элемента управления и изменения его содержимого. Сведения о том, как страница взаимодействует с элементом управления, рассматриваются в следующем разделе.

Замечание

Обратите внимание, что для функции 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 событие для страницы. Обработчик получает те же сведения, что и процедура главного окна хост-окна. Он также передает ссылку на логическое значение handled. Установите handled на true, чтобы указать, что вы обработали сообщение и дополнительная обработка не требуется.

WM_COMMAND отправляется по различным причинам, поэтому необходимо проверить идентификатор уведомления, чтобы определить, является ли это событием, которое требуется обрабатывать. Идентификатор содержится в высоком слове wParam параметра. В примере используются битовые операторы для извлечения идентификатора. Если пользователь сделал или изменил свой выбор, идентификатор будет LBN_SELCHANGE.

Когда LBN_SELCHANGE получено, выборка получает индекс выбранного элемента, отправляя элементу управления сообщение LB_GETCURSEL. Чтобы получить текст, сначала создайте StringBuilder. Затем вы отправляете элемент управления LB_GETTEXT сообщение. Передайте пустой StringBuilder объект в качестве wParam параметра. Когда SendMessage возвращается, StringBuilder будет содержать текст выбранного элемента. Это использование SendMessage требует еще одного объявления PInvoke.

Наконец, задайте значение handled на true, чтобы указать, что сообщение было обработано.

См. также