Hosting di contenuto Win32 in WPFHosting Win32 Content in WPF

PrerequisitiPrerequisites

Vedere interoperatività di WPF e Win32.See WPF and Win32 Interoperation.

Una procedura dettagliata di Win32 all'interno di Windows Presentation Framework (HwndHost)A Walkthrough of Win32 Inside Windows Presentation Framework (HwndHost)

Per riutilizzare Win32Win32 contenuto all'interno di WPFWPF le applicazioni utilizzano HwndHost, che è un controllo che rende HWND simile WPFWPF contenuto.To reuse Win32Win32 content inside WPFWPF applications, use HwndHost, which is a control that makes HWNDs look like WPFWPF content. Come HwndSource, HwndHost è facile da utilizzare: derivano da HwndHost e implementare BuildWindowCore e DestroyWindowCore metodi, quindi creare un'istanza del HwndHost classe derivata e inserirlo all'interno del WPFWPF applicazione.Like HwndSource, HwndHost is straightforward to use: derive from HwndHost and implement BuildWindowCore and DestroyWindowCore methods, then instantiate your HwndHost derived class and place it inside your WPFWPF application.

Se il Win32Win32 logica è già compresso come un controllo, è possibile che il BuildWindowCore implementazione è poco più di una chiamata a CreateWindow.If your Win32Win32 logic is already packaged as a control, then your BuildWindowCore implementation is little more than a call to CreateWindow. Ad esempio, per creare un Win32Win32 controllo LISTBOX di C++C++:For example, to create a Win32Win32 LISTBOX control in C++C++:

virtual HandleRef BuildWindowCore(HandleRef hwndParent) override {  
    HWND handle = CreateWindowEx(0, L"LISTBOX",   
    L"this is a Win32 listbox",  
    WS_CHILD | WS_VISIBLE | LBS_NOTIFY  
    | WS_VSCROLL | WS_BORDER,  
    0, 0, // x, y  
    30, 70, // height, width  
    (HWND) hwndParent.Handle.ToPointer(), // parent hwnd  
    0, // hmenu  
    0, // hinstance  
    0); // lparam  

    return HandleRef(this, IntPtr(handle));  
}  

virtual void DestroyWindowCore(HandleRef hwnd) override {  
    // HwndHost will dispose the hwnd for us  
}  

Si supponga, tuttavia il Win32Win32 codice non sia completamente autonomo?But suppose the Win32Win32 code is not quite so self-contained? Se in tal caso, è possibile creare un Win32Win32 finestra di dialogo casella e incorporare il contenuto in un maggiore WPFWPF dell'applicazione.If so, you can create a Win32Win32 dialog box and embed its contents into a larger WPFWPF application. Nell'esempio viene illustrato in Microsoft Visual StudioMicrosoft Visual Studio e C++C++, sebbene sia possibile effettuare questa operazione in una lingua diversa o dalla riga di comando.The sample shows this in Microsoft Visual StudioMicrosoft Visual Studio and C++C++, although it is also possible to do this in a different language or at the command line.

Iniziare con una semplice finestra di dialogo, viene compilato in un C++C++ DLLDLL progetto.Start with a simple dialog, which is compiled into a C++C++ DLLDLL project.

Successivamente, inserire la finestra di dialogo nel maggiore WPFWPF applicazione:Next, introduce the dialog into the larger WPFWPF application:

  • Compilare il DLLDLL come gestita (/clr)Compile the DLLDLL as managed (/clr)

  • Attivare la finestra di dialogo in un controlloTurn the dialog into a control

  • Definire la classe derivata di HwndHost con BuildWindowCore e DestroyWindowCore metodiDefine the derived class of HwndHost with BuildWindowCore and DestroyWindowCore methods

  • Eseguire l'override TranslateAccelerator metodo per gestire le chiavi di finestra di dialogoOverride TranslateAccelerator method to handle dialog keys

  • Eseguire l'override TabInto per supportare la tabulazione (metodo)Override TabInto method to support tabbing

  • Eseguire l'override OnMnemonic per supportare i tasti di scelta (metodo)Override OnMnemonic method to support mnemonics

  • Creare un'istanza di HwndHost sottoclasse e inserirlo in destra WPFWPF elementoInstantiate the HwndHost subclass and put it under the right WPFWPF element

Attivare la finestra di dialogo in un controlloTurn the Dialog into a Control

