Exemplarische Vorgehensweise: Hosten von WPF-Inhalt in Win32
Windows Presentation Foundation (WPF) stellt eine umfangreiche Umgebung zum Erstellen von Anwendungen bereit. Wenn Sie allerdings bereits erhebliche Arbeit in das Schreiben von Win32-Code investiert haben, kann es effektiver sein, eine vorhandene Anwendung mit WPF zu erweitern, anstatt den ursprünglichen Code neu zu schreiben. WPF bietet ein einfaches Verfahren zum Hosten von WPF-Inhalt in einem Win32-Fenster.
In diesem Lernprogramm erfahren Sie, wie Sie eine Beispielanwendung schreiben, Beispiel für das Hosten von Windows Presentation Foundation-Inhalt in einem Win32-Fenster, die WPF-Inhalt in einem Win32-Fenster hostet. Sie können dieses Beispiel erweitern, um ein beliebiges Win32-Fenster zu hosten. Die Anwendung wird in C++/CLI geschrieben, da verwalteter und nicht verwalteter Code kombiniert werden müssen.
Dieses Thema enthält folgende Abschnitte.
- Anforderungen
- Das grundlegende Verfahren
- Implementieren der Hostanwendung
- Implementieren der WPF-Seite
- Verwandte Abschnitte
Anforderungen
In diesem Lernprogramm wird davon ausgegangen, dass Sie mit den Grundlagen der WPF-Programmierung und Win32-Programmierung vertraut sind. Eine Einführung in die Grundlagen der WPF-Programmierung finden Sie unter Erste Schritte (WPF). Eine Einführung in die Win32-Programmierung finden Sie in zahlreichen Büchern zu diesem Thema, insbesondere in Windows-Programmierung von Charles Petzold.
Da das Beispiel für dieses Lernprogramm in C++/CLI implementiert wurde, wird davon ausgegangen, dass Sie mit C++ vertraut sind und die Win32-API programmieren können. Außerdem werden Kenntnisse in der Programmierung von verwaltetem Code vorausgesetzt. Kenntnisse in C++/CLI sind hilfreich, aber nicht Voraussetzung.
Hinweis |
---|
Dieses Lernprogramm schließt eine Reihe von Codebeispielen aus dem genannten Beispiel ein.Aus Gründen der besseren Lesbarkeit wird jedoch nicht der gesamte Beispielcode aufgeführt.Den vollständigen Beispielcode finden Sie unter Beispiel für das Hosten von Windows Presentation Foundation-Inhalt in einem Win32-Fenster. |
Das grundlegende Verfahren
Dieser Abschnitt umfasst das grundlegende Verfahren zum Hosten von WPF-Inhalt in einem Win32-Fenster. In den weiteren Abschnitten werden die einzelnen Schritte näher erläutert.
Entscheidend für das Hosten von WPF-Inhalt in einem Win32-Fenster ist die HwndSource-Klasse. Diese Klasse umschließt den WPF-Inhalt in einem Win32-Fenster, sodass er als untergeordnetes Fenster in Ihre user interface (UI) eingebunden werden kann. Bei folgender Vorgehensweise werden Win32 und WPF in einer einzigen Anwendung zusammengefasst.
Implementieren Sie den WPF-Inhalt als verwaltete Klasse.
Implementieren Sie eine Win32-Anwendung mit C++/CLI. Wenn Sie mit einer vorhandenen Anwendung und nicht verwaltetem C++-Code beginnen, können Sie diese in der Regel verwalteten Code aufrufen lassen, indem Sie das /clr-Compilerflag in die Projekteinstellungen aufnehmen.
Legen Sie das Threadingmodell auf Singlethread-Apartment (STA) fest.
Behandeln Sie die WM_CREATE-Benachrichtigung in der Fensterprozedur, und führen Sie folgende Schritte aus:
Erstellen Sie ein neues HwndSource-Objekt mit dem übergeordneten Fenster als dessen parent-Parameter.
Erstellen Sie eine Instanz der WPF-Inhaltsklasse.
Weisen Sie dem WPF-Inhaltsobjekt einen Verweis auf die RootVisual-Eigenschaft von HwndSource zu.
Rufen Sie HWND für den Inhalt ab. Die Handle-Eigenschaft des HwndSource-Objekts enthält das Fensterhandle (HWND). Um ein HWND zu erhalten, das Sie im nicht verwalteten Teil der Anwendung verwenden können, wandeln Sie Handle.ToPointer() in ein HWND um.
Implementieren Sie eine verwaltete Klasse, die ein statisches Feld mit einem Verweis auf den WPF-Inhalt enthält. Diese Klasse ermöglicht es Ihnen, einen Verweis auf den WPF-Inhalt vom Win32-Code abzurufen.
Weisen Sie dem statischen Feld den WPF-Inhalt zu.
Um Benachrichtigungen vom WPF-Inhalt zu empfangen, fügen Sie an eines oder mehrere WPF-Ereignisse einen Handler an.
Kommunizieren Sie mithilfe des im statischen Feld gespeicherten Verweises mit dem WPF-Inhalt, um Eigenschaften festzulegen usw.
Hinweis |
---|
Sie können auch Extensible Application Markup Language (XAML) verwenden, um den WPF-Inhalt zu implementieren.Sie müssen ihn allerdings separat als eine dynamic-link library (DLL) kompilieren und auf diese DLL aus Ihrer Win32-Anwendung heraus verweisen.Die übrigen Schritte ähneln dem oben beschriebenen Verfahren. |
Implementieren der Hostanwendung
In diesem Abschnitt wird beschrieben, wie Sie WPF-Inhalt in einer grundlegenden Win32-Anwendung hosten. Der Inhalt selbst wird in C++/CLI als verwaltete Klasse implementiert. Größtenteils ist es eine einfache WPF-Programmierung. Die wichtigsten Aspekte bei der Implementierung von Inhalt werden unter Implementieren des WPF-Inhalts erläutert.
Die grundlegende Anwendung
Hosten des WPF-Inhalts
Verweisen auf den WPF-Inhalt
Kommunizieren mit dem WPF-Inhalt
Die grundlegende Anwendung
Der Ausgangspunkt für die Hostanwendung bestand darin, eine Microsoft Visual Studio 2005-Vorlage zu erstellen.
Öffnen Sie Visual Studio 2005, und wählen Sie im Menü Datei die Option Neues Projekt aus.
Wählen Sie Win32 aus der Liste der Visual C++-Projekttypen aus. Wenn die Standardsprache nicht C++ ist, finden Sie diese Projekttypen unter Andere Sprachen.
Wählen Sie eine Win32-Projekt-Vorlage, weisen Sie dem Projekt einen Namen zu, und klicken Sie auf OK, um den Win32-Anwendungs-Assistenten zu starten.
Übernehmen Sie die Standardeinstellungen des Assistenten, und klicken Sie auf Fertig stellen, um das Projekt zu starten.
Mit der Vorlage wird eine grundlegende Win32-Anwendung erstellt, die Folgendes enthält:
Einen Einstiegspunkt für die Anwendung.
Ein Fenster mit einer zugeordneten Fensterprozedur (WndProc).
Ein Menü mit den Überschriften Datei und Hilfe. Das Menü Datei enthält das Element Beenden, mit dem die Anwendung geschlossen wird. Das Menü Hilfe enthält das Element Info, mit dem ein einfaches Dialogfeld gestartet wird.
Bevor Sie mit dem Schreiben von Code zum Hosten des WPF-Inhalts beginnen, müssen Sie zwei Änderungen an der grundlegenden Vorlage vornehmen.
Die erste besteht darin, das Projekt als verwalteten Code zu kompilieren. Standardmäßig wird das Projekt als nicht verwalteter Code kompiliert. Da aber WPF in verwaltetem Code implementiert wird, muss das Projekt entsprechend kompiliert werden.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie im Kontextmenü Eigenschaften aus, um das Dialogfeld Eigenschaftenseiten zu starten.
Wählen Sie links im Strukturansichtsbereich Konfigurationseigenschaften aus.
Wählen Sie in der Liste Projektstandards im rechten Bereich Unterstützung für Common Language Runtime aus.
Wählen Sie im Dropdownlistenfeld Common Language Runtime-Unterstützung (/clr) aus.
Hinweis |
---|
Das Compilerflag ermöglicht es Ihnen, in der Anwendung verwalteten Code zu verwenden. Der nicht verwaltete Code wird jedoch wie zuvor kompiliert. |
WPF verwendet das Singlethread-Apartmentthreadingmodell (STA). Damit es ordnungsgemäß mit dem WPF-Inhaltscode funktioniert, müssen Sie das Threadingmodell der Anwendung auf STA festlegen, indem Sie ein Attribut auf den Einstiegspunkt anwenden.
[System::STAThreadAttribute] //Needs to be an STA thread to play nicely with WPF
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
Hosten des WPF-Inhalts
Der WPF-Inhalt ist eine einfache Anwendung für die Adresseingabe. Sie besteht aus mehreren TextBox-Steuerelementen zum Erfassen von Benutzername, Adresse usw. Darüber hinaus sind die beiden Button-Steuerelemente OK und Abbrechen enthalten. Wenn der Benutzer auf OK klickt, ruft der Click-Ereignishandler der Schaltfläche Daten aus den TextBox-Steuerelementen ab, weist sie entsprechenden Eigenschaften zu und löst das benutzerdefinierte Ereignis OnButtonClicked aus. Wenn der Benutzer auf Abbrechen klickt, löst der Handler einfach OnButtonClicked aus. Das Ereignisargumentobjekt für OnButtonClicked enthält ein boolesches Feld, das angibt, auf welche Schaltfläche geklickt wurde.
Der Code zum Hosten des WPF-Inhalts wird in einem Handler für die WM_CREATE-Benachrichtigung im Hostfenster implementiert.
case WM_CREATE :
GetClientRect(hWnd, &rect);
wpfHwnd = GetHwnd(hWnd, rect.right-375, 0, 375, 250);
CreateDataDisplay(hWnd, 275, rect.right-375, 375);
CreateRadioButtons(hWnd);
break;
Die GetHwnd-Methode übernimmt Größen- und Positionsinformationen sowie das übergeordnete Fensterhandle und gibt das Fensterhandle des gehosteten WPF-Inhalts zurück.
Hinweis |
---|
Sie können keine #using-Direktive für den System::Windows::Interop-Namespace verwenden.Dies würde zu einem Namenskonflikt zwischen der MSG-Struktur in diesem Namespace und der in winuser.h deklarierten MSG-Struktur führen.Verwenden Sie stattdessen vollqualifizierte Namen, um auf den Inhalt dieses Namespace zuzugreifen. |
HWND GetHwnd(HWND parent, int x, int y, int width, int height)
{
System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters(
"hi" // NAME
);
sourceParams->PositionX = x;
sourceParams->PositionY = y;
sourceParams->Height = height;
sourceParams->Width = width;
sourceParams->ParentWindow = IntPtr(parent);
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD; // style
System::Windows::Interop::HwndSource^ source = gcnew System::Windows::Interop::HwndSource(*sourceParams);
WPFPage ^myPage = gcnew WPFPage(width, height);
//Assign a reference to the WPF page and a set of UI properties to a set of static properties in a class
//that is designed for that purpose.
WPFPageHost::hostedPage = myPage;
WPFPageHost::initBackBrush = myPage->Background;
WPFPageHost::initFontFamily = myPage->DefaultFontFamily;
WPFPageHost::initFontSize = myPage->DefaultFontSize;
WPFPageHost::initFontStyle = myPage->DefaultFontStyle;
WPFPageHost::initFontWeight = myPage->DefaultFontWeight;
WPFPageHost::initForeBrush = myPage->DefaultForeBrush;
myPage->OnButtonClicked += gcnew WPFPage::ButtonClickHandler(WPFButtonClicked);
source->RootVisual = myPage;
return (HWND) source->Handle.ToPointer();
}
Sie können den WPF-Inhalt nicht direkt im Anwendungsfenster hosten. Erstellen Sie zuerst ein HwndSource-Objekt, um den WPF-Inhalt zu umschließen. Dieses Objekt stellt im Wesentlichen ein Fenster zum Hosten von WPF-Inhalt dar. Sie hosten das HwndSource-Objekt im übergeordneten Fenster, indem Sie es als untergeordnetes Element eines Win32-Fensters erstellen, das Teil Ihrer Anwendung ist. Die HwndSource-Konstruktorparameter enthalten größtenteils dieselben Informationen, die Sie beim Erstellen eines untergeordneten Win32-Fensters an CreateWindow übergeben würden.
Als Nächstes erstellen Sie eine Instanz des WPF-Inhaltsobjekts. In diesem Fall wird der WPF-Inhalt unter Verwendung von C++/CLI als separate Klasse, nämlich WPFPage, implementiert. Sie können den WPF-Inhalt auch mit XAML implementieren. Dazu müssen Sie allerdings ein separates Projekt einrichten und den WPF-Inhalt als DLL erstellen. Sie können dem Projekt einen Verweis auf diese DLL hinzufügen und ihn zum Erstellen einer Instanz des WPF-Inhalts verwenden.
Sie zeigen den WPF-Inhalt im untergeordneten Fenster an, indem Sie der RootVisual-Eigenschaft von HwndSource einen Verweis auf den WPF-Inhalt zuweisen.
Durch die nächste Codezeile wird ein Ereignishandler (WPFButtonClicked) an das OnButtonClicked-Ereignis des WPF-Inhalts angefügt. Dieser Handler wird aufgerufen, wenn der Benutzer auf die Schaltfläche OK oder Abbrechen klickt. Weitere Informationen zu diesem Ereignishandler finden Sie unter Kommunizieren mit dem WPF-Inhalt.
Durch die letzte angezeigte Codezeile wird das Fensterhandle (HWND) zurückgegeben, das dem HwndSource-Objekt zugeordnet ist. Sie können dieses Handle vom Win32-Code zum Senden von Meldungen an das gehostete Fenster verwenden, auch wenn das in diesem Beispiel nicht vorgeführt wird. Das HwndSource-Objekt löst jedes Mal ein Ereignis aus, wenn es eine Meldung empfängt. Rufen Sie zum Verarbeiten der Meldungen die AddHook-Methode auf, um einen Meldungshandler anzufügen und anschließend die Meldungen in diesem Handler zu verarbeiten.
Verweisen auf den WPF-Inhalt
Bei vielen Anwendungen kommunizieren Sie zu einem späteren Zeitpunkt mit dem WPF-Inhalt. Angenommen, Sie möchten die WPF-Inhaltseigenschaften ändern oder vom HwndSource-Objekt andere WPF-Inhalte hosten lassen. Dazu benötigen Sie einen Verweis auf das HwndSource-Objekt oder den WPF-Inhalt. Das HwndSource-Objekt und der zugehörige WPF-Inhalt verbleiben im Arbeitsspeicher, bis Sie das Fensterhandle zerstören. Die Variable, die Sie dem HwndSource-Objekt zuweisen, verlässt jedoch den Gültigkeitsbereich, sobald die Steuerung von der Fensterprozedur zurückgegeben wird. Eine übliche Methode, dieses Problem bei Win32-Anwendungen zu umgehen, besteht in der Verwendung einer statischen oder globalen Variablen. Leider können Sie diesen Variablentypen kein verwaltetes Objekt zuweisen. Sie können das Fensterhandle, das dem HwndSource-Objekt zugeordnet ist, einer globalen oder statischen Variablen zuweisen. Dadurch erhalten Sie aber noch keinen Zugriff auf das Objekt.
Die einfachste Lösung für dieses Problem besteht darin, eine verwaltete Klasse zu implementieren, die eine Reihe von statischen Feldern mit Verweisen auf alle verwalteten Objekte enthält, auf die Sie zugreifen müssen. Im Beispiel wird die WPFPageHost-Klasse für einen Verweis auf den WPF-Inhalt verwendet. Außerdem soll sie die Anfangswerte einiger Eigenschaften enthalten, die später vom Benutzer geändert werden können. Dies wird im Header definiert.
public ref class WPFPageHost
{
public:
WPFPageHost();
static WPFPage^ hostedPage;
//initial property settings
static System::Windows::Media::Brush^ initBackBrush;
static System::Windows::Media::Brush^ initForeBrush;
static System::Windows::Media::FontFamily^ initFontFamily;
static System::Windows::FontStyle initFontStyle;
static System::Windows::FontWeight initFontWeight;
static double initFontSize;
};
Der letzte Teil der GetHwnd-Funktion weist diesen Feldern Werte zur späteren Verwendung zu, während myPage im Gültigkeitsbereich verbleibt.
Kommunizieren mit dem WPF-Inhalt
Es gibt zwei Arten der Kommunikation mit dem WPF-Inhalt. Die Anwendung empfängt Informationen vom WPF-Inhalt, wenn der Benutzer auf die Schaltflächen OK oder Abbrechen klickt. Die Anwendung verfügt auch über eine UI, die es dem Benutzer ermöglicht, zahlreiche WPF-Inhaltseigenschaften zu ändern, z. B. Hintergrundfarbe oder Standardschriftgröße.
Wie oben erwähnt löst der WPF-Inhalt ein OnButtonClicked-Ereignis aus, wenn der Benutzer auf eine der beiden Schaltflächen klickt. Die Anwendung fügt einen Handler an dieses Ereignis an, um die entsprechenden Benachrichtigungen zu empfangen. Nach dem Klicken auf die Schaltfläche OK ruft der Handler die Benutzerinformationen aus dem WPF-Inhalt ab und zeigt sie in einer Reihe von statischen Steuerelementen an.
void WPFButtonClicked(Object ^sender, MyPageEventArgs ^args)
{
if(args->IsOK) //display data if OK button was clicked
{
WPFPage ^myPage = WPFPageHost::hostedPage;
LPCWSTR userName = (LPCWSTR) InteropServices::Marshal::StringToHGlobalAuto("Name: " + myPage->EnteredName).ToPointer();
SetWindowText(nameLabel, userName);
LPCWSTR userAddress = (LPCWSTR) InteropServices::Marshal::StringToHGlobalAuto("Address: " + myPage->EnteredAddress).ToPointer();
SetWindowText(addressLabel, userAddress);
LPCWSTR userCity = (LPCWSTR) InteropServices::Marshal::StringToHGlobalAuto("City: " + myPage->EnteredCity).ToPointer();
SetWindowText(cityLabel, userCity);
LPCWSTR userState = (LPCWSTR) InteropServices::Marshal::StringToHGlobalAuto("State: " + myPage->EnteredState).ToPointer();
SetWindowText(stateLabel, userState);
LPCWSTR userZip = (LPCWSTR) InteropServices::Marshal::StringToHGlobalAuto("Zip: " + myPage->EnteredZip).ToPointer();
SetWindowText(zipLabel, userZip);
}
else
{
SetWindowText(nameLabel, L"Name: ");
SetWindowText(addressLabel, L"Address: ");
SetWindowText(cityLabel, L"City: ");
SetWindowText(stateLabel, L"State: ");
SetWindowText(zipLabel, L"Zip: ");
}
}
Der Handler empfängt ein benutzerdefiniertes Ereignisargumentobjekt vom WPF-Inhalt: MyPageEventArgs. Die IsOK-Eigenschaft des Objekts wird auf true festgelegt, wenn auf die Schaltfläche OK geklickt wurde, und auf false, wenn auf die Schaltfläche Abbrechen geklickt wurde.
Nach dem Klicken auf die Schaltfläche OK ruft der Handler einen Verweis auf den WPF-Inhalt aus der Containerklasse ab. Anschließend ruft er die Benutzerinformationen ab, die in den zugeordneten WPF-Inhaltseigenschaften enthalten sind. Dabei werden die statischen Steuerelemente zum Anzeigen der Informationen im übergeordneten Fenster verwendet. Da die WPF-Inhaltsdaten in Form einer verwalteten Zeichenfolge vorliegen, müssen Sie gemarshallt werden, damit sie von einem Win32-Steuerelement verwendet werden können. Wenn auf die Schaltfläche Abbrechen geklickt wurde, löscht der Handler die Daten aus den statischen Steuerelementen.
Die UI der Anwendung stellt eine Reihe von Optionsfeldern bereit, mit denen der Benutzer die Hintergrundfarbe des WPF-Inhalts und verschiedene Schriftarteigenschaften ändern kann. Das folgende Beispiel ist ein Auszug aus der Fensterprozedur der Anwendung (WndProc) und deren Meldungsbehandlung, die zahlreiche Eigenschaften für verschiedene Meldungen festlegt, darunter die Hintergrundfarbe. Die anderen sind ähnlich und werden nicht angezeigt. Sehen Sie sich das vollständige Beispiel an, um ausführliche Informationen und Kontext zu erhalten.
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
//Menu selections
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
//RadioButtons
case IDC_ORIGINALBACKGROUND :
WPFPageHost::hostedPage->Background = WPFPageHost::initBackBrush;
break;
case IDC_LIGHTGREENBACKGROUND :
WPFPageHost::hostedPage->Background = gcnew SolidColorBrush(Colors::LightGreen);
break;
case IDC_LIGHTSALMONBACKGROUND :
WPFPageHost::hostedPage->Background = gcnew SolidColorBrush(Colors::LightSalmon);
break;
Wenn Sie die Hintergrundfarbe festlegen möchten, rufen Sie einen Verweis auf den WPF-Inhalt (hostedPage) von WPFPageHost ab, und legen Sie die Eigenschaft für die Hintergrundfarbe auf die entsprechende Farbe fest. Im Beispiel werden drei Farboptionen verwendet: die ursprüngliche Farbe, hellgrün oder helles lachsrot. Die ursprüngliche Hintergrundfarbe wird als statisches Feld in der WPFPageHost-Klasse gespeichert. Um die anderen beiden festzulegen, erstellen Sie ein neues SolidColorBrush-Objekt und übergeben dem Konstruktor einen statischen Farbwert aus dem Colors-Objekt.
Implementieren der WPF-Seite
Sie können den WPF-Inhalt hosten und verwenden, ohne die tatsächliche Implementierung zu kennen. Wurde der WPF-Inhalt in einer separaten DLL gepackt, kann er in einer beliebigen common language runtime (CLR)-Sprache erstellt worden sein. Es folgt eine kurze exemplarische Vorgehensweise zur C++/CLI-Implementierung, die in diesem Beispiel verwendet wird. Dieser Abschnitt enthält folgende Unterabschnitte.
Layout
Zurückgeben der Daten an das Hostfenster
Festlegen der WPF-Eigenschaften
Layout
Die Elemente der UI im WPF-Inhalt bestehen aus fünf TextBox-Steuerelementen mit zugehörigen Label-Steuerelementen: Name, Adresse, Stadt, Bundesland und Postleitzahl. Darüber hinaus sind die beiden Button-Steuerelemente OK und Abbrechen enthalten.
Der WPF-Inhalt wird in der WPFPage-Klasse implementiert. Das Layout wird mit einem Grid-Layoutelement behandelt. Die Klasse erbt von Grid, wodurch sie zum Stammelement des WPF-Inhalts wird.
Der Konstruktor des WPF-Inhalts übernimmt die erforderliche Breite und Höhe und passt die Größe von Grid entsprechend an. Anschließend wird das grundlegende Layout definiert. Dazu wird eine Gruppe von ColumnDefinition-Objekten und RowDefinition-Objekten erstellt, die jeweils der ColumnDefinitions-Auflistung und der RowDefinitions-Auflistung der Grid-Objektbasis hinzugefügt werden. Dadurch wird ein Raster von fünf Zeilen und sieben Spalten definiert. Die Abmessungen werden durch den Inhalt der Zellen bestimmt.
WPFPage::WPFPage(int allottedWidth, int allotedHeight)
{
array<ColumnDefinition ^> ^ columnDef = gcnew array<ColumnDefinition ^> (4);
array<RowDefinition ^> ^ rowDef = gcnew array<RowDefinition ^> (6);
this->Height = allotedHeight;
this->Width = allottedWidth;
this->Background = gcnew SolidColorBrush(Colors::LightGray);
//Set up the Grid's row and column definitions
for(int i=0; i<4; i++)
{
columnDef[i] = gcnew ColumnDefinition();
columnDef[i]->Width = GridLength(1, GridUnitType::Auto);
this->ColumnDefinitions->Add(columnDef[i]);
}
for(int i=0; i<6; i++)
{
rowDef[i] = gcnew RowDefinition();
rowDef[i]->Height = GridLength(1, GridUnitType::Auto);
this->RowDefinitions->Add(rowDef[i]);
}
Anschließend fügt der Konstruktor die UInelemente zu Grid hinzu. Das erste Element ist der Titeltext. Dabei handelt es sich um ein Label-Steuerelement, das in der ersten Zeile des Rasters zentriert ist.
//Add the title
titleText = gcnew Label();
titleText->Content = "Simple WPF Control";
titleText->HorizontalAlignment = System::Windows::HorizontalAlignment::Center;
titleText->Margin = Thickness(10, 5, 10, 0);
titleText->FontWeight = FontWeights::Bold;
titleText->FontSize = 14;
Grid::SetColumn(titleText, 0);
Grid::SetRow(titleText, 0);
Grid::SetColumnSpan(titleText, 4);
this->Children->Add(titleText);
Die nächste Zeile enthält das Label-Steuerelement für den Namen und das zugehörige TextBox-Steuerelement. Da für jedes aus Beschriftung und Textfeld bestehende Paar derselbe Code verwendet wird, wird er in einem Paar privater Methoden platziert und für alle fünf Paare aus Beschriftung/Textfeld verwendet. Durch die Methoden wird das entsprechende Steuerelement erstellt, und es werden die statischen Methoden SetColumn und SetRow der Grid-Klasse aufgerufen, um die Steuerelemente in der entsprechenden Zelle zu platzieren. Nachdem das Steuerelement erstellt wurde, wird im Beispiel die Add-Methode für die Children-Eigenschaft von Grid aufgerufen, um das Steuerelement zum Raster hinzuzufügen. Der Code zum Hinzufügen der übrigen Beschriftung/Textfeld-Paare ist ähnlich. Sehen Sie sich den Beispielcode an, um ausführliche Informationen zu erhalten.
//Add the Name Label and TextBox
nameLabel = CreateLabel(0, 1, "Name");
this->Children->Add(nameLabel);
nameTextBox = CreateTextBox(1, 1, 3);
this->Children->Add(nameTextBox);
Die Implementierung der beiden Methoden wird wie folgt durchgeführt:
Label ^WPFPage::CreateLabel(int column, int row, String ^ text)
{
Label ^ newLabel = gcnew Label();
newLabel->Content = text;
newLabel->Margin = Thickness(10, 5, 10, 0);
newLabel->FontWeight = FontWeights::Normal;
newLabel->FontSize = 12;
Grid::SetColumn(newLabel, column);
Grid::SetRow(newLabel, row);
return newLabel;
}
TextBox ^WPFPage::CreateTextBox(int column, int row, int span)
{
TextBox ^newTextBox = gcnew TextBox();
newTextBox->Margin = Thickness(10, 5, 10, 0);
Grid::SetColumn(newTextBox, column);
Grid::SetRow(newTextBox, row);
Grid::SetColumnSpan(newTextBox, span);
return newTextBox;
}
Abschließend werden in dem Beispiel die Schaltflächen OK und Abbrechen hinzugefügt, und es wird ein Ereignishandler an ihre Click-Ereignisse angefügt.
//Add the Buttons and atttach event handlers
okButton = CreateButton(0, 5, "OK");
cancelButton = CreateButton(1, 5, "Cancel");
this->Children->Add(okButton);
this->Children->Add(cancelButton);
okButton->Click += gcnew RoutedEventHandler(this, &WPFPage::ButtonClicked);
cancelButton->Click += gcnew RoutedEventHandler(this, &WPFPage::ButtonClicked);
Zurückgeben der Daten an das Hostfenster
Das Click-Ereignis wird durch Klicken auf eine der Schaltflächen ausgelöst. Das Hostfenster könnte einfach Handler an diese Ereignisse anfügen und die Daten direkt aus den TextBox-Steuerelementen abrufen. Im Beispiel wird ein weniger direkter Ansatz verwendet. Click wird innerhalb des WPF-Inhalts behandelt, und anschließend wird ein benutzerdefiniertes OnButtonClicked-Ereignis ausgelöst, um den WPF-Inhalt zu benachrichtigen. Dadurch kann der WPF-Inhalt die Gültigkeit einiger Parameter überprüfen, bevor der Host benachrichtigt wird. Der Handler fragt den Text aus den TextBox-Steuerelementen ab und weist ihn öffentlichen Eigenschaften zu, aus denen der Host die Informationen abrufen kann.
Die Ereignisdeklaration in WPFPage.h:
public:
delegate void ButtonClickHandler(Object ^, MyPageEventArgs ^);
WPFPage();
WPFPage(int height, int width);
event ButtonClickHandler ^OnButtonClicked;
Der Click-Ereignishandler in WPFPage.cpp:
void WPFPage::ButtonClicked(Object ^sender, RoutedEventArgs ^args)
{
//TODO: validate input data
bool okClicked = true;
if(sender == cancelButton)
okClicked = false;
EnteredName = nameTextBox->Text;
EnteredAddress = addressTextBox->Text;
EnteredCity = cityTextBox->Text;
EnteredState = stateTextBox->Text;
EnteredZip = zipTextBox->Text;
OnButtonClicked(this, gcnew MyPageEventArgs(okClicked));
}
Festlegen der WPF-Eigenschaften
Der Win32-Host ermöglicht es dem Benutzer, mehrere WPF Inhaltseigenschaften zu ändern. In Win32 müssen lediglich Eigenschaften geändert werden. Die Implementierung in der WPF-Inhaltsklasse ist etwas komplizierter, da es keine einzelne globale Eigenschaft gibt, mit der die Schriftarten für alle Steuerelemente gesteuert werden. Stattdessen wird die entsprechende Eigenschaft für die einzelnen Steuerelemente in den set-Accessoren der Eigenschaften geändert. Im folgenden Beispiel wird der Code für die DefaultFontFamily-Eigenschaft angezeigt. Durch Festlegen der Eigenschaft wird eine private Methode aufgerufen, mit der die FontFamily-Eigenschaften für verschiedene Steuerelemente festgelegt werden.
Aus WPFPage.h:
property FontFamily^ DefaultFontFamily
{
FontFamily^ get() {return _defaultFontFamily;}
void set(FontFamily^ value) {SetFontFamily(value);}
};
Aus WPFPage.cpp:
void WPFPage::SetFontFamily(FontFamily^ newFontFamily)
{
_defaultFontFamily = newFontFamily;
titleText->FontFamily = newFontFamily;
nameLabel->FontFamily = newFontFamily;
addressLabel->FontFamily = newFontFamily;
cityLabel->FontFamily = newFontFamily;
stateLabel->FontFamily = newFontFamily;
zipLabel->FontFamily = newFontFamily;
}