Passo a passo: hospedagem de conteúdo do WPF no Win32
O WPF (Windows Presentation Foundation) fornece um ambiente avançado para a criação de aplicativos. No entanto, quando você tem um investimento substancial no código Win32, pode ser mais eficaz adicionar funcionalidade do WPF ao seu aplicativo em vez de reescrever seu código original. O WPF fornece um mecanismo simples para hospedar conteúdo do WPF em uma janela do Win32.
Este tutorial descreve como escrever um aplicativo de exemplo, Hospedar conteúdo do WPF em um exemplo de janela Win32, que hospeda conteúdo do WPF em uma janela Win32. Você pode estender este exemplo para hospedar qualquer janela do Win32. Como envolve a combinação de código gerenciado e não gerenciado, o aplicativo é escrito em C++/CLI.
Requisitos
Este tutorial pressupõe uma familiaridade básica com a programação do WPF e do Win32. Para obter uma introdução básica à programação do WPF, consulte Introdução. Para obter uma introdução à programação Win32, você deve referenciar qualquer um dos inúmeros livros sobre o assunto, em particular Programação Windows por Charles Petzold.
Como o exemplo que acompanha este tutorial é implementado na C++/CLI, este tutorial assume familiaridade com o uso do C++ para programar a API do Windows, além de uma compreensão da programação de código gerenciado. Familiaridade com C++/CLI é útil, mas não é essencial.
Nota
Este tutorial inclui vários exemplos de código do exemplo associado. No entanto, para legibilidade, ele não inclui o código de exemplo completo. Para obter o código de exemplo completo, consulte Hospedando Conteúdo WPF em um exemplo de janela Win32.
O procedimento básico
Esta seção descreve o procedimento básico que você usa para hospedar o conteúdo do WPF em uma janela do Win32. As seções restantes explicam os detalhes de cada etapa.
A chave para hospedar o conteúdo do WPF em uma janela do Win32 é a classe HwndSource. Essa classe encapsula o conteúdo do WPF em uma janela do Win32, permitindo que ele seja incorporado à interface do usuário (UI) como uma janela filha. A abordagem a seguir combina o Win32 e o WPF em um único aplicativo.
Implemente o conteúdo do WPF como uma classe gerenciada.
Implemente um aplicativo do Windows com C++/CLI. Se você estiver começando com um aplicativo existente e um código C++ não gerenciado, geralmente poderá habilitá-lo a chamar código gerenciado alterando as configurações do projeto para incluir o sinalizador do compilador
/clr
.Defina o modelo de threading como STA (apartamento com thread único).
Manipule a notificação WM_CREATEem seu procedimento de janela e faça o seguinte:
Crie um novo objeto HwndSource com a janela pai como seu parâmetro
parent
.Crie uma instância da classe de conteúdo do WPF.
Atribua uma referência ao objeto de conteúdo do WPF à propriedade RootVisual do HwndSource.
Obtenha o HWND para o conteúdo. A propriedade Handle do objeto HwndSource contém o identificador de janela (HWND). Para obter um HWND que você pode usar na parte não gerenciada do aplicativo, converta
Handle.ToPointer()
em um HWND.
Implemente uma classe gerenciada que contenha um campo estático para manter uma referência ao conteúdo do WPF. Essa classe permite que você obtenha uma referência ao conteúdo do WPF do código Win32.
Atribua o conteúdo do WPF ao campo estático.
Receba notificações do conteúdo do WPF anexando um manipulador a um ou mais eventos do WPF.
Comunique-se com o conteúdo do WPF usando a referência armazenada no campo estático para definir propriedades e assim por diante.
Nota
Você também pode usar o conteúdo do WPF. No entanto, você precisará compilá-la separadamente como uma DLL (biblioteca de vínculo dinâmico) e referenciar essa DLL do aplicativo Win32. O restante do procedimento é semelhante ao descrito acima.
Implementando o aplicativo host
Esta seção descreve como hospedar conteúdo do WPF em um aplicativo Win32 básico. O conteúdo em si é implementado em C++/CLI como uma classe gerenciada. Na maioria das vezes, é uma programação simples do WPF. Os principais aspectos da implementação de conteúdo do WPF são discutidos em Implementação de Conteúdo.
O aplicativo básico
O ponto de partida para o aplicativo host era criar um modelo do Visual Studio 2005.
Abra o Visual Studio 2005 e selecione Novo Projeto no menu Arquivo.
Selecione Win32 na lista de tipos de projeto do Visual C++. Se o idioma padrão não for C++, você encontrará esses tipos de projeto em Outros Idiomas.
Selecione um modelo de do projeto
Win32, atribua um nome ao projeto e clique em OK para iniciar odo Assistente de Aplicativo doWin32. Aceite as configurações padrão do assistente e clique em Concluir para iniciar o projeto.
O modelo cria um aplicativo Win32 básico, incluindo:
Um ponto de entrada para o aplicativo.
Uma janela, com um procedimento de janela associado (WndProc).
Um menu com os títulos Arquivo e Ajuda. O menu Arquivo tem um item Sair que fecha o aplicativo. O menu Ajuda tem um item Sobre que inicia uma caixa de diálogo simples.
Antes de começar a escrever código para hospedar o conteúdo do WPF, você precisa fazer duas modificações no modelo básico.
A primeira é compilar o projeto como código gerenciado. Por padrão, o projeto é compilado como código não gerenciado. No entanto, como o WPF é implementado no código gerenciado, o projeto deve ser compilado adequadamente.
Clique com o botão direito do mouse no nome do projeto no do Gerenciador de Soluções
e selecione Propriedades no menu de contexto para iniciar a caixa de diálogo Páginas de Propriedades do. Selecione Propriedades de Configuração na visualização em árvore no painel esquerdo.
Selecione suporte ao Common Language Runtime da lista Padrões do Projeto no painel direito.
Selecione de Suporte do Common Language Runtime (/clr) na caixa de listagem suspensa.
Nota
Esse sinalizador do compilador permite que você use código gerenciado em seu aplicativo, mas seu código não gerenciado ainda será compilado como antes.
O WPF usa o modelo de encadeamento STA (apartamento de thread único). Para trabalhar corretamente com o código de conteúdo do WPF, você deve definir o modelo de threading do aplicativo como STA aplicando um atributo ao ponto de entrada.
[System::STAThreadAttribute] //Needs to be an STA thread to play nicely with WPF
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
Hospedando o conteúdo do WPF
O conteúdo do WPF é um aplicativo de entrada de endereço simples. Ele consiste em vários controles TextBox destinados a coletar o nome de usuário, o endereço, entre outros. Há também dois controles de Button, OK e Cancelar. Quando o usuário clica OK, o manipulador de eventos Click do botão coleta os dados dos controles de TextBox, atribui-os às propriedades correspondentes e gera um evento personalizado, OnButtonClicked
. Quando o usuário clica Cancelar, o manipulador simplesmente gera OnButtonClicked
. O objeto de argumento de evento para OnButtonClicked
contém um campo booliano que indica qual botão foi clicado.
O código para hospedar o conteúdo do WPF é implementado em um manipulador para a notificação de WM_CREATE na janela do host.
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;
O método GetHwnd
usa informações de tamanho e posição mais o identificador da janela pai e retorna o identificador de janela do conteúdo do WPF hospedado.
Nota
Você não pode usar uma diretiva #using
para o namespace System::Windows::Interop
. Isso cria uma colisão de nome entre a estrutura MSG nesse namespace e a estrutura MSG declarada em winuser.h. Em vez disso, você deve usar nomes totalmente qualificados para acessar o conteúdo desse namespace.
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();
}
Não é possível hospedar o conteúdo do WPF diretamente na janela do aplicativo. Em vez disso, primeiro você cria um objeto HwndSource para encapsular o conteúdo do WPF. Esse objeto é basicamente uma janela projetada para hospedar um conteúdo do WPF. Você hospeda o objeto HwndSource na janela principal, criando-o como filho de uma janela Win32 que faz parte do aplicativo. Os parâmetros do construtor HwndSource contêm as mesmas informações que você passaria para CreateWindow ao criar uma janela filho do Win32.
Em seguida, você cria uma instância do objeto de conteúdo do WPF. Nesse caso, o conteúdo do WPF é implementado como uma classe separada, WPFPage
, usando C++/CLI. Você também pode implementar o conteúdo do WPF com XAML. No entanto, para fazer isso, você precisa configurar um projeto separado e criar o conteúdo do WPF como uma DLL. Você pode adicionar uma referência a essa DLL ao seu projeto e usar essa referência para criar uma instância do conteúdo do WPF.
Você exibe o conteúdo do WPF na janela filho ao atribuir uma referência desse conteúdo à propriedade RootVisual do HwndSource.
A próxima linha de código anexa um manipulador de eventos, WPFButtonClicked
, ao evento de conteúdo do WPF OnButtonClicked
. Esse manipulador é chamado quando o usuário clica no botão OK ou Cancelar. Consulte o conteúdo de communicating_with_the_WPF para uma discussão mais aprofundada sobre o manipulador de eventos.
A linha final de código mostrada retorna o HWND (identificador de janela) associado ao objeto HwndSource. Você pode usar esse identificador do código Win32 para enviar mensagens para a janela hospedada, embora o exemplo não o faça. O objeto HwndSource gera um evento sempre que recebe uma mensagem. Para processar as mensagens, chame o método AddHook para anexar um manipulador de mensagens e processar as mensagens nesse manipulador.
Mantendo uma referência ao conteúdo do WPF
Para muitos aplicativos, você desejará se comunicar com o conteúdo do WPF mais tarde. Por exemplo, você pode querer modificar as propriedades de conteúdo do WPF, ou talvez que o objeto HwndSource hospede um conteúdo WPF diferente. Para fazer isso, você precisa de uma referência ao objeto HwndSource ou ao conteúdo do WPF. O objeto HwndSource e seu conteúdo do WPF associado permanecem na memória até que você destrua o identificador da janela. No entanto, a variável que você atribuir ao objeto HwndSource sairá do escopo assim que você retornar do procedimento de janela. A maneira usual de lidar com esse problema em aplicativos Win32 é usar uma variável estática ou global. Infelizmente, você não pode atribuir um objeto gerenciado a esses tipos de variáveis. Você pode atribuir o identificador de janela associado a HwndSource objeto a uma variável global ou estática, mas isso não fornece acesso ao objeto em si.
A solução mais simples para esse problema é implementar uma classe gerenciada que contenha um conjunto de campos estáticos para manter referências a todos os objetos gerenciados aos quais você precisa de acesso. O exemplo usa a classe WPFPageHost
para manter uma referência ao conteúdo do WPF, além dos valores iniciais de várias de suas propriedades que podem ser alteradas posteriormente pelo usuário. Isso é definido no cabeçalho.
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;
};
A última parte da função GetHwnd
atribui valores a esses campos para uso posterior enquanto myPage
ainda está no escopo.
Comunicando-se com o conteúdo do WPF
Há dois tipos de comunicação com o conteúdo do WPF. O aplicativo recebe informações do conteúdo do WPF quando o usuário clica nos botões OK ou Cancelar. O aplicativo também tem uma interface do usuário que permite que o usuário altere várias propriedades de conteúdo do WPF, como a cor da tela de fundo ou o tamanho da fonte padrão.
Conforme mencionado acima, quando o usuário clica em qualquer botão, o conteúdo do WPF gera um evento OnButtonClicked
. O aplicativo anexa um manipulador a esse evento para receber essas notificações. Se o botão OK foi clicado, o manipulador obtém as informações do usuário do conteúdo do WPF e exibe-as em um conjunto de controles estáticos.
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: ");
}
}
O manipulador recebe um objeto de argumento de evento personalizado do conteúdo do WPF, MyPageEventArgs
. A propriedade IsOK
do objeto será definida como true
se o botão OK tiver sido clicado e false
se o botão cancelar tiver sido clicado.
Se o botão OK foi clicado, o manipulador obtém uma referência ao conteúdo do WPF da classe de contêiner. Em seguida, ele coleta as informações do usuário mantidas pelas propriedades de conteúdo do WPF associadas e usa os controles estáticos para exibir as informações na janela pai. Como os dados de conteúdo do WPF estão na forma de uma cadeia de caracteres gerenciada, eles precisam ser empacotados para uso por um controle Win32. Se o botão Cancelar tiver sido clicado, o manipulador limpará os dados dos controles estáticos.
A UI do aplicativo fornece um conjunto de botões de opção que permitem ao usuário modificar a cor de fundo do conteúdo do WPF e várias propriedades relacionadas à fonte. O exemplo a seguir é um trecho do procedimento de janela do aplicativo (WndProc) e seu tratamento de mensagens que define várias propriedades em mensagens diferentes, incluindo a cor da tela de fundo. Os outros são semelhantes e não são mostrados. Consulte o exemplo completo para obter detalhes e contexto.
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;
Para definir a cor da tela de fundo, obtenha uma referência ao conteúdo do WPF (hostedPage
) de WPFPageHost
e defina a propriedade de cor da tela de fundo como a cor apropriada. O exemplo usa três opções de cores: a cor original, verde claro ou salmão claro. A cor da tela de fundo original é armazenada como um campo estático na classe WPFPageHost
. Para definir os outros dois, crie um novo objeto SolidColorBrush e passe ao construtor um valor de cores estáticas do objeto Colors.
Implementando a página do WPF
Você pode hospedar e usar o conteúdo do WPF sem qualquer conhecimento da implementação real. Se o conteúdo do WPF tivesse sido empacotado em uma DLL separada, ele poderia ter sido criado em qualquer linguagem CLR (Common Language Runtime). Veja a seguir um breve passo a passo da implementação do C++/CLI que é usado no exemplo. Esta seção contém as seguintes subseções.
Disposição
Os elementos de interface do usuário no conteúdo do WPF consistem em cinco controles TextBox, com controles Label associados: Nome, Endereço, Cidade, Estado e Zip. Há também dois controles Button, OK e Cancelar
O conteúdo do WPF é implementado na classe WPFPage
. O layout é gerenciado com o elemento de layout Grid. A classe herda de Grid, o que efetivamente o torna o elemento raiz de conteúdo do WPF.
O construtor de conteúdo do WPF usa a largura e a altura necessárias e dimensiona a Grid adequadamente. Em seguida, ele define o layout básico criando um conjunto de objetos ColumnDefinition e RowDefinition e adicionando-os à base de objetos GridColumnDefinitions e coleções RowDefinitions, respectivamente. Isso define uma grade de cinco linhas e sete colunas, com as dimensões determinadas pelo conteúdo das células.
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]);
}
Em seguida, o construtor adiciona os elementos da interface ao Grid. O primeiro elemento é o texto do título, que é um controle Label centralizado na primeira linha da grade.
//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);
A próxima linha contém o controle Name Label e seu controle associado TextBox. Como o mesmo código é usado para cada par rótulo/caixa de texto, ele é colocado em um par de métodos privados e usado para todos os cinco pares de rótulo/caixa de texto. Os métodos criam o controle apropriado e chamam os métodos estáticos SetColumn e SetRow da classe Grid para colocar os controles na célula apropriada. Depois que o controle é criado, o exemplo chama o método Add na propriedade Children do Grid para adicionar o controle à grade. O código para adicionar os pares de rótulo/caixa de texto restantes é semelhante. Consulte o código de exemplo para obter detalhes.
//Add the Name Label and TextBox
nameLabel = CreateLabel(0, 1, "Name");
this->Children->Add(nameLabel);
nameTextBox = CreateTextBox(1, 1, 3);
this->Children->Add(nameTextBox);
A implementação dos dois métodos é a seguinte:
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;
}
Por fim, o exemplo adiciona os botões OK e Cancelar e anexa um manipulador de eventos a seus eventos de Click.
//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);
Retornando os dados para a janela do host
Quando qualquer um dos botões é clicado, o seu evento Click é acionado. A janela do host pode simplesmente anexar manipuladores a esses eventos e obter os dados diretamente dos controles de TextBox. O exemplo usa uma abordagem um pouco menos direta. Ele manipula o Click dentro do conteúdo do WPF e, em seguida, dispara um evento personalizado OnButtonClicked
para notificar o conteúdo do WPF. Isso permite que o conteúdo do WPF faça alguma validação de parâmetro antes de notificar o host. O manipulador obtém o texto dos controles de TextBox e o atribui a propriedades públicas, das quais o host pode recuperar as informações.
A declaração de evento, em WPFPage.h:
public:
delegate void ButtonClickHandler(Object ^, MyPageEventArgs ^);
WPFPage();
WPFPage(int height, int width);
event ButtonClickHandler ^OnButtonClicked;
O manipulador de eventos Click, em 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));
}
Definindo as propriedades do WPF
O host Win32 permite que o usuário altere várias propriedades de conteúdo do WPF. Do lado do Win32, é simplesmente uma questão de alterar as propriedades. A implementação na classe de conteúdo do WPF é um pouco mais complicada, pois não há uma única propriedade global que controle as fontes para todos os controles. Em vez disso, a propriedade apropriada para cada controle é alterada nos acessadores do conjunto de propriedades. O exemplo a seguir mostra o código da propriedade DefaultFontFamily
. Ao definir a propriedade, um método privado é chamado, o qual, por conseguinte, configura as propriedades FontFamily para os diversos controles.
Do arquivo WPFPage.h:
property FontFamily^ DefaultFontFamily
{
FontFamily^ get() {return _defaultFontFamily;}
void set(FontFamily^ value) {SetFontFamily(value);}
};
De 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;
}
Consulte também
- HwndSource
- de Interoperação WPF e Win32
.NET Desktop feedback