Поделиться через


Пошаговое руководство. Размещение содержимого WPF в Win32

Windows Presentation Foundation (WPF) предоставляет широкие возможности для создания приложений. Однако при наличии существенных инвестиций в код Win32 может оказаться более эффективным, чтобы добавить функциональные возможности WPF в приложение, а не переписать исходный код. WPF предоставляет простой механизм размещения содержимого WPF в окне Win32.

В этом учебнике описывается создание примера приложения. Пример размещения содержимого WPF в окне Win32, которое размещает содержимое WPF в окне Win32. Вы можете расширить этот пример для размещения любого окна Win32. Поскольку этот пример включает смешанный управляемый и неуправляемый код, приложение создается на C++/CLI.

Требования

В этом учебнике предполагается, что вы знакомы с основами программирования в WPF и Win32. Базовые сведения о программировании WPF см. в разделе Начало работы. Базовые сведения о программировании в Win32 можно получить в любой из многочисленных книг по этой теме, например в руководстве Программирование в Windows Чарльза Петцольда (Charles Petzold).

Поскольку пример, сопровождающий этот учебник, реализован в C++/CLI, от вас потребуется знакомство с использованием C++ для программирования Windows API, а также понимание программирования управляемого кода. Знакомство с C++/CLI желательно, но не обязательно.

Примечание.

Этот учебник включает ряд примеров кода из связанного образца приложения. Однако для удобства чтения он не содержит полный пример кода. Полный пример кода см. в разделе Пример размещения содержимого WPF в окне Win32.

Основная процедура

В этом разделе рассматривается основная процедура, которая используется для размещения содержимого WPF в окне Win32. В остальных разделах подробно описывается каждый шаг.

Ключом к размещению содержимого WPF в окне Win32 является класс HwndSource. Этот класс упаковывает содержимое WPF в окно Win32, что позволяет включить его в пользовательский интерфейс в качестве дочернего окна. Следующий подход объединяет Win32 и WPF в одном приложении.

  1. Реализуйте содержимое WPF в виде управляемого класса.

  2. Реализуйте приложение Windows на C++/CLI. Если вы начинаете с существующего приложения и неуправляемого кода С++, то обычно можете позволить приложению вызывать управляемый код, изменив параметры проекта, чтобы включить флаг компилятора /clr.

  3. Установите в качестве потоковой модели однопотоковое подразделение (STA).

  4. Обработайте уведомление WM_CREATE в процедуре окна и выполните следующее.

    1. Создайте новый объект HwndSource с родительским окном в качестве его параметра parent.

    2. Создайте экземпляр вашего класса содержимого WPF.

    3. Назначьте ссылку на объект содержимого WPF свойству RootVisual объекта HwndSource.

    4. Получите HWND для содержимого. Свойство Handle объекта HwndSource содержит дескриптор окна (HWND). Чтобы получить HWND, который можно использовать в неуправляемой части приложения, приведите Handle.ToPointer() к HWND.

  5. Реализуйте управляемый класс, содержащий статическое поле для хранения ссылки на ваше содержимое WPF. Этот класс позволяет получить ссылку на содержимое WPF из вашего кода Win32.

  6. Назначьте содержимое WPF этому статическому полю.

  7. Получайте уведомления от содержимого WPF путем присоединения обработчика к одному или нескольким событиям WPF.

  8. Взаимодействуйте с содержимым WPF с помощью ссылки, которую вы сохранили в статическом поле, для задания свойств и т. д.

Примечание.

Также можно использовать содержимое WPF. Однако его необходимо скомпилировать отдельно в виде библиотеки динамической компоновки (DLL) и ссылаться на эту библиотеку DLL из приложения Win32. Оставшаяся часть процедуры аналогична описанной выше.

Реализация ведущего приложения

В этом разделе описывается, как разместить содержимое WPF в базовом приложении Win32. Само содержимое реализуется в C++/CLI как управляемый класс. Большей частью это просто программирование WPF. Ключевые аспекты реализации содержимого рассматриваются в разделе Реализация содержимого WPF.

Базовое приложение

