Przewodnik: hostowanie kontrolki Win32 w WPF

Program Windows Presentation Foundation (WPF) udostępnia bogate środowisko do tworzenia aplikacji. Jednak jeśli masz znaczną inwestycję w kod Win32, bardziej skuteczne może być ponowne użycie co najmniej jednego kodu w aplikacji WPF, a nie ponowne zapisywanie go całkowicie. WPF zapewnia prosty mechanizm hostowania okna Win32 na stronie WPF.

Ten temat przeprowadzi Cię przez aplikację, hostowanie kontrolki ListBox Win32 w przykładzie WPF, która hostuje kontrolkę pola listy Win32. Tę ogólną procedurę można rozszerzyć na hostowanie dowolnego okna Win32.

Wymagania

W tym temacie założono podstawową znajomość programowania zarówno WPF, jak i interfejsu API systemu Windows. Aby zapoznać się z podstawowym wprowadzeniem do programowania WPF, zobacz Wprowadzenie. Aby zapoznać się z wprowadzeniem do programowania interfejsu API systemu Windows, zobacz dowolną z licznych książek na ten temat, w szczególności Programowanie systemu Windows autorstwa Charlesa Petzolda.

Ponieważ przykład dołączony do tego tematu jest implementowany w języku C#, korzysta z usług Invocation Services (PInvoke) platformy w celu uzyskania dostępu do interfejsu API systemu Windows. Znajomość funkcji PInvoke jest pomocna, ale nie niezbędna.

Uwaga

Ten temat zawiera szereg przykładów kodu ze skojarzonego przykładu. Jednak w celu zapewnienia czytelności nie zawiera kompletnego przykładowego kodu. Możesz uzyskać lub wyświetlić pełny kod z hostingu kontrolki ListBox Win32 w przykładzie WPF.

Procedura podstawowa

W tej sekcji opisano podstawową procedurę hostowania okna Win32 na stronie WPF. Pozostałe sekcje zawierają szczegółowe informacje o poszczególnych krokach.

Podstawowa procedura hostingu to:

  1. Zaimplementuj stronę WPF do hostowania okna. Jedną z technik jest utworzenie Border elementu w celu zarezerwowania sekcji strony dla okna hostowanego.

  2. Zaimplementuj klasę do hostowania kontrolki dziedziczonej z HwndHostklasy .

  3. W tej klasie przesłonięć HwndHost składowej BuildWindowCoreklasy .

  4. Utwórz hostowane okno jako element podrzędny okna zawierającego stronę WPF. Chociaż konwencjonalne programowanie WPF nie musi jawnie korzystać z niego, strona hostingu jest oknem z uchwytem (HWND). Otrzymasz stronę HWND za pomocą hwndParent parametru BuildWindowCore metody . Hostowane okno powinno zostać utworzone jako element podrzędny tego HWND.

  5. Po utworzeniu okna hosta zwróć wartość HWND okna hostowanego. Jeśli chcesz hostować co najmniej jedną kontrolkę Win32, zazwyczaj utworzysz okno hosta jako element podrzędny HWND i ustawisz elementy podrzędne kontrolek tego okna hosta. Zawijanie kontrolek w oknie hosta zapewnia prosty sposób odbierania powiadomień z kontrolek przez stronę WPF, która zajmuje się niektórymi konkretnymi problemami Win32 z powiadomieniami w granicach HWND.

  6. Obsługa wybranych komunikatów wysyłanych do okna hosta, takich jak powiadomienia z kontrolek podrzędnych. Istnieją dwa sposoby, aby to zrobić.

    • Jeśli wolisz obsługiwać komunikaty w klasie hostingu, przesłoń metodę WndProcHwndHost klasy .

    • Jeśli wolisz obsługiwać komunikaty przez platformę WPF, obsłuż HwndHost zdarzenie klasy w kodzie MessageHook . To zdarzenie występuje dla każdego komunikatu otrzymanego przez hostowane okno. Jeśli wybierzesz tę opcję, nadal musisz zastąpić WndProcelement , ale potrzebujesz tylko minimalnej implementacji.

  7. Zastąpij DestroyWindowCore metody i WndProc klasy HwndHost. Należy zastąpić te metody, aby spełnić umowę HwndHost , ale może być konieczne tylko zapewnienie minimalnej implementacji.

  8. W pliku za pomocą kodu utwórz wystąpienie klasy hostingu kontrolek i utwórz je jako element podrzędny elementu przeznaczonego Border do hostowania okna.

  9. Komunikowanie się z oknem hostowanym przez wysłanie do niego komunikatów systemu Microsoft Windows i obsługę komunikatów z okien podrzędnych, takich jak powiadomienia wysyłane przez kontrolki.

Implementowanie układu strony

