WinForms a C++ - resources

3.3.2017

Před časem jsem v tomto příspěvku ukázal, jak lze i v nových verzích Visual Studia vytvořit WinForms (exe) aplikaci v C++ i přesto, že generátor nového projektu tuto možnost již přímo nenabízí. Nyní něco o práci s resources, konkrétně jak použít vlastní ikony a bitmapy na okně/formuláři a v položkách nabídky nebo panelu nástrojů.

Samozřejmě je možné "vizuálně" přes properties formuláře (resp. položky menu nebo toolbaru) vybrat soubor s ikonou nebo bitmapou. Bohužel však na rozdíl od aplikace psané v C# nemáme v případě obrázků položek menu a toolbaru při výběru volbu "Project resource file". Dále v "properties" projektu není (alespoň pokud vím) možnost výběru ikony která bude "reprezentovat" exe soubor v průzkumníku nebo zástupci. A nakonec při vytvoření projektu není vytvořen soubor resx nebo jiný formát pro resource aplikace. V C# takový soubor je vytvořen a proto do něj můžeme přidat ikonu (i jako existující soubor) a tuto nastavit jako ikonu formuláře jednoduchým zápisem:

// Toto lze v C# zapsat napr. do udalosti Load
this.Icon = Properties.Resources.NazevIkony;

Jak tedy na to ve WinForms aplikaci psané v C++?

Resource manager pro načtení dat z resource

Nejprve si musíme do projektu přidat soubor který bude obsahovat resources. V nabídce projeku zvolíme "Add new Item -> Resource -> Assembly resource file (.resx)", který nazveme například Hlavni.resx. Když chceme "vytáhnout" nějaká data z tohoto souboru, musíme nejprve vytvořit instanci objektu ResourceManager:

System::Resources::ResourceManager^ rm =
  gcnew System::Resources::ResourceManager("WinFormsCpp.Hlavni",
  System::Reflection::Assembly::GetExecutingAssembly());

Prvním parametrem konstruktoru je název souboru (assembly resource file), a co je důležité - bez přípony a včetně namespace!

Nyní si můžeme načíst ikonu a nastavit ji jako ikonu okna (formuláře). To provedeme v obsluze události OnLoad s využitím výše vytvořeného objektu ResourceManager.

this->Icon = (safe_cast<System::Drawing::Icon^>(rm->GetObject("pivo")));

Parametrem metody GetObject je název příslušné ikony kterou jsme přidali do souboru .resx.

Ikona exe souboru a další standardní resources

Tato ikona se nyní zobrazí jako ikona okna, avšak když se podíváme v průzkumníku na vytvořený exe soubor, bude stále reprezentován výchozí ikonou pro aplikace, které nemají vlastní ikonu. Aby se v průzkumníku (nebo jiném souborovém manageru) zobrazila požadovaná ikona, musíme tuto ikonu přidat jako standardní resource nativní aplikace, tak jako v případě aplikace psané v čistém Win API (bez .NET), MFC nebo ATL/WTL apod. Ve Visual Studiu si zobrazíme okno "resource view", klikneme pravým tlačítkem a zvolíme "Add -> Resource -> Icon -> New". Zavřeme okno grafického editoru a otevřeme vytvořený souboru .rc pomocí "view code" a v následujícím řádku nastavíme cestu k souboru ikony, kterou jsme dříve přidali do souboru .resx, nebo samozřejmě můžeme použít libovolný jiný soubor s ikonou.

IDI_ICON1               ICON                    "res\\pivo.ico"

Nyní by se již tato ikona měla zobrazovat jako ikona .exe souboru v průzkumníku. Ještě pro upřesnění: pokud bychom výše uvedeným způsobem přidali do resource více ikon, zobrazena bude ta s nejnižším číslem (IDI_ICON1 je samozřejmě číslo definované v souboru resource.h). Pokud bychom tedy chtěli mít zobrazeno jinou později přidanou ikonu, museli bychom si "ručně pohrát" s čísly v souboru resource.h nebo změnit cesty v souboru .rc. Do nativních resource můžeme také přidat informace o verzi, které se pak zobrazí ve vlastnostech .exe souboru na kartě podrobnosti, včetně čísla verze a copyrightu.

Bitmapy položek menu a panelu nástrojů

Nakonec si ukážeme jak v kódu nastavit obrázky položkám menu a buttonům na panelu nástrojů. Nejprve přidáme "vizuálně" do výše zmíněného souboru resx bitmapu - nejlépe předem připravený soubor velikosti 16x16 pixelů a bitové hloubce 32bit, tj. ARGB, popř. 24bit (RGB). Pak už zbývá jen s použitím objektu ResourceManager načíst tyto bitmapy a nastavit položkám menu, plus nastavit správnou průhlednou barvu. V následující kódu představujícím obsluhu události Load máme vše pohromadě:

private: System::Void OnLoad(System::Object^  sender, System::EventArgs^  e)
{
  System::Resources::ResourceManager^ rm =
    gcnew System::Resources::ResourceManager("WinFormsCpp.Hlavni", System::Reflection::Assembly::GetExecutingAssembly());
  this->Icon = (safe_cast<System::Drawing::Icon^>(rm->GetObject("pivo")));
  this->menuItemKonec->ImageTransparentColor = System::Drawing::Color::Black;
  this->menuItemKonec->Image = (safe_cast<System::Drawing::Image^>(rm->GetObject("zavrit")));
  // Takto bychom mohli načíst bitmapu z resource
  // this->tsButtonKonec->Image = (safe_cast<System::Drawing::Image^>(rm->GetObject("zavrit")));
  // Pokud chceme mít stejný obrázek v menu a toolbaru, je lepší jednoduše takto: 
  this->tsButtonKonec->Image = this->menuItemKonec->Image;
  // V praxi bude na toolbaru více buttonů, proto je jednodušší použít tento cyklus
  // pro nastavení stejné průhledné barvy všem.
  for each (ToolStripItem^ var in toolStrip->Items)
  {
    var->ImageTransparentColor = System::Drawing::Color::Black;
  }
}