チュートリアル: WPF での Win32 コントロールのホストWalkthrough: Hosting a Win32 Control in WPF

Windows Presentation Foundation (WPF) は、アプリケーションを作成するための豊富な環境を提供します。Windows Presentation Foundation (WPF) provides a rich environment for creating applications. ただし、Win32 コードに多大な投資をしている場合は、完全に書き直すのではなく、少なくともそのコードの一部を WPF アプリケーションで再利用する方が効果的な場合があります。However, when you have a substantial investment in Win32 code, it may be more effective to reuse at least some of that code in your WPF application rather than rewrite it completely. Wpf は、WPF ページで Win32 ウィンドウをホストするための簡単なメカニズムを提供します。WPF provides a straightforward mechanism for hosting a Win32 window, on a WPF page.

このトピックでは、Win32 のリストボックスコントロールをホストする、 WPF の ListBox コントロールをホストするアプリケーションについて説明します。This topic walks you through an application, Hosting a Win32 ListBox Control in WPF Sample, that hosts a Win32 list box control. この一般的な手順は、Win32 ウィンドウをホストするように拡張できます。This general procedure can be extended to hosting any Win32 window.

必要条件Requirements

このトピックでは、WPF と Windows API プログラミングの基本的な知識があることを前提としています。This topic assumes a basic familiarity with both WPF and Windows API programming. WPF プログラミングの基本的な概要については、「はじめに」を参照してください。For a basic introduction to WPF programming, see Getting Started. Windows API プログラミングの概要については、「チャールズ Petzold 著) による特定のプログラミングウィンドウ」に記載されている、多くの書籍を参照してください。For an introduction to Windows API programming, see any of the numerous books on the subject, in particular Programming Windows by Charles Petzold.

このトピックに付属するサンプルはにC#実装されているため、プラットフォーム呼び出しサービス (PInvoke) を使用して Windows API にアクセスします。Because the sample that accompanies this topic is implemented in C#, it makes use of Platform Invocation Services (PInvoke) to access the Windows API. PInvoke の知識は役に立ちますが、必須ではありません。Some familiarity with PInvoke is helpful but not essential.

注意

このトピックには、関連付けられているサンプルのコード例が多数含まれています。This topic includes a number of code examples from the associated sample. しかし、読みやすくするため、完全なサンプル コードは含まれていません。However, for readability, it does not include the complete sample code. 完全なコードを取得または表示するには、WPF の ListBox コントロールのホストに関するサンプルを参照してください。You can obtain or view complete code from Hosting a Win32 ListBox Control in WPF Sample.

基本手順The Basic Procedure

このセクションでは、WPF ページで Win32 ウィンドウをホストするための基本的な手順について説明します。This section outlines the basic procedure for hosting a Win32 window on a WPF page. 残りのセクションでは、各手順の詳細について説明します。The remaining sections go through the details of each step.

