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


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

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

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

В этом разделе содержатся следующие подразделы.

  • Требования
  • Основная процедура
  • Реализация макета страницы
  • Реализация класса для размещения элемента управления Microsoft Win32
  • Размещение элемента управления на странице
  • Реализация связи между элементом управления и страницей
  • Связанные разделы

Требования

В материалах данного раздела предполагается начальное знакомство с программированием WPF и Win32. Основные сведения о программировании WPF см. в разделе Приступая к работе (WPF). Для знакомства с программированием Win32 следует обратиться к любой из многочисленных книг по этой теме, например, к Programming Windows, автор Charles Petzold.

Поскольку пример, используемый в этом разделе, реализован на языке C#, в нем применяется Platform Invocation Services (PInvoke) для доступа к Win32 API. Знакомство с 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, обрабатывайте событие MessageHook класса HwndHost в фоновом коде. Это событие возникает для каждого сообщения, получаемого размещаемым окном. Если выбрать такой вариант, по-прежнему необходимо переопределить WndProc, но достаточно минимальной реализации.

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

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

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

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

Макет для страницы WPF, на которой размещен элемент управления ListBox, состоит из двух областей. В левой части страницы размещены несколько элементов управления WPF, которые предоставляют user interface (UI), позволяющий управлять элементом управления Win32. В верхнем правом углу страницы есть квадратная область для размещенного элемента управления ListBox.

Код реализации этого макета довольно прост. Корневой элемент — это DockPanel, имеющий два дочерних элемента. Первый элемент — это Border, в котором размещен элемент управления ListBox. Он занимает квадрат 200x200 в верхнем правом углу страницы. Второй элемент — это StackPanel, содержащий набор элементов управления WPF, отображающих сведения и позволяющих управлять элементом управления ListBox путем задания предоставленных свойств взаимодействия. Для каждого из элементов, являющихся потомками StackPanel, справочные материалы по различным элементам, используемым для детализации того, чем эти элементы являются и что они делают, перечислены в примере ниже, но они не будут описаны здесь (в базовой модели взаимодействия они не требуются, они предоставлены для добавления интерактивности в пример).

<Window
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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
      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
public class ControlHost : HwndHost
{
  IntPtr hwndControl;
  IntPtr hwndHost;
  int hostHeight, hostWidth;

  public ControlHost(double height, double width)
  {
    hostHeight = (int)height;
    hostWidth = (int)width;
  }

Имеется также набор констант. Эти константы в основном берутся из Winuser.h и позволяют использовать обычные имена при вызове функций Win32.

    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
internal const int
  WS_CHILD = 0x40000000,
  WS_VISIBLE = 0x10000000,
  LBS_NOTIFY = 0x00000001,
  HOST_ID = 0x00000002,
  LISTBOX_ID = 0x00000001,
  WS_VSCROLL = 0x00200000,
  WS_BORDER = 0x00800000;

Переопределение BuildWindowCore для создания окна Microsoft Win32

Переопределите этот метод для создания окна Win32, которое будет размещаться на странице, и создайте связь между окном и страницей. Поскольку в этом примере также размещается элемент управления ListBox, создаются два окна. Первое окно — это окно, фактически размещенное на странице WPF. Элемент управления ListBox создается в качестве дочернего элемента этого окна.

Причиной такого подхода является упрощение процесса получения уведомлений от элемента управления. Класс HwndHost позволяет обрабатывать сообщения, отправленные размещаемому окну. Если разместить элемент управления Win32 непосредственно, то будут приходить сообщения, отправленные во внутреннем цикле сообщений элемента управления. Можно отображать элемент управления и отправлять ему сообщения, но не получать уведомления, которые элемент управления отправляет своему родительскому окну. Это означает, помимо прочего, что нет способа обнаружения взаимодействия пользователя с элементом управления. Вместо этого создайте главное окно и сделайте элемент управления дочерним элементом этого окна. Это позволяет обрабатывать сообщения для главного окна, в том числе уведомления, отправленные ему элементом управления. Поскольку главное окно является большим, чем простая обертка для элемента управления, для удобства пакет будет называться элементом управления ListBox.

Создание главного окна и элемента управления ListBox

PInvoke можно использовать для создания главного окна для элемента управления путем создания и регистрации класса окна и т.д. Однако намного более простым способом является создание окна с помощью предопределенного "статического" класса окна. При этом можно использовать процедуру окна, необходимую для получения уведомлений от элемента управления, причем в этом случае требуется минимум кода.

HWND элемента управления предоставляется только через свойство только для чтения, чтобы главная страница могла использовать его для отправки сообщений элементу управления.

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

Элемент управления ListBox создается в качестве дочернего элемента главного окна. Высота и ширина обоих окон задаются значениями, передаваемыми конструктору, описанному выше. Это гарантирует, что размеры главного окна и элемента управления совпадают с зарезервированной областью на странице. После создания окна образец возвращает объект HandleRef, содержащий HWND главного окна.

    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
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);
}
    '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
