Megosztás a következőn keresztül:


Útmutató: Win32-vezérlő beágyazása a WPF-be

A Windows Presentation Foundation (WPF) gazdag környezetet biztosít az alkalmazások létrehozásához. Ha azonban jelentős befektetéssel rendelkezik a Win32-kódba, hatékonyabb lehet a kód legalább egy részének újbóli felhasználása a WPF-alkalmazásban ahelyett, hogy teljesen átírta volna. A WPF egyszerű mechanizmust biztosít a Win32-ablak üzemeltetéséhez egy WPF-oldalon.

Ez a témakör végigvezet egy alkalmazáson, a WPF-mintán, amely egy Win32 listamező vezérlőelemet tartalmaz. Ez az általános eljárás bármely Win32-ablak üzemeltetésére kiterjeszthető.

Követelmények

Ez a témakör a WPF és a Windows API programozásának alapszintű ismeretét feltételezi. A WPF-programozás alapszintű bemutatása: Első lépések. A Windows API programozásának bemutatása érdekében tekintse meg a témával kapcsolatos számos könyvet, különösen Charles Petzold Windows- programozását.

Mivel a témakörhöz tartozó minta a C#-ban van implementálva, a Platform Invocation Services (PInvoke) használatával éri el a Windows API-t. A PInvoke ismerete hasznos, de nem nélkülözhetetlen.

Megjegyzés:

Ez a témakör számos példakódot tartalmaz a társított mintából. Az olvashatóság érdekében azonban nem tartalmazza a teljes mintakódot. A teljes kód lekérésére vagy megtekintésére a WPF-mintából, amely Win32 ListBox-vezérlőt tartalmaz, látogasson el ide.

Az alapvető eljárás

Ez a szakasz a Win32-ablak WPF-oldalon való üzemeltetésének alapvető eljárását ismerteti. A többi szakasz végighalad az egyes lépések részletein.

Az alapvető üzemeltetési eljárás a következő:

  1. Implementáljon egy WPF-lapot az ablak üzemeltetéséhez. Az egyik módszer egy Border elem létrehozása a lap egy szakaszának lefoglalásához a üzemeltetett ablakhoz.

  2. Implementáljon egy osztályt a HwndHostöröklő vezérlő üzemeltetéséhez.

  3. Ebben az osztályban írja felül a HwndHost osztálytagot BuildWindowCore.

  4. Hozza létre a üzemeltetett ablakot a WPF-lapot tartalmazó ablak gyermekeként. Bár a hagyományos WPF-programozásnak nem kell kifejezetten használnia, a kiszolgáló oldal egy ablakkezelővel ellátott ablak (HWND). A HWND lapot a hwndParent metódus BuildWindowCore paraméterén keresztül kapja meg. Az üzemeltetett ablakot ennek a HWND-nek aláablakként kell létrehozni.

  5. Miután létrehozta a gazdaablakot, adja vissza az üzemeltetett ablak HWND azonosítóját. Ha egy vagy több Win32-vezérlőt szeretne üzemeltetni, általában egy hosztablakot hoz létre a HWND alablaként, és a vezérlők ennek a hosztablaknak a gyermekeivé válnak. A gazdaablak vezérlőinek körbefuttatásával a WPF-oldal egyszerűen fogadhat értesítéseket a vezérlőkről, amely a HWND-határon keresztüli értesítésekkel kapcsolatos bizonyos Win32-problémákat kezeli.

  6. Kezelje a gazdaablakba küldött kijelölt üzeneteket, például a gyermek vezérlők értesítései. Ezt kétféleképpen teheti meg.

    • Ha inkább az üzemeltetési osztályban szeretné kezelni az üzeneteket, írja felül a WndProc osztály HwndHost metódusát.

    • Ha azt szeretné, hogy a WPF kezelje az üzeneteket, kezelje a HwndHost osztály MessageHook eseményt a mögöttes kódban. Ez az esemény minden, a üzemeltetett ablak által fogadott üzenetre megtörténik. Ha ezt a lehetőséget választja, akkor is le kell cserélnie a WndProcértékét, de csak minimális megvalósításra van szüksége.

  7. A DestroyWindowCoreWndProc és HwndHost metódusait írd felül. Ezeket a metódusokat felül kell írnia a HwndHost szerződés teljesítéséhez, de lehetséges, hogy csak minimális implementációt kell biztosítania.

  8. A kód mögötti fájlban hozzon létre egy példányt a vezérlő üzemeltetési osztályából, és tegye az ablak üzemeltetésére szolgáló Border elem gyermekének.

  9. Kommunikáljon a üzemeltetett ablakkal a Microsoft Windows-üzenetek küldésével és a gyermekablakból érkező üzenetek kezelésével, például a vezérlők által küldött értesítésekkel.

A lapelrendezés implementálása

