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

Windows Presentation Foundation (WPF) は、アプリケーションの作成に適した環境を提供します。 ただし、Win32 のコードに多くの投資を行った場合は、元のコードを全面的に記述し直すよりも、WPF アプリケーションのコードの少なくとも一部を再利用する方が効率的である場合があります。 WPF には、WPF ページで Win32 ウィンドウをホストするためのわかりやすいメカニズムが用意されています。

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

このトピックは、次のセクションで構成されています。

  • 要件
  • 基本手順
  • ページ レイアウトの実装
  • Microsoft Win32 コントロールをホストするクラスの実装
  • ページでのコントロールのホスト
  • コントロールとページ間の通信の実装
  • 関連トピック

要件

このトピックは、WPF および Win32 のプログラミングに関する基本的な知識があることを前提としています。 WPF プログラミングの概要については、「概要 (WPF)」を参照してください。 Win32 プログラミングの概要については多数の書籍が出版されているので、それらを参照してください。特に、『プログラミング Windows』(Charles Petzold 著) が参考になります。

このトピックに含まれるサンプルは C# で実装されるので、Platform Invocation Services (PInvoke) を利用して、Win32 API にアクセスします。 PInvoke の知識は役立ちますが、必須ではありません。

メモメモ

このトピックには、関連するサンプルからのコード例が数多く含まれています。ただし、読みやすさのために、完全なサンプル コードは含まれていません。コード全体については、WPF での Win32 ListBox コントロールのホストのサンプルを参照してください。

基本手順

ここでは、WPF ページで Win32 ウィンドウをホストするための基本手順を説明します。 残りのセクションでは、各手順の詳細について説明します。

基本的なホスト手順は次のとおりです。

  1. ウィンドウをホストする WPF ページを実装します。 1 つの方法は、Border 要素を作成し、ホストされるウィンドウのページのセクションを予約することです。

  2. HwndHost から継承するコントロールをホストするクラスを実装します。

  3. そのクラスで、HwndHost クラス メンバーの BuildWindowCore をオーバーライドします。

  4. ホストされるウィンドウを、WPF ページを含むウィンドウの子として作成します。 従来の WPF プログラミングでは明示的に利用する必要はありませんが、ホストするページは、ハンドル (HWND) を持つウィンドウです。 ページの HWND は、BuildWindowCore メソッドの hwndParent パラメーターを通じて受け取ります。 ホストされるウィンドウは、この HWND の子として作成する必要があります。

  5. ホスト ウィンドウを作成したら、ホストされるウィンドウの HWND を返します。 1 つ以上の Win32 コントロールをホストする場合、通常は、ホスト ウィンドウを HWND の子として作成し、コントロールをそのホスト ウィンドウの子にします。 複数のコントロールを 1 つのホスト ウィンドウにラップすると、WPF ページでそれらのコントロールから通知を受信することが簡素化され、HWND 境界を越えた通知に関する Win32 での問題を処理できます。

  6. 子コントロールからの通知など、ホスト ウィンドウに送信されたメッセージを選択して処理します。 これには、2 つの方法があります。

    • ホストするクラスでメッセージを処理する場合は、HwndHost クラスの WndProc メソッドをオーバーライドします。

    • WPF でメッセージを処理する場合は、分離コードで HwndHost クラスの MessageHook イベントを処理します。 このイベントは、ホストされるウィンドウで受信されるメッセージごとに発生します。 このオプションを選択する場合でも WndProc をオーバーライドする必要がありますが、最小限の実装だけで十分です。

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

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

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

ページ レイアウトの実装

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

このレイアウトを実装するコードは、非常に単純です。 ルート要素は、2 つの子要素を持つ DockPanel です。 1 つ目は、ListBox コントロールをホストする Border 要素です。 これは、ページの右上隅の 200 × 200 の正方形部分を使用します。 2 つ目は、情報を表示したり、公開されている相互運用プロパティを設定して ListBox コントロールを操作できる一連の WPF コントロールを含む StackPanel 要素です。 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 から継承されます。 コンストラクターは、高さと幅の 2 つのパラメーターを使用します。これらのパラメーターは、ListBox コントロールをホストする Border 要素の高さと幅に対応します。 これらの値を後から使用して、コントロールのサイズが 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;