基本的なホスティング手順は次のとおりです。The basic hosting procedure is:

  1. ウィンドウをホストする WPF ページを実装します。Implement a WPF page to host the window. 1つの方法は、 Borderホストされているウィンドウのページのセクションを予約する要素を作成することです。One technique is to create a Border element to reserve a section of the page for the hosted window.

  2. からHwndHost継承するコントロールをホストするクラスを実装します。Implement a class to host the control that inherits from HwndHost.

  3. そのクラスで、 HwndHostクラスメンバー BuildWindowCoreをオーバーライドします。In that class, override the HwndHost class member BuildWindowCore.

  4. ホストされているウィンドウを、WPF ページを含むウィンドウの子として作成します。Create the hosted window as a child of the window that contains the WPF page. 従来の WPF プログラミングでは、明示的に使用する必要はありませんが、ホストページはハンドル (HWND) を持つウィンドウです。Although conventional WPF programming does not need to explicitly make use of it, the hosting page is a window with a handle (HWND). ページ HWND を受け取るには、 hwndParent BuildWindowCoreメソッドのパラメーターを使用します。You receive the page HWND through the hwndParent parameter of the BuildWindowCore method. ホストされているウィンドウは、この HWND の子として作成される必要があります。The hosted window should be created as a child of this HWND.

  5. ホストウィンドウを作成したら、ホストされているウィンドウの HWND を返します。Once you have created the host window, return the HWND of the hosted window. 1つ以上の Win32 コントロールをホストする場合は、通常、ホストウィンドウを HWND の子として作成し、そのホストウィンドウの子コントロールを作成します。If you want to host one or more Win32 controls, you typically create a host window as a child of the HWND and make the controls children of that host window. ホストウィンドウでコントロールをラップすると、WPF ページでコントロールからの通知を簡単に受信できるようになります。これは、HWND 境界を越えた通知に関する特定の Win32 の問題を処理します。Wrapping the controls in a host window provides a simple way for your WPF page to receive notifications from the controls, which deals with some particular Win32 issues with notifications across the HWND boundary.

  6. 子コントロールからの通知など、ホストウィンドウに送信される選択されたメッセージを処理します。Handle selected messages sent to the host window, such as notifications from child controls. これには、2 つの方法があります。There are two ways to do this.

    • ホスティングクラスでメッセージを処理する場合は、 WndProc HwndHostクラスのメソッドをオーバーライドします。If you prefer to handle messages in your hosting class, override the WndProc method of the HwndHost class.

    • WPF でメッセージを処理する場合は、分離コードでHwndHostクラスMessageHookイベントを処理します。If you prefer to have the WPF handle the messages, handle the HwndHost class MessageHook event in your code-behind. このイベントは、ホストされているウィンドウが受信するすべてのメッセージに対して発生します。This event occurs for every message that is received by the hosted window. このオプションを選択した場合でも、 WndProcをオーバーライドする必要がありますが、必要なのは最小限の実装だけです。If you choose this option, you must still override WndProc, but you only need a minimal implementation.

  7. DestroyWindowCoreWndProcメソッドとメソッドをオーバーライドします。 HwndHostOverride the DestroyWindowCore and WndProc methods of HwndHost. HwndHostコントラクトを満たすには、これらのメソッドをオーバーライドする必要がありますが、必要なのは最小限の実装だけです。You must override these methods to satisfy the HwndHost contract, but you may only need to provide a minimal implementation.

  8. 分離コードファイルで、コントロールホストクラスのインスタンスを作成し、ウィンドウをホストするためのBorder要素の子にします。In your code-behind file, create an instance of the control hosting class and make it a child of the Border element that is intended to host the window.

  9. ホストされたウィンドウとの通信Microsoft WindowsMicrosoft Windowsには、メッセージを送信し、子ウィンドウからのメッセージを処理します。たとえば、コントロールによって送信された通知などです。Communicate with the hosted window by sending it Microsoft WindowsMicrosoft Windows messages and handling messages from its child windows, such as notifications sent by controls.

ページレイアウトを実装するImplement the Page Layout

ListBox コントロールをホストする WPF ページのレイアウトは、2つの領域で構成されます。The layout for the WPF page that hosts the ListBox Control consists of two regions. ページの左側は、Win32 コントロールを操作できるようにするユーザー インターフェイス (UI)user interface (UI)を提供するいくつかの WPF コントロールをホストします。The left side of the page hosts several WPF controls that provide a ユーザー インターフェイス (UI)user interface (UI) that allows you to manipulate the Win32 control. ページの右上隅には、ホストされた ListBox コントロールの四角形の領域があります。The upper right corner of the page has a square region for the hosted ListBox Control.

このレイアウトを実装するコードは非常に単純です。The code to implement this layout is quite simple. ルート要素は、 DockPanel 2 つの子要素を持つです。The root element is a DockPanel that has two child elements. 1つ目はBorder 、ListBox コントロールをホストする要素です。The first is a Border element that hosts the ListBox Control. ページの右上隅に 200 x 200 の四角形があります。It occupies a 200x200 square in the upper right corner of the page. 2つ目はStackPanel 、情報を表示し、公開されている相互運用プロパティを設定することによって ListBox コントロールを操作できるようにする、一連の WPF コントロールを含む要素です。The second is a StackPanel element that contains a set of WPF controls that display information and allow you to manipulate the ListBox Control by setting exposed interoperation properties. の子である各要素についてはStackPanel、これらの要素の詳細に使用されるさまざまな要素のリファレンス資料を参照してください。これらの要素の詳細については、以下のコード例に記載されていますが、ここでは説明しません (基本相互運用モデルには、これらの機能は必要ありません。サンプルにいくつかの対話機能を追加するために用意されています)。For each of the elements that are children of the StackPanel, see the reference material for the various elements used for details on what these elements are or what they do, these are listed in the example code below but will not be explained here (the basic interoperation model does not require any of them, they are provided to add some interactivity to the sample).