A ListBox Controlt futtató WPF-lap elrendezése két régióból áll. A lap bal oldalán több WPF-vezérlő található, amelyek felhasználói felületet (UI) biztosítanak, amelyek lehetővé teszik a Win32 vezérlő manipulálását. A lap jobb felső sarkában egy négyzet alakú terület található a üzemeltetett ListBox Controlhoz.

Az elrendezés implementálásához szükséges kód meglehetősen egyszerű. A gyökérelem egy DockPanel, amely két al-elemből áll. Az első egy Border elem, amely a ListBox-vezérlőt üzemelteti. A lap jobb felső sarkában egy 200x200 négyzetet foglal el. A második egy StackPanel elem, amely olyan WPF-vezérlőket tartalmaz, amelyek információkat jelenítenek meg, és lehetővé teszik a ListBox-vezérlő kezelését a közzétett együttműködési tulajdonságok beállításával. A StackPanelgyermekeinek minden egyes eleméhez tekintse meg a különböző elemek referenciaanyagát, amelyek részletesen ismertetik, hogy mik ezek az elemek vagy mit csinálnak, ezek az alábbi példakódban szerepelnek, de itt nem lesznek magyarázva (az alapvető együttműködési modell nem igényel ezek közül egyiket sem, ezek a minta interaktivitásának hozzáadásához vannak megadva).

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

Osztály implementálása a Microsoft Win32-vezérlő üzemeltetéséhez

A minta magja az az osztály, ControlHost.cs, amely ténylegesen üzemelteti a vezérlőt. Az HwndHost-t örökli. A konstruktor két paramétert használ, a magasságot és a szélességet, amelyek a ListBox vezérlőt futtató Border elem magasságának és szélességének felelnek meg. Ezeket az értékeket később használjuk annak ellenőrzésére, hogy a vezérlő mérete megegyezik-e a Border elemével.

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

Állandók is vannak. Ezek az állandók nagyrészt a Winuser.h-tól származnak, és lehetővé teszik a hagyományos nevek használatát a Win32-függvények meghívásakor.

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

A BuildWindowCore felülbírálása a Microsoft Win32 ablak létrehozásához

Ezt a módszert felülbírálva hozza létre az oldal által üzemeltetett Win32-ablakot, és hozza létre a kapcsolatot az ablak és a lap között. Mivel ez a minta egy ListBox-vezérlő üzemeltetését foglalja magában, két ablak jön létre. Az első a WPF-oldal által üzemeltetett ablak. A Listamező vezérlőelem az ablak gyermekeként jön létre.

Ennek a megközelítésnek az az oka, hogy leegyszerűsíti a vezérlőtől érkező értesítések fogadásának folyamatát. A HwndHost osztály lehetővé teszi az általa üzemeltetett ablakba küldött üzenetek feldolgozását. Ha közvetlenül Win32-vezérlőt üzemeltet, a vezérlő belső üzenethurkának küldött üzeneteket kapja. Megjelenítheti a vezérlőt, és üzeneteket küldhet neki, de nem kapja meg a vezérlő által a szülőablakba küldött értesítéseket. Ez többek között azt jelenti, hogy nem tudja észlelni, hogy a felhasználó mikor lép kapcsolatba a vezérlővel. Ehelyett hozzon létre egy gazdagépablakot, és tegye a vezérlőelemet az ablak gyermekévé. Ez lehetővé teszi a gazdagép ablak üzeneteinek feldolgozását, beleértve a vezérlő által küldött értesítéseket is. A kényelem érdekében, mivel a gazdagép ablaka alig több mint egy egyszerű burkoló a vezérlő számára, a csomagra ListBox-vezérlőként hivatkozunk.

A fogadóablak és a Listamező vezérlő létrehozása

A PInvoke használatával létrehozhat egy gazdaablakot a vezérlő számára, például egy ablakosztály létrehozásával és regisztrálásával. Sokkal egyszerűbb módszer azonban egy előre definiált "statikus" ablakosztályú ablak létrehozása. Ez biztosítja a vezérlőtől érkező értesítések fogadásához szükséges ablakos eljárást, és minimális kódolást igényel.

A vezérlő HWND-je egy írásvédett tulajdonságon keresztül jelenik meg, úgy, hogy a gazda oldal használhatja arra, hogy üzeneteket küldjön a vezérlőnek.

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

A Listamező vezérlőelem a gazdaablak gyermekeként jön létre. Mindkét ablak magassága és szélessége a konstruktornak átadott értékekre van beállítva, a fentebb tárgyaltak szerint. Ez biztosítja, hogy a gazdagépablak és a vezérlő mérete megegyezik a lap fenntartott területével. Az ablakok létrehozása után a minta egy HandleRef objektumot ad vissza, amely a gazdagépablak HWND-ját tartalmazza.

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

A DestroyWindow és a WndProc implementálása

A BuildWindowCoremellett felül kell bírálnia a WndProcDestroyWindowCore és HwndHost módszereit is . Ebben a példában a vezérlő üzenetét a MessageHook kezelő kezeli, így a WndProc és a DestroyWindowCore megvalósítása minimális. A WndProcesetén állítsa a handledfalse értékre, hogy jelezze, hogy az üzenet nem lett kezelve, és 0 értéket ad vissza. DestroyWindowCoreesetén egyszerűen törje be az ablakot.

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