Отправной точкой для ведущего приложения было создание шаблона Visual Studio 2005.

  1. Откройте Visual Studio 2005 и выберите пункт Создать проект в меню Файл.

  2. Выберите Win32 в списке типов проектов Visual C++. Если C++ не установлен в качестве языка по умолчанию, вы найдете эти типы проектов в разделе Другие языки.

  3. Выберите шаблон Проект Win32, задайте имя для проекта и нажмите кнопку ОК, чтобы запустить мастер приложений Win32.

  4. Примите параметры мастера по умолчанию и нажмите кнопку Готово, чтобы запустить проект.

Этот шаблон создает базовое приложение Win32, включая:

  • точку входа для приложения;

  • окно со связанной процедурой окна (WndProc);

  • меню с заголовками Файл и Справка. Меню Файл содержит пункт Выход, с помощью которого можно закрыть приложение. Меню Справка содержит пункт О программе, который запускает простое диалоговое окно.

Прежде чем приступить к созданию кода для размещения содержимого WPF, необходимо внести два изменения в этот базовый шаблон.

Сначала следует скомпилировать проект как управляемый код. По умолчанию проект компилируется как неуправляемый код. Однако поскольку WPF реализуется в управляемом коде, проект должен быть скомпилирован соответствующим образом.

  1. Щелкните правой кнопкой мыши имя проекта в обозревателе решений и выберите в контекстном меню пункт Свойства, чтобы запустить диалоговое окно Страницы свойств.

  2. Выберите пункт Свойства конфигурации в представлении дерева в левой области.

  3. Выберите пункт Поддержка общеязыковой среды выполнения (CLR) в списке Значения по умолчанию для проекта в правой области.

  4. Выберите в раскрывающемся списке пункт Поддержка CLR-среды (/clr).

Примечание.

Этот флаг компилятора позволяет использовать управляемый код в приложении, но ваш неуправляемый код будет продолжать компилироваться как раньше.

WPF использует потоковую модель однопотокового подразделения (STA). Чтобы правильно работать с содержимым WPF, необходимо задать STA в качестве потоковой модели путем применения атрибута к точке входа.