//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);

Реализация DestroyWindow и WndProc

В дополнение к BuildWindowCore необходимо также переопределить методы WndProc и DestroyWindowCore для HwndHost. В этом примере сообщения для элемента управления обрабатываются обработчиком MessageHook, таким образом, реализация WndProc и DestroyWindowCore минимальна. В случае WndProc задайте параметру handled значение false для указания того, что сообщение не было обработано, и верните 0. В случае DestroyWindowCore просто уничтожьте окно.

    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
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);
}
    <DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
    Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
    End Function
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);

Размещение элемента управления на странице

Чтобы разместить элемент управления на странице, сначала создайте новый экземпляр класса ControlHost. Передайте высоту и ширину элемента границы, содержащего элемент управления (ControlHostElement), конструктору ControlHost. При этом гарантируется корректность размеров элемента управления ListBox. Затем разместите элемент управления на странице путем назначения объекта ControlHost свойству Child ведущего Border.

В образце задается обработчик событий MessageHook для ControlHost, чтобы получать сообщения от элемента управления. Это событие вызывается для каждого сообщения, посланного размещаемому окну. В этом случае это сообщения, отправленные окну, которое служит оболочкой текущего элемента управления ListBox, включая уведомления от элемента управления. В примере вызывается метод SendMessage для получения сведений от элемента управления и для изменения его содержимого. Детали взаимодействия страницы с элементом управления рассматриваются в следующем подразделе.

ПримечаниеПримечание

Обратите внимание на то, что для SendMessage присутствуют два объявления PInvoke.Это необходимо, так как в одном случае используется параметр wParam для передачи строки, а в другом параметр используется для передачи целого числа.Необходимо отдельно объявить каждую сигнатуру, чтобы убедиться, что данные маршалируются правильно.

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
    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();
    }

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

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);

Реализация связи между элементом управления и страницей

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

  • Добавьте элемент в список.

  • Удалите выделенный элемент из списка.

  • Отобразите текст текущего выделенного элемента списка.

  • Отобразите количество элементов в списке.

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

Чтобы добавить элементы, отправьте списку сообщение LB_ADDSTRING. Чтобы удалить элементы, отправьте LB_GETCURSEL для получения индекса текущего выделенного элемента, а затем LB_DELETESTRING для удаления элемента. В примере также отправляется LB_GETCOUNT, а возвращенное значение используется для обновления экрана, отображающего число элементов. В обоих экземплярах SendMessage используется одно из объявлений PInvoke, рассмотренных в предыдущем подразделе.

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
private void AppendText(object sender, EventArgs args)
{
  if (txtAppend.Text != string.Empty)
  {
    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();
}

Когда пользователь выбирает элемент, изменяется область выделения, и элемент управления уведомляет главное окно путем отправки ему сообщения WM_COMMAND, которое вызывает событие MessageHook для страницы. Обработчик получает те же сведения, что и процедура главного окна. Он также передает ссылку на логическое значение handled. Присвойте параметру handled значение true, чтобы указать, что сообщение было обработано и дальнейшая обработка не требуется.

WM_COMMAND отправляется по различным причинам, поэтому необходимо проверить идентификатор уведомления, чтобы определить, является ли событие тем, которое вы хотите обработать. Идентификатор содержится в старшем слове параметра wParam. Поскольку в Microsoft .NET нет макроса HIWORD, в образце используются битовые операции для получения идентификатора. Если пользователь сделал выбор или изменил его, идентификатором будет LBN_SELCHANGE.

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

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

См. также

Ссылки

HwndHost

Основные понятия

Взаимодействие WPF и Win32

Пошаговое руководство. Начало работы с WPF