A vezérlő elhelyezése az oldalon

Ha a vezérlőt a lapon szeretné üzemeltetni, először létre kell hoznia a ControlHost osztály új példányát. Adja át a vezérlőt (ControlHostElement) tartalmazó szegélyelem magasságát és szélességét a ControlHost konstruktornak. Ez biztosítja, hogy a ListBox megfelelően legyen méretezve. Ezután a vezérlőt a lapon úgy helyezi el, hogy a ControlHost objektumot hozzárendeli a gazdagép ChildBorder tulajdonságához.

A minta egy kezelőt csatol a MessageHookControlHost eseményéhez, hogy üzeneteket fogadjon a vezérlőtől. Ez az esemény minden, a üzemeltetett ablakba küldött üzenethez megjelenik. Ebben az esetben ezek az üzenetek kerülnek az ablakba, amelyek a tényleges ListBox-vezérlőt burkolják, beleértve a vezérlő értesítéseit is. A minta meghívja a SendMessage-t, hogy információkat szerezzen be a vezérlőből, és módosítsa annak tartalmát. A lap vezérlővel való kommunikációjának részleteit a következő szakaszban tárgyaljuk.

Megjegyzés:

Figyelje meg, hogy két PInvoke-deklaráció van a SendMessage-hez. Erre azért van szükség, mert az egyik a wParam paraméterrel ad át egy sztringet, a másik pedig egész számot ad át. Minden aláíráshoz külön deklarációra van szükség az adatok megfelelőségének biztosításához.

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

Kommunikáció megvalósítása a vezérlő és a lap között

A vezérlőt Windows-üzenetek küldésével módosíthatja. A vezérlő értesítést küld, ha a felhasználó interakcióba lép vele, a gazdagép ablakának értesítéseket küldve. A WIN32 ListBox-vezérlőt futtató a WPF-mintában egy olyan felhasználói felületet tartalmaz, amely számos példát mutat be ennek működésére:

  • Elem hozzáfűzése a listához.

  • A kijelölt elem törlése a listából

  • Az aktuálisan kijelölt elem szövegének megjelenítése.

  • A lista elemeinek számának megjelenítése.

A felhasználó úgy is kijelölhet egy elemet a listamezőben, hogy rá kattint, ugyanúgy, mint egy hagyományos Win32-alkalmazás esetében. A megjelenített adatok minden alkalommal frissülnek, amikor a felhasználó módosítja a lista állapotát egy elem kiválasztásával, hozzáadásával vagy hozzáfűzésével.

Az elemek hozzáfűzéséhez küldjön egy üzenetet a listadoboznak az LB_ADDSTRING parancssegítségével. Elemek törléséhez küldjön LB_GETCURSEL az aktuális kijelölés indexének lekéréséhez, majd LB_DELETESTRING az elem törléséhez. A minta LB_GETCOUNTis küld, és a visszaadott érték használatával frissíti az elemek számát megjelenítő megjelenítést. A SendMessage mindkét példánya az előző szakaszban tárgyalt PInvoke-deklarációk egyikét használja.

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

Amikor a felhasználó kiválaszt egy elemet, vagy módosítja a kijelölést, a vezérlő egy WM_COMMAND üzenetet küld a gazdaablaknak,, amely a lap MessageHook eseményét emeli ki. A kezelő ugyanazokat az információkat kapja, mint a gazdaablak főablak-eljárása. Egy logikai értékre vonatkozó hivatkozást is átad, handled. Ön a handled értékét true-re állítja, jelezve, hogy az üzenetet kezelte, és nincs szükség további feldolgozásra.

WM_COMMAND-t többféle okból küldik el, ezért meg kell vizsgálnia az értesítő azonosítót annak megállapításához, hogy az eseményt kezelni szeretné-e. Az azonosító a wParam paraméter felső szavában található. A minta bitenkénti operátorokat használ az azonosító kinyeréséhez. Ha a felhasználó végrehajtotta vagy módosította a kijelölést, az azonosító LBN_SELCHANGElesz.

Amikor LBN_SELCHANGE érkezik, a minta lekéri a kijelölt elem indexét azáltal, hogy LB_GETCURSEL üzenetet küld a vezérlőnek. A szöveg lekéréséhez először létre kell hoznia egy StringBuilder. Ezután LB_GETTEXT üzenetet küld a vezérlőnek. Adja át az üres StringBuilder objektumot wParam paraméterként. Amikor SendMessage ad vissza, a StringBuilder a kijelölt elem szövegét fogja tartalmazni. A SendMessage használatához még egy PInvoke-deklarációra van szükség.

Végül állítsa a handledtrue értékre, hogy jelezze, hogy az üzenet kezelése megtörtént.

Lásd még