[System::STAThreadAttribute] //Needs to be an STA thread to play nicely with WPF
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{

Размещение содержимого WPF

Содержимое WPF является простым приложением ввода адреса. Оно состоит из нескольких элементов управления TextBox для получения имени пользователя, адреса и т. д. Существует также два элемента управления Button и кнопки ОК и Отмена. Когда пользователь нажимает кнопку ОК, обработчик событий Click этой кнопки собирает данные из элементов управления TextBox, назначает их соответствующим свойствам и вызывает пользовательское событие OnButtonClicked. Когда пользователь нажимает кнопку Отмена, обработчик просто вызывает событие OnButtonClicked. Объект аргумента события для OnButtonClicked содержит логическое поле, которое указывает, какая кнопка была нажата.

Код для размещения содержимого WPF реализуется в обработчике для уведомления WM_CREATE в главном окне.

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;

Метод GetHwnd принимает сведения о размере и позиции, а также дескрипторе родительского окна и возвращает дескриптор окна размещенного содержимого WPF.

Примечание.

Нельзя использовать директиву #using для пространства имен System::Windows::Interop. Иначе будет создан конфликт имен между структурой MSG в этом пространстве имен и структурой MSG, объявленной в winuser.h. Вместо этого необходимо использовать полные имена для доступа к содержимому этого пространства имен.

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

Нельзя разместить содержимое WPF непосредственно в окне приложения. Вместо этого сначала создайте объект HwndSource, который должен включать в себя содержимое WPF. Этот объект по сути является окном, предназначенным для размещения содержимого WPF. Вы размещаете объект HwndSource в родительском окне, создав его в качестве дочернего элемента окна Win32, которое является частью приложения. Параметры конструктора HwndSource содержат в основном те же сведения, которые будут передаваться в CreateWindow при создании дочернего окна Win32.

Далее необходимо создать экземпляр объекта содержимого WPF. В этом случае содержимое WPF реализуется как отдельный класс WPFPage, использующий C++/CLI. Вы также можете реализовать содержимое WPF с помощью XAML. Однако для этого необходимо настроить отдельный проект и создать содержимое WPF в виде библиотеки DLL. Вы можете добавить ссылку на эту DLL в свой проект и использовать эту ссылку для создания экземпляра содержимого WPF.

Чтобы отобразить содержимое WPF в вашем дочернем окне, назначьте ссылку на содержимое WPF свойству RootVisual объекта HwndSource.

Следующая строка кода присоединяет обработчик событий WPFButtonClicked к событию OnButtonClicked содержимого WPF. Этот обработчик вызывается, когда пользователь нажимает кнопку ОК или Отмена. Дальнейшее обсуждение этого обработчика событий см. в разделе Взаимодействие с содержимым WPF.

В последней строке кода показан возврат дескриптора окна (HWND), который связан с объектом HwndSource. Вы можете использовать этот дескриптор из своего кода Win32 для отправки сообщений в размещенное окно, хотя в этом примере так не делается. Объект HwndSource инициирует событие каждый раз при получении сообщения. Для обработки сообщений вызовите метод AddHook, чтобы присоединить обработчик сообщений, а затем обрабатывать сообщения в этом обработчике.

Хранение ссылки на содержимое WPF

Для многих приложений потребуется взаимодействовать с содержимым WPF позднее. Например, может возникнуть необходимость изменить свойства содержимого WPF или разместить в объекте HwndSource другое содержимое WPF. Для этого потребуется ссылка на объект HwndSource или содержимое WPF. Объект HwndSource и связанное с ним содержимое WPF остаются в памяти до уничтожения дескриптора окна. Однако переменная, которая назначается объекту HwndSource, выйдет из области действия сразу после возврата из процедуры окна. Обычный способ решения этой проблемы с приложениями Win32 заключается в использовании статической или глобальной переменной. К сожалению, нельзя назначать управляемый объект таким типам переменных. Вы можете назначить дескриптор окна, связанный с объектом HwndSource, глобальной или статической переменной, но которая не предоставляет доступ к самому объекту.

Самым простым решением этой проблемы является реализация управляемого класса, который содержит набор статических полей для хранения ссылок на любые управляемые объекты, к которым требуется доступ. В данном примере используется класс WPFPageHost для хранения ссылки на содержимое WPF, а также начальные значения его свойств, которые могут быть изменены пользователем позднее. Это определяется в заголовке.

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

В последней части GetHwnd функция присваивает значения этим полям для последующего использования, пока myPage остается в области.

Взаимодействие с содержимым WPF

Существует два типа взаимодействия с содержимым WPF. Приложение получает сведения из содержимого WPF, когда пользователь нажимает кнопки "ОК " или "Отмена ". Приложение также имеет пользовательский интерфейс, позволяющий пользователю изменять различные свойства содержимого WPF, например цвет фона или размер шрифта по умолчанию.

Как упоминалось выше, когда пользователь нажимает одну из кнопок, содержимое WPF вызывает событие OnButtonClicked. Приложение присоединяет обработчик к этому событию, чтобы получать эти уведомления. Если была нажата кнопка ОК, обработчик возвращает сведения о пользователе из содержимого WPF и отображает его в наборе статических элементов управления.

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: ");
    }
}

Обработчик получает объект аргумента пользовательского события из содержимого WPF — MyPageEventArgs. Для свойства IsOK объекта устанавливается значение true, если была нажата кнопка ОК, и значение false, если была нажата кнопка Отмена.

Если была нажата кнопка ОК, обработчик получает ссылку на содержимое WPF из класса контейнера. Затем он собирает сведения о пользователе, которые хранятся в соответствующих свойствах содержимого WPF, и использует статические элементы управления для отображения этих сведений в родительском окне. Поскольку данные содержимого WPF хранятся в виде управляемых строк, они должны быть маршалированы для использования элементом управления Win32. Если была нажата кнопка Отмена, обработчик удаляет данные из статических элементов управления.

Пользовательский интерфейс приложения предоставляет набор переключателей, с помощью которых пользователь может изменять цвет фона для содержимого WPF и некоторые свойства, связанные со шрифтами. Следующий пример представляет выдержку из процедуры окна приложения (WndProc), содержащую обработку его сообщения, которая устанавливает разные свойства в различных сообщениях, включая цвет фона. Остальные примеры похожи и не приводятся. Подробные сведения и контекст см. в полном примере.

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;

