Share via


Instruções passo a passo: hospedando conteúdo do WPF em Win32

O Windows Presentation Foundation (WPF) 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 a funcionalidade WPF ao seu aplicativo em vez de reescrever o código original. WPF fornece um mecanismo simples para hospedar conteúdo WPF em uma janela Win32.

Este tutorial descreve como escrever um aplicativo de exemplo, Hospedando conteúdo WPF em um exemplo de janela Win32, que hospeda conteúdo WPF em uma janela Win32. Você pode estender este exemplo para hospedar qualquer janela do Win32. Como envolve a mistura 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 WPF e Win32. Para obter uma introdução básica à programação do WPF, consulte Introdução. Para uma introdução à programação Win32, você deve fazer referência a qualquer um dos vários livros sobre o assunto, em particular Programming Windows por Charles Petzold.

Como o exemplo que acompanha este tutorial é implementado em C++/CLI, este tutorial pressupõe familiaridade com o uso de C++ para programar a API do Windows, além de uma compreensão da programação de código gerenciado. A familiaridade com C++/CLI é útil, mas não essencial.

Observação

Este tutorial inclui vários exemplos de código da amostra associada. No entanto, para facilitar a leitura, não inclui o código de exemplo completo. Para obter o código de exemplo completo, consulte Hospedando o conteúdo do WPF em uma amostra de janela do Win32.

O procedimento básico

Esta seção descreve o procedimento básico que você usa para hospedar conteúdo WPF em uma janela Win32. As outras seções explicam os detalhes de cada etapa.

A chave para hospedar conteúdo WPF em uma janela Win32 é a HwndSource classe. 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 filho. A abordagem a seguir combina o Win32 e o WPF em um único aplicativo.

  1. Implemente seu conteú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 para chamar código gerenciado alterando as configurações do projeto para incluir o sinalizador do /clr compilador.

  3. Defina o modelo de threading como STA (Single-Threaded Apartment).

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

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

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

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

    4. Obtenha o HWND do conteúdo. A Handle propriedade do objeto contém o identificador de HwndSource janela (HWND). Para obter um HWND que você possa usar na parte não gerenciada de seu aplicativo, transmita Handle.ToPointer() para um HWND.

  5. Implemente uma classe gerenciada que contém 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 WPF do seu 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 dos 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ê terá que compilá-lo separadamente como uma biblioteca de vínculo dinâmico (DLL) e fazer referência a essa DLL do seu aplicativo Win32. O restante do procedimento é semelhante ao descrito acima.

Implementando o aplicativo host

Esta seção descreve como hospedar conteú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 WPF simples. Os principais aspectos da implementação de conteúdo são abordados em Implementando o conteúdo do WPF.

O aplicativo básico

O ponto de partida para o aplicativo host foi 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 Outras linguagens.

  3. Selecione um modelo Projeto do Win32, atribua um nome ao projeto e clique em OK para iniciar o Assistente de Aplicativo do 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 do aplicativo.

  • Uma janela, com um procedimento de janela associado (WndProc).

  • Um menu com 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 um código gerenciado. Por padrão, o projeto é compilado como um código não gerenciado. No entanto, como o WPF é implementado em código gerenciado, o projeto deve ser compilado de acordo.

  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 no modo de exibição de árvore no painel esquerdo.

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

  4. Selecione Suporte a Common Language Runtime (/clr) na caixa de listagem suspensa.

Observação

Esse sinalizador de compilação permite que você use um código gerenciado no aplicativo, mas o código não gerenciado continuará sendo compilado como antes.

O WPF usa o modelo de threading STA (single-threaded apartment). Para funcionar corretamente com o código de conteú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 tomar nome de usuário, endereço e assim por diante. Há também dois Button controles, OK e Cancelar. Quando o usuário clica em OK, o manipulador de eventos do Click botão coleta os dados dos TextBox controles, atribui-os às propriedades correspondentes e gera um evento personalizado, OnButtonClicked. Quando o usuário clica em Cancelar, o manipulador apenas aciona OnButtonClicked. O objeto de argumento de evento para OnButtonClicked contém um campo booleano 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 GetHwnd método usa informações de tamanho e posição mais o identificador de janela pai e retorna o identificador de janela do conteúdo WPF hospedado.

Observação

Não é possível usar uma #using diretiva para o System::Windows::Interop namespace. Isso cria uma colisão de nome entre a estrutura nesse namespace e a MSG estrutura MSG declarada em winuser.h. Em vez disso, use 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, você primeiro cria um HwndSource objeto para encapsular o conteúdo do WPF. Esse objeto é basicamente uma janela projetada para hospedar um conteúdo WPF. Você hospeda o objeto na janela pai criando-o HwndSource como um filho de uma janela Win32 que faz parte do seu aplicativo. Os HwndSource parâmetros do construtor contêm praticamente as mesmas informações que você passaria para CreateWindow quando você cria uma janela filho Win32.