<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 コントロールをホストするクラスを実装するImplement a Class to Host the Microsoft Win32 Control

このサンプルの中核となるのは、ControlHost.cs コントロールを実際にホストするクラスです。The core of this sample is the class that actually hosts the control, ControlHost.cs. HwndHost から継承します。It inherits from HwndHost. コンストラクターは、2つのパラメーター (height と width) を受け取ります。このパラメーターはBorder 、ListBox コントロールをホストする要素の高さと幅に対応しています。The constructor takes two parameters, height and width, which correspond to the height and width of the Border element that hosts the ListBox control. これらの値は、コントロールのサイズが要素にBorder一致するように後で使用されます。These values are used later to ensure that the size of the control matches the Border element.

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

定数のセットもあります。There is also a set of constants. これらの定数は、ほとんどの場合 Winuser. h から取得され、Win32 関数を呼び出すときに従来の名前を使用できます。These constants are largely taken from Winuser.h, and allow you to use conventional names when calling Win32 functions.

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 ウィンドウを作成しますOverride BuildWindowCore to Create the Microsoft Win32 Window

このメソッドをオーバーライドして、ページによってホストされる Win32 ウィンドウを作成し、ウィンドウとページ間の接続を確立します。You override this method to create the Win32 window that will be hosted by the page, and make the connection between the window and the page. このサンプルには ListBox コントロールのホストが含まれているため、2つのウィンドウが作成されます。Because this sample involves hosting a ListBox Control, two windows are created. 1つ目は、WPF ページによって実際にホストされるウィンドウです。The first is the window that is actually hosted by the WPF page. ListBox コントロールは、そのウィンドウの子として作成されます。The ListBox Control is created as a child of that window.

この方法は、コントロールから通知を受信するプロセスを簡略化するためのものです。The reason for this approach is to simplify the process of receiving notifications from the control. HwndHostクラスを使用すると、ホストしているウィンドウに送信されたメッセージを処理できます。The HwndHost class allows you to process messages sent to the window that it is hosting. Win32 コントロールを直接ホストする場合は、コントロールの内部メッセージループに送信されたメッセージを受け取ります。If you host a Win32 control directly, you receive the messages sent to the internal message loop of the control. コントロールを表示してメッセージを送信することはできますが、コントロールから親ウィンドウに送信される通知は受け取りません。You can display the control and send it messages, but you do not receive the notifications that the control sends to its parent window. これは特に、ユーザーがコントロールと対話するときに検出する方法がないことを意味します。This means, among other things, that you have no way of detecting when the user interacts with the control. 代わりに、ホストウィンドウを作成し、そのウィンドウの子にコントロールを設定します。Instead, create a host window and make the control a child of that window. これにより、コントロールによって送信された通知を含め、ホストウィンドウのメッセージを処理できます。This allows you to process the messages for the host window including the notifications sent to it by the control. 便宜上、ホストウィンドウはコントロールの単純なラッパーよりもわずかであるため、パッケージは ListBox コントロールと呼ばれます。For convenience, since the host window is little more than a simple wrapper for the control, the package will be referred to as a ListBox control.

ホストウィンドウと ListBox コントロールの作成Create the Host Window and ListBox Control

PInvoke を使用して、ウィンドウクラスを作成して登録することで、コントロールのホストウィンドウを作成できます。You can use PInvoke to create a host window for the control by creating and registering a window class, and so on. ただし、はるかに簡単な方法は、定義済みの "静的" ウィンドウクラスを使用してウィンドウを作成することです。However, a much simpler approach is to create a window with the predefined "static" window class. これにより、コントロールからの通知を受信するために必要なウィンドウプロシージャが提供され、最小限のコーディングが必要になります。This provides you with the window procedure you need in order to receive notifications from the control, and requires minimal coding.

