Compartilhar via


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, hospedando conteúdo do WPF em um exemplo de janela Win32, que hospeda o conteúdo do WPF em uma janela do 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 uma introdução à programação Win32, você deve referenciar qualquer um dos inúmeros livros sobre o assunto, em particular Programming 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.

Observação

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 Hospedagem de conteúdo 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 HwndSource classe. Essa classe envolve o conteúdo do WPF em uma janela do Win32, permitindo que ele seja incorporado à interface do usuário (IU) como uma janela filha. A abordagem a seguir combina o Win32 e o WPF em um único aplicativo.

  1. Implemente o conteúdo do WPF como uma classe gerenciada.

  2. Implemente um aplicativo do Windows com C++/CLI. Se você estiver começando com um aplicativo existente e 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.

  3. Defina o modelo de threading como STA (apartamento com thread único).

  4. Manipule a notificação WM_CREATE no procedimento de janela e faça o seguinte:

    1. Crie um objeto HwndSource novo com a janela pai como parâmetro parent.

    2. Crie uma instância da classe de conteúdo do WPF.

    3. Atribua uma referência ao objeto de conteúdo do WPF à propriedade RootVisual do HwndSource.

    4. Obtenha o HWND para o conteúdo. A Handle propriedade do HwndSource objeto 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.

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

  6. Atribua o conteúdo do WPF ao campo estático.

  7. Receba notificações do conteúdo do WPF anexando um manipulador a um ou mais eventos do WPF.

  8. Comunique-se com o conteúdo do WPF usando a referência armazenada no campo estático para definir propriedades e assim por diante.

Observação

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 são discutidos na implementação do conteúdo do WPF.

O aplicativo básico

O ponto de partida para o aplicativo host era criar um modelo do Visual Studio 2005.

  1. Abra o Visual Studio 2005 e selecione Novo Projeto no menu Arquivo .

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

  3. Selecione um modelo do Projeto Win32 , atribua um nome ao projeto e clique em OK para iniciar o Assistente de Aplicativo Win32.

  4. 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 Arquivo e Ajuda. O menu Arquivo tem um item Exit 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.

  1. Clique com o botão direito do mouse no nome do projeto no Gerenciador de Soluções e selecione Propriedades no menu de contexto para iniciar a caixa de diálogo Páginas de Propriedades .

  2. Selecione Propriedades de Configuração na exibição em árvore do painel esquerdo.

  3. Selecione o suporte do Common Language Runtime na lista Padrões do Projeto no painel direito.

  4. Selecione Common Language Runtime Support (/clr) na lista suspensa.

Observação

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 utiliza o modelo de threading STA (single-threaded apartment). 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 TextBox controles para coletar o nome de usuário, o endereço e assim por diante. Há também dois Button controles, OK e Cancel. Quando o usuário clica em OK, o manipulador de eventos do botão Click coleta os dados dos controles TextBox, os atribui a propriedades correspondentes e gera um evento personalizado, OnButtonClicked. Quando o usuário clica em 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 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 utiliza informações de tamanho e posição, além do identificador da janela pai, e retorna o identificador da janela do conteúdo WPF hospedado.

Observação

Você não pode usar uma #using diretiva para o System::Windows::Interop namespace. Isso cria uma colisão de nome entre a estrutura MSG do 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 HwndSource objeto para encapsular o conteúdo do WPF. Esse objeto é basicamente uma janela projetada para hospedar um conteúdo do WPF. Você hospeda o HwndSource objeto na janela pai ao criá-lo como um filho de uma janela Win32 pertencente ao seu aplicativo. Os HwndSource parâmetros do construtor 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, WPFPageusando 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 atribuindo uma referência ao conteúdo do WPF à RootVisual propriedade do HwndSource.

A próxima linha de código anexa um manipulador WPFButtonClickedde eventos ao evento de conteúdo OnButtonClicked do WPF. Esse manipulador é chamado quando o usuário clica no botão OK ou Cancelar . Consulte comunicando_com_o_conteúdo_WPF para mais discussão sobre esse manipulador de eventos.

A linha final de código mostrada retorna o HWND (identificador de janela) associado ao HwndSource objeto. 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 HwndSource objeto gera um evento sempre que recebe uma mensagem. Para processar as mensagens, chame o AddHook método para anexar um manipulador de mensagens e processe 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, fazer com que o objeto HwndSource hospede conteúdo WPF diferente. Para fazer isso, você precisa de uma referência ao HwndSource objeto ou ao conteúdo do WPF. O HwndSource objeto 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 HwndSource objeto sairá do escopo assim que você retornar do procedimento de janela. A maneira habitual de lidar com esse problema com 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 HwndSource ao 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 WPFPageHost classe 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 GetHwnd função 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 Cancel . 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 OnButtonClicked evento. 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 do objeto IsOK é definida como true se o botão OK for clicado, e como false se o botão Cancelar for clicado.

Se o botão OK tiver sido clicado, o manipulador receberá 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 interface do usuário do aplicativo fornece um conjunto de botões de opção que permitem ao usuário modificar a cor da tela 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;

pt-BR: Para definir a cor de fundo, obtenha uma referência ao conteúdo do WPF (hostedPage) e defina a propriedade da cor 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 WPFPageHost classe. Para definir os outros dois, você cria um novo SolidColorBrush objeto e passa ao construtor um valor de cores estáticas do Colors objeto.

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.

Esquema

Os elementos de interface do usuário no conteúdo do WPF consistem em cinco TextBox controles, com controles associados Label : Nome, Endereço, Cidade, Estado e Zip. Há também dois Button controles, OK e Cancel

O conteúdo do WPF é implementado na WPFPage classe. O layout é manipulado com um elemento de layout Grid. A classe herda de , o que efetivamente a torna o elemento raiz de Gridconteúdo do WPF.

O construtor de conteúdo do WPF usa a largura e a altura necessárias e dimensiona de Grid acordo. Em seguida, ele define o layout básico criando um conjunto de objetos ColumnDefinition e RowDefinition e os adicionando às coleções base de Grid e ColumnDefinitions objetos, 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 de interface do usuário ao Grid. O primeiro elemento é o texto do título, que é um Label controle 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 da classe GridSetColumn e SetRow para colocar os controles na célula apropriada. Depois que o controle é criado, o exemplo chama o Add método na Children propriedade 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 Cancel e anexa um manipulador de eventos a seus Click eventos.

//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 um dos botões é clicado, o evento Click é disparado. A janela do host pode simplesmente anexar manipuladores a esses eventos e obter os dados diretamente dos TextBox controles. O exemplo usa uma abordagem um pouco menos direta. Ele manipula o conteúdo do Click WPF e, em seguida, gera um evento OnButtonClicked personalizado 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 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 Click manipulador de eventos, 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 DefaultFontFamily propriedade. A definição da propriedade chama um método privado que, por sua vez, configura as propriedades FontFamily dos vários controles.

A partir de 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