Microsoft Win32 ウィンドウを作成するための BuildWindowCore のオーバーライド

このメソッドをオーバーライドして、ページでホストされる Win32 ウィンドウを作成し、ウィンドウとページの間を接続します。 このサンプルでは ListBox コントロールをホストするため、2 つのウィンドウが作成されます。 1 つは、WPF ページで実際にホストされるウィンドウです。 ListBox コントロールは、そのウィンドウの子として作成されます。

この方法を使用するのは、コントロールから通知を受信するプロセスを簡単にするためです。 HwndHost クラスを使用することにより、ホストしているウィンドウに送信されるメッセージを処理できます。 Win32 コントロールを直接ホストする場合は、コントロールの内部メッセージ ループに送信されたメッセージを受信します。 コントロールを表示してそのコントロールにメッセージを送信できますが、コントロールがその親ウィンドウに送信する通知は受信されません。 これが意味することはいくつかありますが、その 1 つは、ユーザーがコントロールと対話していることを検出する手段がないことです。 そのため、代わりにホスト ウィンドウを作成し、コントロールをそのウィンドウの子にします。 この方法を使用すると、コントロールがホスト ウィンドウに対して送信する通知を含め、ホスト ウィンドウのメッセージを処理できます。 ホスト ウィンドウは、コントロールの単純なラッパーとほとんど変わりないため、便宜上、パッケージは ListBox コントロールとして参照されます。

ホスト ウィンドウと ListBox コントロールの作成

PInvoke を使用して、ウィンドウ クラスを作成、登録するなどの方法で、コントロールのホスト ウィンドウを作成できます。 ただし、定義済みの "静的" ウィンドウ クラスを使用してウィンドウを作成した方がはるかに簡単です。 この方法では、コントロールから通知を受信するために必要なウィンドウ プロシージャが提供されるので、最小限のコーディングだけで済みます。

コントロールの HWND は読み取り専用プロパティを通じて公開されるため、ホスト ページは HWND を使用してコントロールにメッセージを送信できます。

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

ListBox コントロールは、ホスト ウィンドウの子として作成されます。 両方のウィンドウの高さと幅は、既に説明したように、コンストラクターに渡される値に設定されます。 このため、ホスト ウィンドウとコントロールのサイズは、ページ上の予約された領域と同じになります。 ウィンドウが作成されると、このサンプルでは、ホスト ウィンドウの HWND を含む HandleRef オブジェクトが返されます。

    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 に加え、HwndHostWndProc メソッドと DestroyWindowCore メソッドもオーバーライドする必要があります。 この例では、コントロールのメッセージが MessageHook ハンドラーで処理されるため、少なくとも WndProcDestroyWindowCore の実装が必要です。 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 のサイズが正しく設定されます。 次に、ホストの BorderChild プロパティに ControlHost オブジェクトを割り当てて、ページ上のコントロールをホストします。

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

メモメモ

SendMessage には 2 つのPInvoke 宣言があります。2 つ必要なのは、一方は 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 メッセージを送信して、コントロールを操作します。 コントロールは、そのホスト ウィンドウに通知を送信することで、ユーザーがコントロールと対話したことを通知します。 WPF での Win32 ListBox コントロールのホストのサンプルには、この動作の次のようないくつかの例を示す UI が含まれています。

  • リストへの項目の追加

  • リストからの選択した項目の削除

  • 現在選択されている項目のテキストの表示

  • リスト内の項目数の表示

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

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

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 はさまざまな理由で送信されるため、通知 ID を調べて、処理するイベントかどうかを判断する必要があります。 ID は、wParam パラメーターの上位ワードに格納されています。 Microsoft .NET には HIWORD マクロがないため、このサンプルではビット単位の演算子を使用して ID を抽出します。 ユーザーが選択や選択の変更を行うと、ID は LBN_SELCHANGE になります。

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

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

参照

参照

HwndHost

概念

WPF と Win32 の相互運用性

チュートリアル: WPF の概要