Bagikan melalui


Panduan: Menghosting Kontrol Win32 di WPF

Windows Presentation Foundation (WPF) menyediakan lingkungan yang kaya untuk membuat aplikasi. Namun, ketika Anda memiliki investasi besar dalam kode Win32, mungkin lebih efektif untuk menggunakan kembali setidaknya beberapa kode itu di aplikasi WPF Anda daripada menulis ulang sepenuhnya. WPF menyediakan mekanisme langsung untuk menghosting jendela Win32, pada halaman WPF.

Topik ini memandu Anda melalui aplikasi, Hosting Kontrol Kotak Daftar Win32 di Sampel WPF, yang menghosting kontrol kotak daftar Win32. Prosedur umum ini dapat diperluas untuk menghosting jendela Win32 apa pun.

Persyaratan

Topik ini mengasumsikan keakraban dasar dengan pemrograman WPF dan Windows API. Untuk pengenalan dasar pemrograman WPF, lihat Memulai. Untuk pengenalan pemrograman Windows API, lihat salah satu dari banyak buku tentang subjek, khususnya Pemrograman Windows oleh Charles Petzold.

Karena sampel yang menyertai topik ini diimplementasikan dalam C#, sampel ini menggunakan Platform Invocation Services (PInvoke) untuk mengakses Windows API. Beberapa keakraban dengan PInvoke sangat membantu tetapi tidak penting.

Nota

Topik ini mencakup sejumlah contoh kode dari sampel terkait. Namun, untuk keterbacaan, kode sampel lengkap tidak disertakan. Anda dapat memperoleh atau melihat kode lengkap dari Hosting Kotak Daftar Kontrol Win32 dalam Sampel WPF.

Prosedur Dasar

Bagian ini menguraikan prosedur dasar untuk menghosting jendela Win32 di halaman WPF. Bagian yang tersisa membahas detail dari setiap langkah.

Prosedur hosting dasar adalah:

  1. Terapkan halaman WPF untuk menghosting jendela. Salah satu tekniknya adalah membuat elemen Border untuk menyisihkan salah satu bagian halaman bagi jendela yang dihosting.

  2. Terapkan kelas untuk menghosting kontrol yang mewarisi dari HwndHost.

  3. Di kelas itu, ambil alih anggota kelas HwndHostBuildWindowCore.

  4. Buat jendela yang dihosting sebagai anak jendela yang berisi halaman WPF. Meskipun pemrograman WPF konvensional tidak perlu secara eksplisit menggunakannya, halaman hosting adalah sebuah jendela dengan handle (HWND). Anda menerima HWND jendela halaman melalui parameter hwndParent dari metode BuildWindowCore. Jendela yang dihosting harus dibuat sebagai anak dari HWND ini.

  5. Setelah Anda membuat jendela host, kembalikan HWND dari jendela yang dihosting. Jika Anda ingin menghosting satu atau beberapa kontrol Win32, Anda biasanya membuat jendela host sebagai anak dari HWND dan membuat kontrol-kontrol tersebut sebagai anak dari jendela host itu. Mengemas kontrol dalam jendela host menyediakan cara yang mudah bagi halaman WPF Anda untuk menerima pemberitahuan dari kontrol tersebut, yang mengatasi beberapa masalah khusus Win32 terkait dengan pemberitahuan di sepanjang batas HWND.

  6. Menangani pesan terpilih yang dikirim ke jendela host, seperti pemberitahuan dari kontrol bawaan. Ada dua cara untuk melakukan ini.

    • Jika Anda lebih suka menangani pesan di kelas hosting Anda, timpa metode WndProc di kelas HwndHost.

    • Jika Anda lebih suka WPF menangani pesan, tangani peristiwa HwndHost kelas MessageHook dalam kode-behind Anda. Kejadian ini terjadi untuk setiap pesan yang diterima oleh jendela yang dihosting. Jika Anda memilih opsi ini, Anda masih harus mengambil alih WndProc, tetapi Anda hanya memerlukan implementasi minimal.

  7. Gantikan metode DestroyWindowCore dan WndProc dalam HwndHost. Anda harus mengambil alih metode ini untuk memenuhi kontrak HwndHost, tetapi Anda mungkin hanya perlu memberikan implementasi minimal.

  8. Dalam file code-behind Anda, buat instans kelas hosting kontrol dan jadikan sebagai turunan dari elemen Border yang dimaksudkan untuk menghosting jendela aplikasi.

  9. Berkomunikasi dengan jendela yang dihosting dengan mengirimkan pesan dari Microsoft Windows dan menangani pesan dari jendela anak, seperti pemberitahuan yang dikirimkan oleh kontrol.

Menerapkan Tata Letak Halaman

