Przewodnik: hostowanie kontrolki Win32 w WPF

Windows Presentation Foundation (WPF) oferuje bogate środowisko do tworzenia aplikacji. Jeśli jednak masz znaczną inwestycję w kod Win32, może być bardziej efektywne ponowne użycie co najmniej jednego z tych kodów w aplikacji WPF, a nie wielokrotne pisanie. WPF udostępnia prosty mechanizm hostingu okna Win32 na stronie WPF.

Ten temat przeprowadzi Cię przez aplikację, która obsługuje formant ListBox elementu Win32 w przykładzie WPF, który hostuje kontrolkę pole listy Win32. Ta ogólna procedura może zostać rozszerzona o obsługę dowolnego okna Win32.

Wymagania

W tym temacie założono podstawową znajomość programów WPF i Windows API. Aby zapoznać się z podstawowymi wprowadzeniem do programowania WPF, zobacz wprowadzenie. Aby zapoznać się z wprowadzeniem do programowania interfejsu API systemu Windows, zobacz dowolną z wielu książek w temacie, w szczególności programowanie systemu Windows przez Charles Petzold.

Ponieważ przykład, który towarzyszy temu tematowi, jest zaimplementowany w języku C#, umożliwia dostęp do interfejsu API systemu Windows za pomocą usług wywołań platformy (PInvoke). Znajomość funkcji PInvoke jest przydatna, ale nie jest istotna.

Uwaga

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

Podstawowa procedura

W tej sekcji opisano podstawową procedurę hostingu okna Win32 na stronie WPF. Pozostałe sekcje zawierają szczegółowe informacje o każdym z kroków.

Podstawowa procedura hostingu:

  1. Zaimplementuj stronę WPF, aby hostować okno. Jedną z technik jest utworzenie Border elementu w celu zarezerwowania sekcji strony dla hostowanego okna.

  2. Zaimplementuj klasę, aby hostować kontrolkę, która dziedziczy z HwndHost .

  3. W tej klasie Przesłoń HwndHost element członkowski klasy BuildWindowCore .

  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 dojściem (HWND). Wartość parametru HWND zostanie wyświetlona przez hwndParent parametr BuildWindowCore metody. Hostowane okno powinno być tworzone jako element podrzędny tego parametru HWND.

  5. Po utworzeniu okna hosta Zwróć wartość HWND okna hostowanego. Jeśli chcesz hostować co najmniej jedną kontrolkę Win32, zazwyczaj tworzysz okno hosta jako element podrzędny HWND i kontroluje elementy podrzędne tego okna tego hosta. Zawijanie formantów w oknie hosta zapewnia prostą metodę otrzymywania powiadomień z formantów, które pomogą w przypadku niektórych określonych problemów Win32 z powiadomieniami na granicy HWND.

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

    • Jeśli wolisz obsługiwać komunikaty w klasie hostingu, Zastąp WndProc metodę HwndHost klasy.

    • Jeśli wolisz, aby program WPF obsłużył komunikaty, należy obsłużyć HwndHost MessageHook zdarzenie klasy w kodzie. To zdarzenie występuje dla każdego komunikatu, który jest odbierany przez hostowane okno. W przypadku wybrania tej opcji należy nadal przesłonić WndProc , ale potrzebna jest tylko minimalna implementacja.

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

  8. W pliku związanym z kodem Utwórz wystąpienie klasy hostingu kontrolki i Uczyń ją elementem podrzędnym Border elementu, który jest przeznaczony do hostowania okna.

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

Implementowanie układu strony

Układ strony WPF, która hostuje formant ListBox, składa się z dwóch regionów. Lewa strona na stronie zawiera kilka formantów WPF, które zapewniają interfejs użytkownika (UI) , że można manipulować kontrolką Win32. Prawy górny róg strony ma kwadratowy region dla hostowanej kontrolki ListBox.