È possibile attivare una finestra di dialogo in HWND usando gli stili WS_CHILD e DS_CONTROL figlio.You can turn a dialog box into a child HWND using the WS_CHILD and DS_CONTROL styles. Andare nel file di risorsa (RC) in cui è definita la finestra di dialogo e trovare l'inizio della definizione della finestra di dialogo:Go into the resource file (.rc) where the dialog is defined, and find the beginning of the definition of the dialog:

IDD_DIALOG1 DIALOGEX 0, 0, 303, 121  
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU  

Modificare la seconda riga a:Change the second line to:

STYLE DS_SETFONT | WS_CHILD | WS_BORDER | DS_CONTROL  

Questa azione non completamente comprimerlo in un controllo autonomo. è necessario chiamare IsDialogMessage() in modo Win32Win32 determinati messaggi vengano elaborati, ma la modifica del controllo fornisce un modo semplice per inserire i controlli in un altro oggetto HWND.This action does not fully package it into a self-contained control; you still need to call IsDialogMessage() so Win32Win32 can process certain messages, but the control change does provide a straightforward way of putting those controls inside another HWND.

Sottoclasse HwndHostSubclass HwndHost

Importare gli spazi dei nomi seguenti:Import the following namespaces:

namespace ManagedCpp  
{  
    using namespace System;  
    using namespace System::Windows;  
    using namespace System::Windows::Interop;  
    using namespace System::Windows::Input;  
    using namespace System::Windows::Media;  
    using namespace System::Runtime::InteropServices;  

Quindi creare una classe derivata di HwndHost ed eseguire l'override di BuildWindowCore e DestroyWindowCore metodi:Then create a derived class of HwndHost and override the BuildWindowCore and DestroyWindowCore methods:

public ref class MyHwndHost : public HwndHost, IKeyboardInputSink {  
    private:  
        HWND dialog;  

    protected:   
        virtual HandleRef BuildWindowCore(HandleRef hwndParent) override {  
            InitializeGlobals();   
            dialog = CreateDialog(hInstance,   
                MAKEINTRESOURCE(IDD_DIALOG1),   
                (HWND) hwndParent.Handle.ToPointer(),  
                (DLGPROC) About);   
            return HandleRef(this, IntPtr(dialog));  
        }  

        virtual void DestroyWindowCore(HandleRef hwnd) override {  
            // hwnd will be disposed for us  
        }  

Questo caso si usa il CreateDialog per creare la finestra di dialogo che è realmente un controllo.Here you use the CreateDialog to create the dialog box that is really a control. Poiché si tratta di uno dei primi metodi chiamati all'interno di DLLDLL, è inoltre necessario eseguire alcuni standard Win32Win32 inizializzazione richiamando una funzione che verrà definita in un secondo momento, chiamato InitializeGlobals():Since this is one of the first methods called inside the DLLDLL, you should also do some standard Win32Win32 initialization by calling a function you will define later, called InitializeGlobals():

bool initialized = false;  
    void InitializeGlobals() {  
        if (initialized) return;  
        initialized = true;  

        // TODO: Place code here.  
        MSG msg;  
        HACCEL hAccelTable;  

        // Initialize global strings  
        LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);  
        LoadString(hInstance, IDC_TYPICALWIN32DIALOG, szWindowClass, MAX_LOADSTRING);  
        MyRegisterClass(hInstance);  

Eseguire l'override del metodo TranslateAccelerator per gestire le chiavi di finestra di dialogoOverride TranslateAccelerator Method to Handle Dialog Keys

Se si esegue questo esempio a questo punto, si otterrebbe un controllo di finestra di dialogo che consente di visualizzare, ma in cui viene ignorata tutti della tastiera che consente l'elaborazione di una finestra di dialogo di una finestra di dialogo funzionale.If you ran this sample now, you would get a dialog control that displays, but it would ignore all of the keyboard processing that makes a dialog box a functional dialog box. È necessario eseguire l'override di TranslateAccelerator implementazione (proveniente da IKeyboardInputSink, un'interfaccia che HwndHost implementa).You should now override the TranslateAccelerator implementation (which comes from IKeyboardInputSink, an interface that HwndHost implements). Questo metodo viene chiamato quando l'applicazione riceve WM_KEYDOWN e WM_SYSKEYDOWN.This method gets called when the application receives WM_KEYDOWN and WM_SYSKEYDOWN.

#undef TranslateAccelerator  
        virtual bool TranslateAccelerator(System::Windows::Interop::MSG% msg,   
            ModifierKeys modifiers) override   
        {  
            ::MSG m = ConvertMessage(msg);  

            // Win32's IsDialogMessage() will handle most of our tabbing, but doesn't know   
            // what to do when it reaches the last tab stop  
            if (m.message == WM_KEYDOWN && m.wParam == VK_TAB) {  
                HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1);  
                HWND lastTabStop = GetDlgItem(dialog, IDCANCEL);  
                TraversalRequest^ request = nullptr;  

                if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) {  
                    // this code should work, but there’s a bug with interop shift-tab in current builds                      
                    request = gcnew TraversalRequest(FocusNavigationDirection::Last);  
                }  
                else if (!GetKeyState(VK_SHIFT) && GetFocus() == lastTabStop) {  
                    request = gcnew TraversalRequest(FocusNavigationDirection::Next);  
                }  

                if (request != nullptr)  
                    return ((IKeyboardInputSink^) this)->KeyboardInputSite->OnNoMoreTabStops(request);  

            }  

            // Only call IsDialogMessage for keys it will do something with.  
            if (msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) {  
                switch (m.wParam) {  
                    case VK_TAB:  
                    case VK_LEFT:  
                    case VK_UP:  
                    case VK_RIGHT:  
                    case VK_DOWN:  
                    case VK_EXECUTE:  
                    case VK_RETURN:  
                    case VK_ESCAPE:  
                    case VK_CANCEL:  
                        IsDialogMessage(dialog, &m);  
                        // IsDialogMessage should be called ProcessDialogMessage --  
                        // it processes messages without ever really telling you  
                        // if it handled a specific message or not  
                        return true;  
                }  
            }  

            return false; // not a key we handled  
        }  

Si tratta di una grande quantità di codice in un dato specifico, pertanto è possibile usare alcune informazioni dettagliate sulle.This is a lot of code in one piece, so it could use some more detailed explanations. Prima di tutto, il codice utilizzando C++C++ e C++C++ ; le macro, è necessario tenere presente che è già presente una macro denominata TranslateAccelerator, definito in winuser. h:First, the code using C++C++ and C++C++ macros; you need to be aware that there is already a macro named TranslateAccelerator, which is defined in winuser.h:

#define TranslateAccelerator  TranslateAcceleratorW  

Assicurarsi quindi di definire un TranslateAccelerator (metodo) e non un TranslateAcceleratorW metodo.So make sure to define a TranslateAccelerator method and not a TranslateAcceleratorW method.

Analogamente, è presente il winuser. h MSG non gestito e gestito Microsoft::Win32::MSG struct.Similarly, there is both the unmanaged winuser.h MSG and the managed Microsoft::Win32::MSG struct. Per evitare ambiguità tra i due oggetti utilizzando il C++C++ :: operatore.You can disambiguate between the two using the C++C++ :: operator.

virtual bool TranslateAccelerator(System::Windows::Interop::MSG% msg,   
    ModifierKeys modifiers) override   
{  
    ::MSG m = ConvertMessage(msg);  

Sia accodati hanno gli stessi dati, ma a volte risulta più facile lavorare con la definizione non gestita, pertanto in questo esempio è possibile definire la routine di conversione ovvia:Both MSGs have the same data, but sometimes it is easier to work with the unmanaged definition, so in this sample you can define the obvious conversion routine:

::MSG ConvertMessage(System::Windows::Interop::MSG% msg) {  
    ::MSG m;  
    m.hwnd = (HWND) msg.hwnd.ToPointer();  
    m.lParam = (LPARAM) msg.lParam.ToPointer();  
    m.message = msg.message;  
    m.wParam = (WPARAM) msg.wParam.ToPointer();  

    m.time = msg.time;  

    POINT pt;  
    pt.x = msg.pt_x;  
    pt.y = msg.pt_y;  
    m.pt = pt;  

    return m;  
}  

Torna alla TranslateAccelerator.Back to TranslateAccelerator. Il principio di base consiste nel chiamare il Win32Win32 funzione IsDialogMessage per effettuare più operazioni possibili, ma IsDialogMessage non dispone dell'accesso a qualsiasi elemento esterno la finestra di dialogo.The basic principle is to call the Win32Win32 function IsDialogMessage to do as much work as possible, but IsDialogMessage does not have access to anything outside the dialog. Durante l'esecuzione di una scheda di utente nella finestra di dialogo, quando la tabulazione oltre l'ultimo controllo nella finestra di dialogo, è necessario impostare lo stato attivo di WPFWPF parte chiamando IKeyboardInputSite::OnNoMoreStops.As a user tab around the dialog, when tabbing runs past the last control in our dialog, you need to set focus to the WPFWPF portion by calling IKeyboardInputSite::OnNoMoreStops.

// Win32's IsDialogMessage() will handle most of the tabbing, but doesn't know   
// what to do when it reaches the last tab stop  
if (m.message == WM_KEYDOWN && m.wParam == VK_TAB) {  
    HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1);  
    HWND lastTabStop = GetDlgItem(dialog, IDCANCEL);  
    TraversalRequest^ request = nullptr;  

    if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) {  
        request = gcnew TraversalRequest(FocusNavigationDirection::Last);  
    }  
    else if (!GetKeyState(VK_SHIFT) && GetFocus() ==  lastTabStop) { {  
        request = gcnew TraversalRequest(FocusNavigationDirection::Next);  
    }  

    if (request != nullptr)  
        return ((IKeyboardInputSink^) this)->KeyboardInputSite->OnNoMoreTabStops(request);  
}  

Infine, viene chiamato IsDialogMessage.Finally, call IsDialogMessage. Ma una delle responsabilità di un TranslateAccelerator metodo impone WPFWPF se la sequenza di tasti è stata gestita o non.But one of the responsibilities of a TranslateAccelerator method is telling WPFWPF whether you handled the keystroke or not. Se non viene gestita, l'evento di input può effettuare il tunneling e a bolle attraverso il resto dell'applicazione.If you did not handle it, the input event can tunnel and bubble through the rest of the application. In questo caso, si espone una particolarità della gestione dei messaggi della tastiera e la natura dell'architettura di input in Win32Win32.Here, you will expose a quirk of keyboard messange handling and the nature of the input architecture in Win32Win32. Purtroppo, IsDialogMessage non restituito in alcun modo se gestisce una particolare sequenza di tasti.Unfortunately, IsDialogMessage does not return in any way whether it handles a particular keystroke. Peggio ancora, verrà chiamato DispatchMessage() su sequenze di tasti che non deve gestire!Even worse, it will call DispatchMessage() on keystrokes it should not handle! Pertanto è necessario decodificare IsDialogMessagee lo chiama solo per gestiscono i tasti che:So you will have to reverse-engineer IsDialogMessage, and only call it for the keys you know it will handle:

// Only call IsDialogMessage for keys it will do something with.  
if (msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) {  
    switch (m.wParam) {  
        case VK_TAB:  
        case VK_LEFT:  
        case VK_UP:  
        case VK_RIGHT:  
        case VK_DOWN:  
        case VK_EXECUTE:  
        case VK_RETURN:  
        case VK_ESCAPE:  
        case VK_CANCEL:  
            IsDialogMessage(dialog, &m);  
            // IsDialogMessage should be called ProcessDialogMessage --  
            // it processes messages without ever really telling you  
            // if it handled a specific message or not  
            return true;  
    }  

Eseguire l'override del metodo TabInto per supportare la tabulazioneOverride TabInto Method to Support Tabbing

Ora che è stato implementato TranslateAccelerator, un utente può spostarsi nella finestra di dialogo casella e uscirne tramite tab in maggiore WPFWPF dell'applicazione.Now that you have implemented TranslateAccelerator, a user can tab around inside the dialog box and tab out of it into the greater WPFWPF application. Ma un utente non è possibile scheda alla finestra di dialogo.But a user cannot tab back into the dialog box. Per risolvere questo problema, si esegue l'override TabInto:To solve that, you override TabInto:

public:   
    virtual bool TabInto(TraversalRequest^ request) override {  
        if (request->FocusNavigationDirection == FocusNavigationDirection::Last) {  
            HWND lastTabStop = GetDlgItem(dialog, IDCANCEL);  
            SetFocus(lastTabStop);  
        }  
        else {  
            HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1);  
            SetFocus(firstTabStop);  
        }  
        return true;  
    }  

Il TraversalRequest parametro indica se l'azione della scheda è una scheda o MAIUSC.The TraversalRequest parameter tells you whether the tab action is a tab or shift tab.

Eseguire l'override del metodo OnMnemonic per supportare i tasti di sceltaOverride OnMnemonic Method to Support Mnemonics

Gestione della tastiera è quasi completata, ma ne esiste uno cosa mancante: tasti di scelta non funzionano.Keyboard handling is almost complete, but there is one thing missing – mnemonics do not work. Se un utente preme alt + F, attivo non passa per il "nome:" casella di modifica.If a user presses alt-F, focus doe not jump to the "First name:" edit box. In tal caso, si esegue l'override di OnMnemonic metodo:So, you override the OnMnemonic method:

virtual bool OnMnemonic(System::Windows::Interop::MSG% msg, ModifierKeys modifiers) override {  
    ::MSG m = ConvertMessage(msg);  

    // If it's one of our mnemonics, set focus to the appropriate hwnd  
    if (msg.message == WM_SYSCHAR && GetKeyState(VK_MENU /*alt*/)) {  
        int dialogitem = 9999;  
        switch (m.wParam) {  
            case 's': dialogitem = IDOK; break;  
            case 'c': dialogitem = IDCANCEL; break;  
            case 'f': dialogitem = IDC_EDIT1; break;  
            case 'l': dialogitem = IDC_EDIT2; break;  
            case 'p': dialogitem = IDC_EDIT3; break;  
            case 'a': dialogitem = IDC_EDIT4; break;  
            case 'i': dialogitem = IDC_EDIT5; break;  
            case 't': dialogitem = IDC_EDIT6; break;  
            case 'z': dialogitem = IDC_EDIT7; break;  
        }  
        if (dialogitem != 9999) {  
            HWND hwnd = GetDlgItem(dialog, dialogitem);  
            SetFocus(hwnd);  
            return true;  
        }  
    }  
    return false; // key unhandled  
};  

Non viene chiamato IsDialogMessage qui?Why not call IsDialogMessage here? È necessario lo stesso problema come prima, ovvero è necessario essere in grado di informare WPFWPF codice se il codice gestito la sequenza di tasti oppure No, e IsDialogMessage non.You have the same issue as before--you need to be able to inform WPFWPF code whether your code handled the keystroke or not, and IsDialogMessage cannot do that. È inoltre disponibile un secondo problema, perché IsDialogMessage rifiuta di elaborare il tasto di scelta rapida se HWND con lo stato attivo non è presente all'interno della casella della finestra di dialogo.There is also a second issue, because IsDialogMessage refuses to process the mnemonic if the focused HWND is not inside the dialog box.

Creare un'istanza della classe HwndHost derivataInstantiate the HwndHost Derived Class

Infine, dopo che tutto il supporto di scheda e di chiave è presente, è possibile inserire il HwndHost in più grande WPFWPF dell'applicazione.Finally, now that all the key and tab support is in place, you can put your HwndHost into the larger WPFWPF application. Se l'applicazione principale viene scritto in XAMLXAML, è il modo più semplice per inserirlo nella posizione corretta per lasciare vuota Border elemento in cui si desidera inserire il HwndHost.If the main application is written in XAMLXAML, the easiest way to put it in the right place is to leave an empty Border element where you want to put the HwndHost. Di seguito si crea un Border denominato insertHwndHostHere:Here you create a Border named insertHwndHostHere:

<Window x:Class="WPFApplication1.Window1"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    Title="Windows Presentation Framework Application"  
    Loaded="Window1_Loaded"  
    >  
    <StackPanel>  
        <Button Content="WPF button"/>  
        <Border Name="insertHwndHostHere" Height="200" Width="500"/>  
        <Button Content="WPF button"/>  
    </StackPanel>  
</Window>  

Quindi è comunque per trovare un buon punto nella sequenza di codice per creare un'istanza di HwndHost e connetterla al Border.Then all that remains is to find a good place in code sequence to instantiate the HwndHost and connect it to the Border. In questo esempio, si verrà inserirlo all'interno del costruttore per la Window classe derivata:In this example, you will put it inside the constructor for the Window derived class:

public partial class Window1 : Window {  
    public Window1() {  
    }  

    void Window1_Loaded(object sender, RoutedEventArgs e) {  
        HwndHost host = new ManagedCpp.MyHwndHost();  
        insertHwndHostHere.Child = host;  
    }  
}  

Che consente di:Which gives you:

Schermata dell'applicazione WPFWPF application screen shot

Vedere ancheSee Also

Interoperatività di WPF e Win32WPF and Win32 Interoperation