Tata letak untuk halaman WPF yang menghosting Kontrol ListBox terdiri dari dua wilayah. Sisi kiri halaman menghosting beberapa kontrol WPF yang menyediakan antarmuka pengguna (UI) yang memungkinkan Anda memanipulasi kontrol Win32. Sudut kanan atas halaman memiliki wilayah persegi untuk Kontrol Kotak Daftar yang dihosting.

Kode untuk mengimplementasikan tata letak ini cukup sederhana. Elemen root adalah DockPanel yang memiliki dua elemen anak. Yang pertama adalah elemen Border yang menghosting Kontrol ListBox. Ini menempati persegi 200x200 di sudut kanan atas halaman. Yang kedua adalah elemen StackPanel yang berisi sekumpulan kontrol WPF yang menampilkan informasi dan memungkinkan Anda memanipulasi Kontrol ListBox dengan mengatur properti interoperabilitas yang diekspos. Untuk setiap elemen yang merupakan anak dari StackPanel, lihat materi referensi untuk berbagai elemen yang digunakan untuk detail tentang apa elemen-elemen ini atau apa yang mereka lakukan, ini tercantum dalam contoh kode di bawah ini tetapi tidak akan dijelaskan di sini (model interoperaksi dasar tidak memerlukan salah satunya, mereka disediakan untuk menambahkan beberapa interaktivitas ke sampel).

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

Menerapkan Kelas untuk Menghosting Kontrol Microsoft Win32

Inti dari sampel ini adalah kelas yang benar-benar menghosting kontrol, ControlHost.cs. Ini mewarisi dari HwndHost. Konstruktor mengambil dua parameter, tinggi dan lebar, yang sesuai dengan tinggi dan lebar elemen Border yang menghosting kontrol ListBox. Nilai-nilai ini digunakan nanti untuk memastikan bahwa ukuran kontrol cocok dengan elemen 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

Ada juga satu set konstanta. Konstanta ini sebagian besar diambil dari Winuser.h, dan memungkinkan Anda menggunakan nama konvensional saat memanggil fungsi 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

Mengganti BuildWindowCore untuk Membuat Jendela Microsoft Win32

Anda mengganti implementasi metode ini untuk membuat jendela Win32 yang akan dihosting oleh halaman dan membuat koneksi antara jendela dan halaman. Karena sampel ini melibatkan hosting Kontrol ListBox, dua jendela dibuat. Yang pertama adalah jendela yang sebenarnya dikelola oleh halaman WPF. Kontrol ListBox dibuat sebagai sub-objek dari jendela tersebut.

Alasan pendekatan ini adalah untuk menyederhanakan proses penerimaan pemberitahuan dari kontrol. Kelas HwndHost memungkinkan Anda memproses pesan yang dikirim ke jendela yang di-hosting oleh kelas tersebut. Jika Anda menghosting kontrol Win32 secara langsung, Anda menerima pesan yang dikirim ke loop pesan internal dari kontrol. Anda dapat menampilkan kontrol dan mengirim pesan, tetapi Anda tidak menerima pemberitahuan yang dikirim kontrol ke jendela induknya. Ini berarti, antara lain, bahwa Anda tidak memiliki cara untuk mendeteksi ketika pengguna berinteraksi dengan kontrol. Sebagai gantinya, buat jendela host dan jadikan kontrol sebagai anak dari jendela tersebut. Ini memungkinkan Anda memproses pesan untuk jendela host termasuk pemberitahuan yang dikirim ke dalamnya oleh kontrol. Untuk kenyamanan, karena jendela induk hanyalah pembungkus sederhana untuk kontrol, paket ini akan disebut sebagai kontrol ListBox.

Membuat Jendela Host dan Kontrol Kotak Daftar

Anda dapat menggunakan PInvoke untuk membuat jendela host untuk kontrol dengan membuat dan mendaftarkan kelas jendela, dan sebagainya. Namun, pendekatan yang jauh lebih sederhana adalah membuat jendela dengan kelas jendela "statis" yang telah ditentukan sebelumnya. Ini memberi Anda prosedur jendela yang Anda butuhkan untuk menerima pemberitahuan dari pengendali, dan hanya memerlukan sedikit pengodean.

HWND kontrol diekspos melalui properti baca-saja, sehingga halaman host dapat menggunakannya untuk mengirim pesan ke kontrol.

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

Kontrol ListBox dibuat sebagai turunan dari jendela host. Tinggi dan lebar dari kedua jendela diatur sesuai dengan nilai yang diteruskan ke konstruktor, seperti telah dibahas sebelumnya. Ini memastikan bahwa ukuran jendela host dan elemen kontrol identik dengan area yang dicadangkan di halaman. Setelah jendela dibuat, sampel mengembalikan objek HandleRef yang berisi HWND jendela host.

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

Menerapkan DestroyWindow dan WndProc