Kod do zaimplementowania tego układu jest bardzo prosty. Element główny ma DockPanel dwa elementy podrzędne. Pierwszy to Border element, który hostuje formant ListBox. W prawym górnym rogu strony zajmuje się 200x200 kwadrat. Drugi to StackPanel element, który zawiera zestaw kontrolek WPF, które wyświetlają informacje i umożliwiają manipulowanie formantem ListBox przez ustawienie uwidocznionych właściwości operacji międzyoperacyjnego. Dla każdego elementu, który jest elementem podrzędnym StackPanel , Zobacz materiał referencyjny dla różnych elementów używanych do szczegółowych informacji o tym, co to są te elementy lub co robią, są one wymienione w poniższym przykładowym kodzie, ale nie zostaną wyjaśnione w tym miejscu (podstawowy model międzyoperacyjny nie wymaga żadnej z nich, są one dostarczane w celu dodania niektórych interakcji do przykładu).

<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 Win32 firmy Microsoft

Rdzeń tego przykładu jest klasą, która faktycznie hostuje formant, ControlHost. cs. Dziedziczy po HwndHost . Konstruktor przyjmuje dwa parametry, Wysokość i szerokość, które odnoszą się do wysokości i szerokości Border elementu, który hostuje formant ListBox. Te wartości są używane później, aby upewnić się, że rozmiar kontrolki pasuje do Border elementu.

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ą znacznie 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ąp BuildWindowCore, aby utworzyć okno Microsoft Win32

Ta metoda zostanie zastąpiona, aby utworzyć okno Win32, które będzie hostowane na stronie, i nawiązać połączenie między oknem a stroną. Ponieważ ten przykład obejmuje hostowanie kontrolki ListBox, tworzone są dwa okna. Pierwszy jest oknem, które jest przechowywane na stronie WPF. Formant ListBox jest tworzony jako element podrzędny tego okna.

Przyczyną tego podejścia jest uproszczenie procesu otrzymywania powiadomień z formantu. HwndHostKlasa umożliwia przetwarzanie komunikatów wysyłanych do okna, które obsługuje. W przypadku bezpośredniej obsługi kontrolki Win32 można odbierać komunikaty wysyłane do wewnętrznej pętli komunikatów formantu. Możesz wyświetlić formant i wysłać wiadomości IT, ale nie otrzymujesz powiadomień wysyłanych przez formant do okna nadrzędnego. Oznacza to między innymi, że nie ma możliwości wykrywania, kiedy użytkownik współdziała z kontrolką. Zamiast tego Utwórz okno hosta i uczyń formant elementem podrzędnym tego okna. Dzięki temu można przetwarzać komunikaty dla okna hosta, w tym powiadomienia wysyłane do niego przez formant. Dla wygody, ponieważ okno hosta jest nieco więcej niż prosta otoka dla kontrolki, pakiet będzie określany jako formant ListBox.

Utwórz okno hosta i formant ListBox

Za pomocą funkcji PInvoke można utworzyć okno hosta dla kontrolki, tworząc i rejestrując klasę okna i tak dalej. Jednak znacznie prostsze podejście polega na utworzeniu okna ze wstępnie zdefiniowaną klasą okna "static". Zapewnia to procedurę okna, która jest wymagana w celu otrzymywania powiadomień z kontrolki i wymaga minimalnego kodowania.

Właściwość HWND formantu jest uwidaczniana za pomocą właściwości tylko do odczytu, dzięki czemu Strona hosta może 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

Formant ListBox jest tworzony jako element podrzędny okna hosta. Wysokość i szerokość obu okien są ustawiane na wartości przesyłane do konstruktora, omówione powyżej. Dzięki temu rozmiar okna i kontrolki hosta jest identyczny z zastrzeżonym obszarem na stronie. Po utworzeniu systemu Windows, przykład zwraca HandleRef obiekt, który zawiera właściwość 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

Implementacja DestroyWindow i WndProc

