チュートリアル: WPF で Win32 コントロールをホストする

Windows Presentation Foundation (WPF) では、アプリケーションを作成するための充実した環境が提供されます。 ただし、Win32 コードにかなりの投資がある場合は、それを完全に書き換えるより、そのコードの一部でも WPF アプリケーションで再利用する方が効率的な場合があります。 WPF には、WPF ページで Win32 ウィンドウをホストするための簡単なメカニズムがあります。

このトピックでは、Win32 のリスト ボックス コントロールをホストするアプリケーション (WPF で Win32 の ListBox コントロールをホストするサンプル) について説明します。 この一般的な手順を拡張し、任意の Win32 ウィンドウをホストできます。

必要条件

このトピックは、WPF と Windows API 両方のプログラミングについての基本的な知識があることを前提としています。 WPF のプログラミングの基本的な概要については、概要に関する記事を参照してください。 Windows API のプログラミングの概要については、この主題に関する数多くの書籍 (特に Charles Petzold の『Programming Windows (プログラミング Windows) 』) を参照してください。

このトピックに付属するサンプルは C# で実装されているため、Windows API へのアクセスにはプラットフォーム呼び出しサービス (PInvoke) が使用されています。 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. 子コントロールからの通知など、ホスト ウィンドウに送信されるように選択されたメッセージを処理します。 これには、2 つの方法があります。

    • ホスティング クラスでメッセージを処理したい場合は、HwndHost クラスの WndProc メソッドをオーバーライドします。

    • WPF でメッセージを処理したい場合は、コードビハインドで HwndHost クラスの MessageHook イベントを処理します。 このイベントは、ホストされているウィンドウが受信するすべてのメッセージに対して発生します。 このオプションを選択した場合でも、WndProc をオーバーライドする必要がありますが、最低限の実装のみが必要です。

  7. HwndHostDestroyWindowCore メソッドと WndProc メソッドをオーバーライドします。 HwndHost のコントラクトを満たすようにこれらのメソッドをオーバーライドする必要がありますが、必要なのは最小限の実装だけです。

  8. 分離コード ファイルで、コントロール ホスティング クラスのインスタンスを作成し、ウィンドウをホストするための Border 要素の子にします。

  9. Microsoft Windows メッセージを送信し、子ウィンドウからのメッセージ (コントロールによって送信された通知など) を処理することによって、ホストされたウィンドウと通信します。

ページ レイアウトを実装する

ListBox コントロールをホストする WPF ページのレイアウトは、2 つの領域で構成されます。 ページの左側では、Win32 コントロールを操作するためのユーザー インターフェイス (UI) を提供するいくつかの WPF コントロールがホストされています。 ページの右上隅には、ホストされた ListBox コントロールのための四角形の領域があります。

このレイアウトを実装するコードは非常に単純です。 ルート要素は、2 つの子要素を持つ DockPanel です。 1 つ目は、ListBox コントロールをホストする Border 要素です。 ページの右上隅の 200 x 200 の正方形を占めます。 2 つ目は StackPanel 要素で、情報を表示し、公開される相互運用プロパティを設定することによって ListBox コントロールを操作できる、一連の WPF コントロールが含まれます。 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 から継承します。 コンストラクターが受け取る 2 つのパラメーター height と width は、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 コントロールのホスティングが含まれているため、2 つのウィンドウを作成します。 1 つ目は、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 コントロールは、ホスト ウィンドウの子として作成されます。 両方のウィンドウの高さと幅は、前に説明したように、コンストラクターに渡される値に設定されます。 これにより、ホスト ウィンドウとコントロールのサイズは、ページ上の予約領域と同じになります。 ウィンドウが作成された後、サンプルではホスト ウィンドウの HWND が含まれる HandleRef オブジェクトが返されます。

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 に加えて、HwndHostWndProc メソッドと DestroyWindowCore メソッドもオーバーライドする必要があります。 この例では、コントロールへのメッセージは MessageHook ハンドラーによって処理されるため、WndProcDestroyWindowCore の実装は最小限に抑えられます。 WndProc の場合は、handledfalse に設定して、メッセージが処理されなかったことを示し、0 を返します。 DestroyWindowCore の場合は、単にウィンドウを破棄します。

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  handled = false;
  return IntPtr.Zero;
}

protected override void DestroyWindowCore(HandleRef hwnd)
{
  DestroyWindow(hwnd.Handle);
}
Protected Overrides Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
  handled = False
  Return IntPtr.Zero
End Function

Protected Overrides Sub DestroyWindowCore(ByVal hwnd As HandleRef)
  DestroyWindow(hwnd.Handle)