Em seguida, crie uma instância do objeto de conteú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 de eventos, , WPFButtonClickedao evento de conteúdo OnButtonClicked WPF. Esse manipulador é chamado quando o usuário clica no botão OK ou Cancelar. Consulte Conteúdo do communicating_with_the_WPF para obter mais informações sobre esse manipulador de eventos.

A linha final de código mostrada retorna o identificador de janela (HWND) que está associado ao HwndSource objeto. Você pode usar esse identificador do seu código Win32 para enviar mensagens para a janela hospedada, embora o exemplo não faça isso. O HwndSource objeto gera um evento toda vez que recebe uma mensagem. Para processar as mensagens, chame o método para anexar um manipulador de mensagens e, em seguida, processe AddHook 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, talvez você queira modificar as propriedades de conteúdo do WPF ou talvez fazer com que o HwndSource objeto hospede conteúdo WPF diferente. Para fazer isso, você precisa de uma referência ao objeto ou ao HwndSource conteúdo do WPF. O HwndSource objeto e seu conteúdo WPF associado permanecem na memória até que você destrua o identificador da janela. No entanto, a variável atribuída ao HwndSource objeto sairá do escopo assim que você retornar do procedimento de janela. A maneira usual de lidar com esse problema com aplicativos Win32 é usar uma variável estática ou global. Infelizmente, não é possível atribuir um objeto gerenciado a esses tipos de variáveis. Você pode atribuir o identificador de janela associado ao HwndSource objeto a uma variável global ou estática, mas esse doe não fornece acesso ao objeto em si.

A solução mais simples para esse problema é implementar uma classe gerenciada que contém um conjunto de campos estáticos para manter referências a todos os objetos gerenciados aos quais você precisa ter 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 estiver no escopo.

Comunicando com o conteúdo do WPF

Existem 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 ao usuário alterar várias propriedades de conteúdo do WPF, como a cor do plano de fundo ou o tamanho da fonte padrão.

Como mencionado acima, quando o usuário clica em qualquer um dos botões, 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 as exibe 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 WPF, MyPageEventArgs. A propriedade do IsOK objeto é definida como true se o botão OK foi clicado e false se o botão Cancelar foi clicado.

Se o botão OK foi clicado, o manipulador obtém uma referência ao conteúdo WPF da classe de contêiner. Em seguida, ele coleta as informações do usuário que são mantidas pelas propriedades de conteú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 recebido um clique, 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 de plano de fundo do conteúdo do WPF e várias propriedades relacionadas à fonte. O exemplo a seguir é um trecho do procedimento de janela (WndProc) do aplicativo e sua manipulação de mensagens que define várias propriedades em diferentes mensagens, incluindo a cor da tela de fundo. As outras são semelhantes e não são mostradas. Consulte a amostra completa para obter detalhes e o 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 do plano de fundo, obtenha uma referência ao conteúdo do WPF (hostedPage) e defina a propriedade de cor do plano de WPFPageHost fundo para a cor apropriada. A amostra usa três opções de cores: a cor original, verde-claro e salmão-claro. A cor de plano de fundo original é armazenada como um campo estático na WPFPageHost classe. Para definir os outros dois, crie um novo SolidColorBrush objeto e passe 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). A seguir está um breve passo a passo da implementação C++/CLI usada no exemplo. Esta seção contém as subseções a seguir.

Layout

Os elementos da interface do usuário no conteúdo do WPF consistem em cinco TextBox controles, com controles associados Label : Name, Address, City, State e Zip. Há também dois Button controles, OK e Cancelar

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

O construtor de conteú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 e e adicionando-os à base de ColumnDefinition objetos e RowDefinitionsRowDefinition coleçõesGrid, ColumnDefinitions 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 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 particulares e usado para todos os cinco pares de rótulo/caixa de texto. Os métodos criam o controle apropriado e chamam a Grid classe estática SetColumn e SetRow métodos para colocar os controles na célula apropriada. Depois que o controle é criado, o exemplo chama o método na Children propriedade do Grid para adicionar o Add 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 ocorre da seguinte forma:

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

Finalmente, o exemplo adiciona os botões OK e Cancelar e anexa um manipulador de eventos aos 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 à janela do host

Quando um dos botões é clicado, seu Click evento é gerado. A janela do host poderia 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 dentro do WPF e, em seguida, gera um evento OnButtonClickedpersonalizado , para notificar o Click 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 TextBox texto dos controles 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 WPF é um pouco mais complicada, porque não há uma única propriedade global que controla as fontes para todos os controles. Em vez disso, a propriedade apropriada de cada controle é alterada nos acessadores set das propriedades. O exemplo a seguir mostra o código da DefaultFontFamily propriedade. A definição da propriedade chama um método private que, por sua vez, define as FontFamily propriedades para os vários controles.

Em WPFPage.h:

property FontFamily^ DefaultFontFamily
{
  FontFamily^ get() {return _defaultFontFamily;}
  void set(FontFamily^ value) {SetFontFamily(value);}
};

Em 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;
}

Confira também