コントロールの HWND は、読み取り専用プロパティを通じて公開されます。これにより、ホストページは、このプロパティを使用して、コントロールにメッセージを送信できます。The HWND of the control is exposed through a read-only property, such that the host page can use it to send messages to the control.

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

ListBox コントロールは、ホストウィンドウの子として作成されます。The ListBox control is created as a child of the host window. 両方のウィンドウの高さと幅は、前に説明したように、コンストラクターに渡される値に設定されます。The height and width of both windows are set to the values passed to the constructor, discussed above. これにより、ホストウィンドウとコントロールのサイズは、ページ上の予約領域と同じになります。This ensures that the size of the host window and control is identical to the reserved area on the page. ウィンドウが作成された後、このサンプルHandleRefは、ホストウィンドウの HWND を含むオブジェクトを返します。After the windows are created, the sample returns a HandleRef object that contains the HWND of the host window.

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 を実装するImplement DestroyWindow and WndProc

BuildWindowCore加えて、のWndProc DestroyWindowCoreメソッドとメソッドもオーバーライドする必要があります。HwndHostIn addition to BuildWindowCore, you must also override the WndProc and DestroyWindowCore methods of the HwndHost. この例では、コントロールのメッセージがMessageHookハンドラーによって処理されるため、とWndProc DestroyWindowCoreの実装は最小限に抑えられます。In this example, the messages for the control are handled by the MessageHook handler, thus the implementation of WndProc and DestroyWindowCore is minimal. WndProc場合は、をにhandled false設定して、メッセージが処理されなかったことを示し、0を返します。In the case of WndProc, set handled to false to indicate that the message was not handled and return 0. DestroyWindowCore場合は、単にウィンドウを破棄します。For DestroyWindowCore, simply destroy the window.

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

ページ上のコントロールをホストするHost the Control on the Page

ページ上のコントロールをホストするには、最初にControlHostクラスの新しいインスタンスを作成します。To host the control on the page, you first create a new instance of the ControlHost class. コントロールを含む border 要素の高さと幅 (ControlHostElement) ControlHostをコンストラクターに渡します。Pass the height and width of the border element that contains the control (ControlHostElement) to the ControlHost constructor. これにより、ListBox のサイズが正しく設定されます。This ensures that the ListBox is sized correctly. 次に、 ControlHostオブジェクトをホストBorderChildプロパティに割り当てることによって、ページ上のコントロールをホストします。You then host the control on the page by assigning the ControlHost object to the Child property of the host Border.

このサンプルでは、のMessageHookイベントにハンドラーをアタッチして、 ControlHostコントロールからメッセージを受信します。The sample attaches a handler to the MessageHook event of the ControlHost to receive messages from the control. このイベントは、ホストされているウィンドウに送信されるすべてのメッセージに対して発生します。This event is raised for every message sent to the hosted window. この場合、コントロールからの通知など、実際の ListBox コントロールをラップするウィンドウに送信されるメッセージです。In this case, these are the messages sent to window that wraps the actual ListBox control, including notifications from the control. このサンプルでは、SendMessage を呼び出して、コントロールから情報を取得し、その内容を変更します。The sample calls SendMessage to obtain information from the control and modify its contents. ページがコントロールと通信する方法の詳細については、次のセクションで説明します。The details of how the page communicates with the control are discussed in the next section.

注意

SendMessage の PInvoke 宣言が2つあることに注意してください。Notice that there are two PInvoke declarations for SendMessage. これが必要になるのはwParam 、1つはパラメーターを使用して文字列を渡し、もう1つは、それを使用して整数を渡すためです。This is necessary because one uses the wParam parameter to pass a string and the other uses it to pass an integer. データが正しくマーシャリングされるようにするには、署名ごとに個別の宣言が必要です。You need a separate declaration for each signature to ensure that the data is marshaled correctly.

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

コントロールとページ間の通信を実装するImplement Communication Between the Control and the Page

