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:
Zaimplementuj stronę WPF do hostowania okna. Jedną z technik jest utworzenie Border elementu w celu zarezerwowania sekcji strony dla okna hostowanego.
Zaimplementuj klasę do hostowania kontrolki dziedziczonej z HwndHostklasy .
W tej klasie przesłonięć HwndHost składowej BuildWindowCoreklasy .
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.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.
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.
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.
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.
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_GETCOUNT
ró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 handled
logicznej . 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_SELCHANGE
wartość .
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ż
.NET Desktop feedback