Układ strony WPF hostujący kontrolkę ListBox składa się z dwóch regionów. Po lewej stronie znajduje się kilka kontrolek WPF, które udostępniają interfejs użytkownika, który umożliwia manipulowanie kontrolką Win32. Prawy górny róg strony ma region kwadratowy dla hostowanej kontrolki ListBox.

Kod do zaimplementowania tego układu jest dość prosty. Element główny jest elementem zawierającym DockPanel dwa elementy podrzędne. Pierwszy to element hostujący Border kontrolkę ListBox. Zajmuje 200x200 kwadrat w prawym górnym rogu strony. Drugi to StackPanel element zawierający zestaw kontrolek WPF, które wyświetlają informacje i umożliwiają manipulowanie kontrolką ListBox przez ustawienie uwidocznionych właściwości współdziałania. Dla każdego z elementów podrzędnych StackPanelelementu , zobacz materiał referencyjny dla różnych elementów używanych w celu uzyskania szczegółowych informacji na temat tego, czym są te elementy lub co robią, są one wymienione w poniższym przykładowym kodzie, ale nie zostaną tutaj wyjaśnione (podstawowy model współdziałania nie wymaga żadnego z nich, są one udostępniane w celu dodania pewnej interakcyjności do próbki).

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

Implementowanie klasy do hostowania kontrolki Microsoft Win32

Rdzeniem tego przykładu jest klasa, która faktycznie hostuje kontrolkę ControlHost.cs. Dziedziczy HwndHostz elementu . Konstruktor przyjmuje dwa parametry, wysokość i szerokość, które odpowiadają wysokości i szerokości Border elementu, który hostuje kontrolkę ListBox. Te wartości są używane później, aby upewnić się, że rozmiar kontrolki jest zgodny z elementem 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

Istnieje również zestaw stałych. Te stałe są w dużej mierze pobierane z winuser.h i umożliwiają używanie konwencjonalnych nazw podczas wywoływania funkcji 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

Zastąpij pozycję BuildWindowCore, aby utworzyć okno Microsoft Win32

Zastąpisz tę metodę, aby utworzyć okno Win32, które będzie hostowane przez stronę, i nawiązać połączenie między oknem a stroną. Ponieważ ten przykład obejmuje hostowanie kontrolki ListBox, tworzone są dwa okna. Pierwszy to okno, które jest rzeczywiście hostowane przez stronę WPF. Kontrolka ListBox jest tworzona jako element podrzędny tego okna.

Przyczyną tego podejścia jest uproszczenie procesu odbierania powiadomień z kontrolki. Klasa HwndHost umożliwia przetwarzanie komunikatów wysyłanych do okna, które hostuje. W przypadku bezpośredniego hostowania kontrolki Win32 komunikaty wysyłane do wewnętrznej pętli komunikatów kontrolki. Kontrolkę można wyświetlić i wysłać do niej komunikaty, ale nie otrzymasz powiadomień wysyłanych przez kontrolkę do okna nadrzędnego. Oznacza to między innymi, że nie ma możliwości wykrywania, gdy użytkownik wchodzi w interakcję z kontrolką. Zamiast tego utwórz okno hosta i ustaw kontrolkę jako element podrzędny tego okna. Dzięki temu można przetwarzać komunikaty dla okna hosta, w tym powiadomienia wysyłane do niego przez kontrolkę. Dla wygody, ponieważ okno hosta jest nieco bardziej niż prostą otoką kontrolki, pakiet będzie określany jako kontrolka ListBox.

Tworzenie okna hosta i kontrolki ListBox

Możesz użyć funkcji PInvoke, aby utworzyć okno hosta dla kontrolki, tworząc i rejestrując klasę okien itd. Jednak znacznie prostszą metodą jest utworzenie okna ze wstępnie zdefiniowaną klasą okien "statycznych". Zapewnia to procedurę okna wymaganą do odbierania powiadomień z kontrolki i wymaga minimalnej ilości kodu.

HWND kontrolki jest uwidoczniona za pośrednictwem właściwości tylko do odczytu, tak aby strona hosta mogła jej używać do wysyłania komunikatów do kontrolki.

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

Kontrolka ListBox jest tworzona jako element podrzędny okna hosta. Wysokość i szerokość obu okien są ustawione na wartości przekazane do konstruktora, omówione powyżej. Dzięki temu rozmiar okna hosta i kontrolki są identyczne z obszarem zarezerwowanym na stronie. Po utworzeniu okien przykład zwraca HandleRef obiekt zawierający HWND okna hosta.

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

Implementowanie systemu DestroyWindow i WndProc

Oprócz BuildWindowCoremetody należy również zastąpić WndProc metody i DestroyWindowCore .HwndHost W tym przykładzie komunikaty dla kontrolki są obsługiwane przez MessageHook program obsługi, dlatego implementacja WndProc i DestroyWindowCore jest minimalna. W przypadku parametru WndProcustaw handled wartość na , aby false wskazać, że komunikat nie został obsłużony i zwraca wartość 0. W przypadku DestroyWindowCoreprogramu wystarczy zniszczyć okno.

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