コントロールを操作するには、Windows メッセージを送信します。You manipulate the control by sending it Windows messages. コントロールは、ユーザーがホストウィンドウに通知を送信することによって対話するときに通知します。The control notifies you when the user interacts with it by sending notifications to its host window. WPF での Win32 ListBox コントロールのホストサンプルに含まれる UI には、そのしくみの例がいくつか用意されています。The Hosting a Win32 ListBox Control in WPF sample includes a UI that provides several examples of how this works:

  • リストに項目を追加します。Append an item to the list.

  • 選択した項目を一覧から削除しますDelete the selected item from the list

  • 現在選択されている項目のテキストを表示します。Display the text of the currently selected item.

  • リスト内の項目の数を表示します。Display the number of items in the list.

ユーザーは、従来の Win32 アプリケーションの場合と同様に、リストボックス内の項目をクリックして選択することもできます。The user can also select an item in the list box by clicking on it, just as they would for a conventional Win32 application. 表示されるデータは、ユーザーが項目を選択、追加、または追加することによってリストボックスの状態を変更するたびに更新されます。The displayed data is updated each time the user changes the state of the list box by selecting, adding, or appending an item.

項目を追加するには、リストボックス LB_ADDSTRINGにメッセージを送信します。To append items, send the list box an LB_ADDSTRING message. 項目を削除するにLB_GETCURSELは、を送信して現在の選択範囲LB_DELETESTRINGのインデックスを取得し、その項目を削除します。To delete items, send LB_GETCURSEL to get the index of the current selection and then LB_DELETESTRING to delete the item. また、このサンプルLB_GETCOUNTはを送信し、返された値を使用して、項目数を示す表示を更新します。The sample also sends LB_GETCOUNT, and uses the returned value to update the display that shows the number of items. のこれらのSendMessageインスタンスはどちらも、前のセクションで説明した PInvoke 宣言のいずれかを使用します。Both these instances of SendMessage use one of the PInvoke declarations discussed in the previous section.

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

ユーザーが項目を選択するか、項目の選択内容を変更すると、コントロールはそのMessageHook WM_COMMAND ページにメッセージを送信してホストウィンドウに通知します。これにより、ページのイベントが発生します。When the user selects an item or changes their selection, the control notifies the host window by sending it a WM_COMMAND message, which raises the MessageHook event for the page. ハンドラーは、ホストウィンドウのメインウィンドウプロシージャと同じ情報を受け取ります。The handler receives the same information as the main window procedure of the host window. また、 handledブール値への参照も渡します。It also passes a reference to a Boolean value, handled. をにhandled true設定すると、メッセージを処理したことを示し、それ以上の処理は必要ありません。You set handled to true to indicate that you have handled the message and no further processing is needed.

WM_COMMANDはさまざまな理由で送信されるため、通知 ID を調べて、処理するイベントであるかどうかを確認する必要があります。WM_COMMAND is sent for a variety of reasons, so you must examine the notification ID to determine whether it is an event that you wish to handle. ID は、 wParamパラメーターの上位ワードに含まれています。The ID is contained in the high word of the wParam parameter. このサンプルでは、ビットごとの演算子を使用して ID を抽出します。The sample uses bitwise operators to extract the ID. ユーザーが選択内容を変更した場合、ID LBN_SELCHANGEはになります。If the user has made or changed their selection, the ID will be LBN_SELCHANGE.

LBN_SELCHANGE受信されると、サンプルはコントロールに LB_GETCURSELメッセージを送信して、選択された項目のインデックスを取得します。When LBN_SELCHANGE is received, the sample gets the index of the selected item by sending the control a LB_GETCURSEL message. テキストを取得するには、まずをStringBuilder作成します。To get the text, you first create a StringBuilder. 次に、コントロールを LB_GETTEXTメッセージとして送信します。You then send the control an LB_GETTEXT message. StringBuilderのオブジェクトをwParamパラメーターとして渡します。Pass the empty StringBuilder object as the wParam parameter. SendMessageを返しStringBuilderた場合、には選択した項目のテキストが格納されます。When SendMessage returns, the StringBuilder will contain the text of the selected item. このの使用SendMessageには、もう1つの PInvoke 宣言が必要です。This use of SendMessage requires yet another PInvoke declaration.

最後に、 handledtrueに設定して、メッセージが処理されたことを示します。Finally, set handled to true to indicate that the message has been handled.

関連項目See also