Oprócz programu BuildWindowCore należy również zastąpić WndProc DestroyWindowCore metody i HwndHost . W tym przykładzie komunikaty dla formantu są obsługiwane przez MessageHook program obsługi, więc implementacja WndProc i DestroyWindowCore jest minimalny. W przypadku WndProc , ustaw na, handled false Aby wskazać, że wiadomość nie została obsłużona i zwrócić 0. Dla DestroyWindowCore , 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ć formant na stronie, należy najpierw utworzyć nowe wystąpienie ControlHost klasy. Przekaż wysokość i Szerokość elementu obramowania, który zawiera kontrolkę ( ControlHostElement ) do ControlHost konstruktora. Daje to pewność, że rozmiar elementu ListBox jest prawidłowy. Następnie można hostować kontrolę 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 formantu. To zdarzenie jest zgłaszane dla każdej wiadomości wysyłanej do hostowanego okna. W takim przypadku komunikaty wysyłane do okna, które zawijają rzeczywistą kontrolkę ListBox, w tym powiadomienia z formantu. Przykład wywołuje SendMessage, aby uzyskać informacje z kontrolki i modyfikować jej zawartość. Szczegóły dotyczące sposobu komunikowania się strony z kontrolką zostały omówione w następnej sekcji.

Uwaga

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

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ą

Formant można manipulować przez wysłanie komunikatów systemu Windows. Kontrolka powiadamia użytkownika, gdy użytkownik współdziała z nim, wysyłając powiadomienia do okna hosta. Formant ListBox w systemie Win32 w przykładzie WPF zawiera interfejs użytkownika, który zawiera kilka przykładów tego, jak to działa:

  • Dołącz element do listy.

  • Usuń wybrany element z listy

  • Wyświetla tekst aktualnie zaznaczonego elementu.

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

Użytkownik może również wybrać element w polu listy, klikając go, podobnie jak w przypadku konwencjonalnej aplikacji Win32. Wyświetlane dane są aktualizowane za każdym razem, gdy użytkownik zmienia stan pola listy przez wybranie, dodanie lub dołączenie elementu.

Aby dołączyć elementy, Wyślij pole listy do LB_ADDSTRING komunikatu. Aby usunąć elementy, Wyślij, LB_GETCURSEL Aby pobrać indeks bieżącego zaznaczenia, a następnie LB_DELETESTRING usunąć element. Przykład wysyła również LB_GETCOUNT i używa zwracanej wartości, aby zaktualizować ekran, który pokazuje liczbę elementów. Oba te wystąpienia SendMessage korzystają z jednej z deklaracji PInvoke omówionych 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, formant powiadamia okno hosta, wysyłając do niego WM_COMMAND komunikat, który wywołuje MessageHook zdarzenie dla strony. Program obsługi otrzymuje te same informacje, co procedura okna głównego okna hosta. Przekazuje także odwołanie do wartości logicznej handled . Ustawisz wartość tak handled true , aby wskazywała, że komunikat został obsłużony, a dalsze przetwarzanie nie jest potrzebne.

WM_COMMAND jest wysyłany z różnych powodów, dlatego należy sprawdzić identyfikator powiadomienia, aby określić, czy jest to zdarzenie, które ma zostać obsłużone. Identyfikator jest zawarty w wysokim słowie wParam parametru. W przykładzie zastosowano operatory bitowe do wyodrębnienia identyfikatora. Jeśli użytkownik wprowadził lub zmienił wybór, identyfikator będzie LBN_SELCHANGE .

Gdy LBN_SELCHANGE jest odbierany, próbka Pobiera indeks wybranego elementu przez wysłanie kontrolki LB_GETCURSEL komunikatu. Aby uzyskać tekst, należy najpierw utworzyć StringBuilder . Następnie wysyłasz kontrolkę LB_GETTEXT komunikat. Przekaż pusty StringBuilder obiekt jako wParam parametr. Gdy SendMessage zwraca, StringBuilder będzie zawierać tekst wybranego elementu. To użycie SendMessage wymaga jeszcze innej deklaracji PInvoke.

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

Zobacz też