End Sub
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
<DllImport("user32.dll", EntryPoint := "DestroyWindow", CharSet := CharSet.Unicode)>
Friend Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean
End Function

ページでコントロールをホストする

ページでコントロールをホストするには、最初に ControlHost クラスの新しいインスタンスを作成します。 コントロールを含む境界要素の高さと幅 (ControlHostElement) を、ControlHost コンストラクターに渡します。 これにより、ListBox のサイズが正しく設定されます。 次に、ホスト BorderChild プロパティに ControlHost オブジェクトを割り当てることによって、ページでコントロールをホストします。

このサンプルでは、コントロールからメッセージを受け取るため、ControlHostMessageHook イベントにハンドラーをアタッチします。 このイベントは、ホストされているウィンドウに送られるすべてのメッセージで発生します。 この場合は、コントロールからの通知など、実際の ListBox コントロールをラップするウィンドウに送られるメッセージです。 サンプルでは、SendMessage を呼び出して、コントロールから情報を取得し、その内容を変更します。 ページがコントロールと通信する方法の詳細については、次のセクションで説明します。

注意

SendMessage に対して PInvoke の宣言が 2 つあることに注意してください。 1 つでは wParam パラメーターを使用して文字列を渡し、もう 1 つではそれを使用して整数を渡すため、このようにする必要があります。 データが正しくマーシャリングされるようにするには、シグネチャごとに個別の宣言が必要です。

public partial class HostWindow : Window
{
int selectedItem;
IntPtr hwndListBox;
ControlHost listControl;
Application app;
Window myWindow;
int itemCount;

private void On_UIReady(object sender, EventArgs e)
{
  app = System.Windows.Application.Current;
  myWindow = app.MainWindow;
  myWindow.SizeToContent = SizeToContent.WidthAndHeight;
  listControl = new ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth);
  ControlHostElement.Child = listControl;
  listControl.MessageHook += new HwndSourceHook(ControlMsgFilter);
  hwndListBox = listControl.hwndListBox;
  for (int i = 0; i < 15; i++) //populate listbox
  {
    string itemText = "Item" + i.ToString();
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" +  itemCount.ToString();
}
Partial Public Class HostWindow
    Inherits Window
    Private selectedItem As Integer
    Private hwndListBox As IntPtr
    Private listControl As ControlHost
    Private app As Application
    Private myWindow As Window
    Private itemCount As Integer

    Private Sub On_UIReady(ByVal sender As Object, ByVal e As EventArgs)
        app = System.Windows.Application.Current
        myWindow = app.MainWindow
        myWindow.SizeToContent = SizeToContent.WidthAndHeight
        listControl = New ControlHost(ControlHostElement.ActualHeight, ControlHostElement.ActualWidth)
        ControlHostElement.Child = listControl
        AddHandler listControl.MessageHook, AddressOf ControlMsgFilter
        hwndListBox = listControl.hwndListBox
        For i As Integer = 0 To 14 'populate listbox
            Dim itemText As String = "Item" & i.ToString()
            SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, itemText)
        Next i
        itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
        numItems.Text = "" & itemCount.ToString()
    End Sub

private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  int textLength;

  handled = false;
  if (msg == WM_COMMAND)
  {
    switch ((uint)wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
    {
      case LBN_SELCHANGE : //Get the item text and display it
        selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
        textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero);
        StringBuilder itemText = new StringBuilder();
        SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText);
        selectedText.Text = itemText.ToString();
        handled = true;
        break;
    }
  }
  return IntPtr.Zero;
}
internal const int
  LBN_SELCHANGE = 0x00000001,
  WM_COMMAND = 0x00000111,
  LB_GETCURSEL = 0x00000188,
  LB_GETTEXTLEN = 0x0000018A,
  LB_ADDSTRING = 0x00000180,
  LB_GETTEXT = 0x00000189,
  LB_DELETESTRING = 0x00000182,
  LB_GETCOUNT = 0x0000018B;

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       IntPtr wParam,
                                       IntPtr lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
                                       int msg,
                                       int wParam,
                                       [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
                                          int msg,
                                          IntPtr wParam,
                                          String lParam);

Private Function ControlMsgFilter(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    Dim textLength As Integer

    handled = False
    If msg = WM_COMMAND Then
        Select Case CUInt(wParam.ToInt32()) >> 16 And &HFFFF 'extract the HIWORD
            Case LBN_SELCHANGE 'Get the item text and display it
                selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
                textLength = SendMessage(listControl.hwndListBox, LB_GETTEXTLEN, IntPtr.Zero, IntPtr.Zero)
                Dim itemText As New StringBuilder()
                SendMessage(hwndListBox, LB_GETTEXT, selectedItem, itemText)
                selectedText.Text = itemText.ToString()
                handled = True
        End Select
    End If
    Return IntPtr.Zero
End Function
Friend Const LBN_SELCHANGE As Integer = &H1, WM_COMMAND As Integer = &H111, LB_GETCURSEL As Integer = &H188, LB_GETTEXTLEN As Integer = &H18A, LB_ADDSTRING As Integer = &H180, LB_GETTEXT As Integer = &H189, LB_DELETESTRING As Integer = &H182, LB_GETCOUNT As Integer = &H18B

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As StringBuilder) As Integer
End Function