Чтобы задать цвет фона, получите ссылку на содержимое WPF (hostedPage) из WPFPageHost и установите в свойстве цвета фона соответствующий цвет. В примере используется три варианта цвета: исходный цвет, светло-зеленый и светло-оранжевый. Исходный цвет фона хранится в виде статического поля в классе WPFPageHost. Чтобы задать другие два цвета, создайте новый объект SolidColorBrush и передайте в конструктор значение статического цвета из объекта Colors.

Реализация страницы WPF

Вы можете размещать и использовать содержимое WPF без каких-либо знаний о фактической реализации. Если содержимое WPF было упаковано в отдельную DLL, его можно встроить в любом языке общеязыковой среды выполнения (CLR). Ниже приведено краткое пошаговое описание реализации C++/CLI, которая используется в данном примере. Этот подраздел состоит из следующих подразделов.

Макет

Элементы в содержимом WPF состоят из пяти элементов управления TextBox, связанных с элементами управления Label: имя, адрес, город, состояние и индекс. Существует также два элемента управления Button: ОК и Отмена

Содержимое WPF реализовано в классе WPFPage. Макет обрабатывается с помощью элемента макета Grid. Этот класс наследует от Grid, который фактически делает его корневым элементом содержимого WPF.

Конструктор содержимого WPF принимает требуемые ширину и высоту, а также устанавливает размеры Grid соответствующим образом. Затем он определяет базовый макет, создавая набор объектов ColumnDefinition и RowDefinition и добавляя их в соответствующие коллекции ColumnDefinitions и RowDefinitions базового объекта Grid. Это задает сетку из пяти строк и семи столбцов с размерами, определяемыми содержимым ячеек.

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

Затем конструктор добавляет элементы пользовательского интерфейса в объект Grid. Первый элемент — это текст заголовка, который является элементом управления Label, выравниваемым по центру в первой строке сетки.

//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);

Следующая строка содержит элемент управления Label «Имя» и связанный с ним элемент управления TextBox. Так как тот же код используется для каждой пары «метка/текстовое поле», он помещается в пару закрытых методов и используется для всех пяти пар «метка/текстовое поле». Эти методы создают соответствующий элемент управления и вызывают класс Grid статического метода SetColumn и методы SetRow для размещения элементов управления в соответствующую ячейку. После создания элемента управления в примере вызывается метод Add в свойстве Children объекта Grid для добавления элемента управления в сетку. Для добавления оставшихся пар «метка/текстовое поле» используется аналогичный код. Подробности см. в примере кода.

//Add the Name Label and TextBox
nameLabel = CreateLabel(0, 1, "Name");
this->Children->Add(nameLabel);
nameTextBox = CreateTextBox(1, 1, 3);
this->Children->Add(nameTextBox);

Реализация этих двух методов выглядит следующим образом:

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

Наконец, в этом примере добавляются кнопки ОК и Отмена, а затем присоединяется обработчик событий к их событиям 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);

Возврат данных в главное окно

При нажатии одной из кнопок вызывается ее событие Click. Главное окно может просто присоединять обработчики к этим событиям и получать данные напрямую из элементов управления TextBox. В примере используется несколько менее прямой подход. В этом примере выполняется обработка Click в содержимом WPF, а затем вызывается пользовательское событие OnButtonClicked для уведомления содержимого WPF. Это позволяет содержимому WPF выполнить некоторую проверку параметров перед уведомлением узла. Обработчик получает текст из элементов управления TextBox и назначает его общим свойствам, из которых узел может получать сведения.

Объявление события в WPFPage.h:

public:
  delegate void ButtonClickHandler(Object ^, MyPageEventArgs ^);
  WPFPage();
  WPFPage(int height, int width);
  event ButtonClickHandler ^OnButtonClicked;

Обработчик событий Click в 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));
}

Установка свойств WPF

Узел Win32 предоставляет пользователю возможность изменять разные свойства содержимого WPF. Со стороны Win32 это просто изменение свойств. Реализация в классе содержимого WPF несколько более сложная, поскольку нет одного глобального свойства, которое управляет шрифтами для всех элементов управления. Вместо этого для каждого элемента управления изменяется соответствующее свойство в методах доступа set этого свойства. В следующем примере приведен код для свойства DefaultFontFamily. Задание свойства вызывает закрытый метод, который в свою очередь устанавливает свойства FontFamily для различных элементов управления.

Из WPFPage.h:

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

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

См. также