Hostowanie kontrolki na stronie

Aby hostować kontrolkę na stronie, należy najpierw utworzyć nowe wystąpienie ControlHost klasy. Przekaż wysokość i szerokość elementu obramowania, który zawiera kontrolkę (ControlHostElement) do konstruktora ControlHost . Dzięki temu pole ListBox ma prawidłowy rozmiar. Następnie należy hostować kontrolkę na stronie, przypisując ControlHost obiekt do Child właściwości hosta Border.

Przykład dołącza procedurę obsługi do MessageHook zdarzenia ControlHost , aby odbierać komunikaty z kontrolki. To zdarzenie jest zgłaszane dla każdego komunikatu wysyłanego do hostowanego okna. W takim przypadku są to komunikaty wysyłane do okna, które opakowuje rzeczywistą kontrolkę ListBox, w tym powiadomienia z kontrolki. Przykład wywołuje metodę SendMessage, aby uzyskać informacje z kontrolki i zmodyfikować jej zawartość. Szczegółowe informacje o tym, jak strona komunikuje się z kontrolką, zostały omówione w następnej sekcji.

Uwaga

Zwróć uwagę, że istnieją dwie deklaracje PInvoke dla funkcji SendMessage. Jest to konieczne, ponieważ jeden używa parametru wParam do przekazania ciągu, a drugi używa go do przekazania liczby całkowitej. Potrzebna jest oddzielna deklaracja dla każdego podpisu, aby upewnić się, że dane są prawidłowo marshalowane.

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

Implementowanie komunikacji między kontrolką a stroną

Kontrolkę można manipulować, wysyłając komunikaty systemu Windows. Kontrolka powiadamia o tym, kiedy użytkownik wchodzi z nim w interakcję, wysyłając powiadomienia do okna hosta. Hostowanie kontrolki Win32 ListBox w przykładzie WPF zawiera interfejs użytkownika, który zawiera kilka przykładów sposobu działania:

  • Dołącz element do listy.

  • Usuń wybrany element z listy

  • Wyświetl tekst aktualnie wybranego elementu.

  • Wyświetl liczbę elementów na liście.

Użytkownik może również wybrać element w polu listy, klikając go tak samo jak w przypadku konwencjonalnej aplikacji Win32. Wyświetlane dane są aktualizowane za każdym razem, gdy użytkownik zmienia stan pola listy, wybierając, dodając lub dołączając element.

Aby dołączyć elementy, wyślij wiadomość w LB_ADDSTRING polu listy. Aby usunąć elementy, wyślij polecenie LB_GETCURSEL , aby pobrać indeks bieżącego zaznaczenia, a następnie LB_DELETESTRING usunąć element. Przykład wysyła LB_GETCOUNTrównież wartość i używa zwróconej wartości, aby zaktualizować ekran, który pokazuje liczbę elementów. Oba te wystąpienia SendMessage używają jednej z deklaracji PInvoke omówionej w poprzedniej sekcji.

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

Gdy użytkownik wybierze element lub zmieni wybór, kontrolka powiadamia okno hosta, wysyłając mu WM_COMMAND komunikat, który zgłasza MessageHook zdarzenie dla strony. Procedura obsługi otrzymuje te same informacje co główna procedura okna hosta. Przekazuje również odwołanie do wartości handledlogicznej . Ustawiono wartość handled , aby true wskazać, że komunikat został obsłużony i nie jest potrzebne żadne dalsze przetwarzanie.

WM_COMMAND program jest wysyłany z różnych powodów, dlatego należy sprawdzić identyfikator powiadomienia, aby określić, czy jest to zdarzenie, które chcesz obsłużyć. Identyfikator jest zawarty w wysokim słowie parametru wParam . W przykładzie użyto operatorów bitowych do wyodrębnienia identyfikatora. Jeśli użytkownik dokonał wyboru lub zmienił jego wybór, identyfikator będzie miał LBN_SELCHANGEwartość .

Po LBN_SELCHANGE odebraniu przykład pobiera indeks wybranego elementu przez wysłanie kontrolki komunikatuLB_GETCURSEL. Aby uzyskać tekst, należy najpierw utworzyć element StringBuilder. Następnie wysyłasz kontrolkę komunikat.LB_GETTEXT Przekaż pusty StringBuilder obiekt jako wParam parametr . Po SendMessage powrocie StringBuilder element będzie zawierać tekst zaznaczonego elementu. To użycie wymaga kolejnej SendMessage deklaracji PInvoke.

Na koniec ustaw handled wartość na , aby true wskazać, że komunikat został obsłużony.

Zobacz też