<DllImport("user32.dll", EntryPoint:="SendMessage", CharSet:=CharSet.Unicode)>
Friend Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function

コントロールとページの間の通信を実装する

Windows メッセージをコントロールに送信して、コントロールを操作します。 ユーザーがコントロールを操作すると、コントロールからホスト ウィンドウに通知が送られます。 WPF で Win32 の ListBox コントロールをホストするサンプルには、このしくみのいくつかの例を示す UI が含まれています。

  • リストに項目を追加します。

  • 選択した項目をリストから削除します

  • 現在選択されている項目のテキストを表示します。

  • リスト内の項目の数を表示します。

ユーザーは、従来の Win32 アプリケーションの場合と同様に、リスト ボックス内の項目をクリックして選択することもできます。 表示されるデータは、ユーザーが項目を選択したり追加したりすることによってリスト ボックスの状態を変更するたびに、更新されます。

項目を追加するには、リスト ボックスに LB_ADDSTRING メッセージを送信します。 項目を削除するには、LB_GETCURSEL を送信して現在選択されている項目のインデックスを取得した後、LB_DELETESTRING を送信して項目を削除します。 サンプルでは、LB_GETCOUNT も送信され、返された値を使用して項目数を示す表示が更新されます。 SendMessage のどちらのインスタンスでも、前のセクションで説明した PInvoke の宣言のいずれかが使用されます。

private void AppendText(object sender, EventArgs args)
{
  if (!string.IsNullOrEmpty(txtAppend.Text))
  {
    SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
private void DeleteText(object sender, EventArgs args)
{
  selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero);
  if (selectedItem != -1) //check for selected item
  {
    SendMessage(hwndListBox, LB_DELETESTRING, (IntPtr)selectedItem, IntPtr.Zero);
  }
  itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero);
  numItems.Text = "" + itemCount.ToString();
}
Private Sub AppendText(ByVal sender As Object, ByVal args As EventArgs)
    If txtAppend.Text <> String.Empty Then
        SendMessage(hwndListBox, LB_ADDSTRING, IntPtr.Zero, txtAppend.Text)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub
Private Sub DeleteText(ByVal sender As Object, ByVal args As EventArgs)
    selectedItem = SendMessage(listControl.hwndListBox, LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero)
    If selectedItem <> -1 Then 'check for selected item
        SendMessage(hwndListBox, LB_DELETESTRING, New IntPtr(selectedItem), IntPtr.Zero)
    End If
    itemCount = SendMessage(hwndListBox, LB_GETCOUNT, IntPtr.Zero, IntPtr.Zero)
    numItems.Text = "" & itemCount.ToString()
End Sub

ユーザーが項目を選択するか、選択を変更すると、コントロールによって WM_COMMAND メッセージが送信されてホスト ウィンドウに通知され、これによりページに対する MessageHook イベントが発生します。 ハンドラーは、ホスト ウィンドウのメイン ウィンドウ プロシージャと同じ情報を受け取ります。 また、ブール値 handled への参照も渡されます。 メッセージの処理が済み、それ以上の処理は必要ないことを示すには、handledtrue に設定します。

WM_COMMAND はさまざまな理由で送信されるため、通知 ID を調べて、処理する必要のあるイベントかどうかを確認する必要があります。 ID は、wParam パラメーターの上位ワードに格納されています。 サンプルでは、ビットごとの演算子を使用して ID が抽出されます。 ユーザーが選択を行うか変更した場合、ID は LBN_SELCHANGE になります。

LBN_SELCHANGE を受け取ると、サンプルでは、コントロールに LB_GETCURSEL メッセージを送信することによって、選択された項目のインデックスが取得されます。 テキストを取得するには、最初に StringBuilder を作成します。 その後、コントロールに LB_GETTEXT メッセージを送信します。 wParam パラメーターとして空の StringBuilder オブジェクトを渡します。 SendMessage から戻った時点で、StringBuilder には選択された項目のテキストが格納されています。 SendMessage をこのように使用するには、PInvoke の宣言がもう 1 つ必要です。

最後に、handledtrue に設定して、メッセージが処理されたことを示します。

関連項目