Selain BuildWindowCore, Anda juga harus mengganti metode WndProc dan DestroyWindowCore dari HwndHost. Dalam contoh ini, pesan untuk kontrol ditangani oleh handler MessageHook, sehingga implementasi WndProc dan DestroyWindowCore minimal. Dalam kasus WndProc, atur handled ke false untuk menunjukkan bahwa pesan tidak ditangani dan dikembalikan 0. Untuk DestroyWindowCore, cukup hancurkan jendela.

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

Letakkan Elemen Kontrol di Halaman

Untuk menghosting kontrol di halaman, Anda terlebih dahulu membuat instans baru dari kelas ControlHost. Teruskan tinggi dan lebar elemen batas yang berisi kontrol (ControlHostElement) ke konstruktor ControlHost. Ini memastikan bahwa ListBox berukuran benar. Anda kemudian menempatkan kontrol di halaman dengan menetapkan objek ControlHost ke properti Child host Border.

Sampel melampirkan handler ke event MessageHook dari ControlHost untuk menerima pesan dari elemen kendali. Pernyataan ini dihasilkan untuk setiap pesan yang dikirim ke jendela yang dihosting. Dalam hal ini, pesan-pesan tersebut dikirim ke jendela yang melingkupi pengendali ListBox aktual, termasuk pemberitahuan dari pengendali. Sampel memanggil SendMessage untuk mendapatkan informasi dari kontrol dan mengubah kontennya. Detail bagaimana halaman berkomunikasi dengan kontrol dibahas di bagian berikutnya.

Nota

Perhatikan bahwa ada dua deklarasi PInvoke untuk SendMessage. Ini diperlukan karena seseorang menggunakan parameter wParam untuk meneruskan string dan yang lain menggunakannya untuk meneruskan bilangan bulat. Anda memerlukan deklarasi terpisah untuk setiap penandatanganan untuk memastikan bahwa data diatur dengan benar.

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

Menerapkan Komunikasi Antara Kontrol dan Halaman

Anda memanipulasi kontrol dengan mengiriminya pesan Windows. Kontrol memberi tahu Anda ketika pengguna berinteraksi dengannya dengan mengirim pemberitahuan ke jendela host-nya. Sampel Hosting Win32 ListBox Control di WPF menyertakan UI yang menyediakan beberapa contoh cara kerjanya:

  • Tambahkan item ke daftar.

  • Menghapus item terpilih dari daftar

  • Tampilkan teks item terpilih saat ini.

  • Tampilkan jumlah item dalam daftar.

Pengguna juga dapat memilih item dalam kotak daftar dengan mengkliknya, seperti yang mereka lakukan untuk aplikasi Win32 konvensional. Data yang ditampilkan diperbarui setiap kali pengguna mengubah status kotak daftar dengan memilih, menambahkan, atau menambahkan item.

Untuk menambahkan item, kirimkan pesan ke kotak daftar dengan LB_ADDSTRING. Untuk menghapus item, kirim LB_GETCURSEL untuk mendapatkan indeks pilihan saat ini lalu LB_DELETESTRING untuk menghapus item. Sampel juga mengirim LB_GETCOUNT, dan menggunakan nilai yang dikembalikan untuk memperbarui tampilan yang menunjukkan jumlah item. Kedua contoh SendMessage yang ini menggunakan salah satu deklarasi PInvoke yang dibahas di bagian sebelumnya.

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

Saat pengguna memilih item atau mengubah pilihan mereka, kontrol memberi tahu jendela host dengan mengirimkan pesan WM_COMMAND, yang meningkatkan peristiwa MessageHook untuk halaman. Handler menerima informasi yang sama dengan prosedur jendela utama dari jendela host. Ini juga meneruskan referensi ke nilai Boolean, handled. Anda mengatur handled ke true untuk menunjukkan bahwa Anda telah menangani pesan dan tidak diperlukan pemrosesan lebih lanjut.

WM_COMMAND dikirim karena berbagai alasan, jadi Anda harus memeriksa ID pemberitahuan untuk menentukan apakah itu adalah peristiwa yang ingin Anda tangani. ID terkandung dalam bagian tinggi dari parameter wParam. Sampel menggunakan operator bitwise untuk mengekstrak ID. Jika pengguna telah membuat atau mengubah pilihan mereka, ID akan LBN_SELCHANGE.

Saat LBN_SELCHANGE diterima, sampel mendapatkan indeks item yang dipilih dengan mengirim kontrol pesan LB_GETCURSEL. Untuk mendapatkan teks, Anda terlebih dahulu membuat StringBuilder. Anda kemudian mengirim kontrol pesan LB_GETTEXT. Teruskan objek StringBuilder kosong sebagai parameter wParam. Saat SendMessage kembali, StringBuilder akan berisi teks item yang dipilih. Penggunaan SendMessage ini memerlukan deklarasi PInvoke lainnya.

Terakhir, atur handled ke true untuk menunjukkan bahwa pesan telah ditangani.

Lihat juga