|
Otázky a odpovědi |
|||||
Programátorská poradna |
||||||
Časová náročnost (min): |
Začátečník |
Pokročilý |
Profesionál |
|||
|
|
|
||||
|
Použitý operační systém : Hlavní vývojový
nástroj : Další vývojový software : Jiný software : |
Windows 2000 SP3 Visual C# .NET 2002 Žádný Žádný |
||||
|
|
Chtěl bych z aplikace napsané v C# zavolat API funkci
Windows, ovšem nemůžu najít způsob, jak to provést. Mohl byste mi poradit? |
Budete-li se pokoušet volat z řízené
aplikace neřízenou funkci aplikačního programovacího rozhraní (API) Windows,
budete muset splnit před samotným použitím vámi požadované funkce několik
podmínek: 1.
Deklaraci API funkce musí předcházet použití
speciálního atributu DllImportAttribute, který se nachází ve jmenném
prostoru System.Runtime.InteropServices. Atribut DllImportAttribute
naznačuje, že bude importována API funkce s neřízeným kódem. Ve
skutečnosti je atribut pouze jiným označením pro speciální třídu se stejným
názvem (DllImportAttribute). Konstruktor této třídy pracuje
s jedním parametrem typu string, kterým je název DLL knihovny, ve
které je uložen kód API funkce, kterou chcete použít. 2.
Deklarační příkaz API funkce musí obsahovat
klíčová slova static a extern. 3.
Deklarace API funkce v řízeném kódu musí
přesně odpovídat prototypu funkce v neřízeném kódu. To znamená, že
deklarace musí obsahovat stejné jméno, dále stejnou signaturu
s příslušnými datovými typy formálních parametrů funkce a také stejnou
návratovou hodnotu (pokud existuje). Jestliže budou splněny tyto podmínky, bude
API funkce úspěšně naimportována do kódu vaší řízené aplikace. S API
funkcí můžete posléze pracovat jako s jakoukoliv jinou funkcí. Podívejme
se na ukázku použití API funkce operačního systému Windows: // ...
Tento programový kód se nachází uvnitř třídy Form1. static
void Main() { Application.Run(new Form1()); } [System.Runtime.InteropServices.DllImport("User32")] private
static extern
bool MoveWindow(int
hWnd, int
X, int Y, int
nWidth, int nHeight, bool
bRepaint); [System.Runtime.InteropServices.DllImport("User32")] private
static extern
bool AnimateWindow(int
hWnd, uint
dwTime, uint dwFlags); private
void button1_Click(object
sender, System.EventArgs e) { frm = new Form(); frm.Load += new EventHandler(frm_Load); frm.Show(); } Form frm; private
void frm_Load(object
sender, System.EventArgs e) { int
handle = frm.Handle.ToInt32(); MoveWindow(handle, 362,
234, 300, 300, true); AnimateWindow(handle, 200,
0x00080000); } // ...
zde pokračuje kód třídy Form1. Ukázkový programový kód používá pro přesun
a animaci nově vytvořené instance třídy Form dvě API funkce: MoveWindow
a AnimateWindow. Před každou z funkcí se nachází atribut DllImport
se jménem dynamicky linkované knihovny (DLL), ve které je uložen kód dané funkce
(obě zde použité funkce jsou uloženy v souboru User32.dll). Velmi
důležité je, aby byl atribut zapsán v hranatých závorkách. Za atributem
se nachází samotný deklarační příkaz, který je složen z modifikátoru
přístupu (private), klíčových slov static a extern, typem
návratové hodnoty funkce a její signaturou (datové typy přitom musí odpovídat
datovým typům použitím v neřízeném kódu API funkce). Ve výše uvedené
programové ukázce je dále deklarována referenční proměnná frm
s oborem třídy, které může uchovávat referenci na instance třídy Form.
V událostní proceduře button1_Click
je vytvořena nová instance třídy Form,
přičemž dochází také k vytvoření systémového delegáta (EventHandler) pro zpracovatele události
Load nově vytvořené instance.
Jakmile bude aktivována metoda Show objektu frm, delegát
usměrní program tak, aby byl uskutečněn programový kód, jenž se nachází ve
zpracovateli události frm_Load. Když dojde k události Load
objektu frm, bude vykonán kód zpracovatele frm_Load (k události
dojde po zavolání metody Show příslušné instance). Zpracovatel
události frm_Load obsahuje následující řádky programového kódu: private void frm_Load(object sender, System.EventArgs e) { int
handle = frm.Handle.ToInt32(); MoveWindow(handle, 362, 234, 300,
300, true); AnimateWindow(handle, 200,
0x00080000); } Co proboha
tento kód provádí? Tak především, je získán handle okna instance třídy Form.
Handle představuje jednoznačný identifikátor, pomocí kterého Windows
přistupují k vytvořené instanci (hodnota identifikátoru handle je v tomto
případě převedena do podoby 32bitového celého čísla pomocí metody ToInt32).
Handle okna potřebujeme získat, neboť jej využijeme v obou API funkcích.
Abyste přesně věděli, co uvedené API funkce provádějí, představíme si je
podrobněji: 1.
API funkce
MoveWindow má tuto všeobecní podobu (v C/C++): BOOL MoveWindow( HWND hWnd, // handle okna int X, // horizontální
pozice okna
int Y, // vertikální
pozice okna int nWidth, //
šířka okna
int nHeight, // výška
okna BOOL bRepaint // volba, která
určuje, zdali bude po přemístění realizováno // také
překreslení okna ); Podoba naší funkce je následovní: MoveWindow(handle, 362, 234, 300, 300, true); Funkce zachovává implicitní velikost okna instance třídy Form
(300x300 pixelů), ovšem mění pozici levého horního bodu okna na bod se
souřadnicemi (362, 234). Poslední parametr (bRepaint) je nastaven na
hodnotu true, což znamená, že ihned poté, co se okno instance přesune,
bude provedeno jeho překreslení. 2.
API funkce
AnimateWindow má tuto všeobecní
podobu (opět v C/C++): BOOL AnimateWindow( HWND
hwnd, // handle
okna DWORD
dwTime, // doba
trvání animace okna DWORD
dwFlags // typ
animace ); A opět se podívejme na naší implementaci funkce AnimateWindow: AnimateWindow(handle, 200, 0x00080000); Prvním parametrem funkce je handle okna, které bude animováno. Druhý parametr (dwTime) je datového typu DWORD (typ DWORD představuje 32bitové celé číslo bez znaménka, jehož maximální hodnota je 232-1). V C# se typ DWORD samozřejmě nepoužívá, ovšem jeho ekvivalentem je datový typ uint, jenž disponuje stejným rozsahem platných celočíselných hodnot. Parametr dwTime odpovídá době trvání animace, která se udává v milisekundách. V našem případě bude animace trvat 200 milisekund, což je doporučená hodnota daná způsobem práce operačního systému. Nejhrozivěji pravděpodobně vypadá třetí parametr (dwFlags), jehož hodnotou je podivně vyhlížející číslo v šestnáctkové soustavě 0x0008000. Numerická hodnota 0x0008000 odpovídá interní konstantě AW_BLEND, která se používá v případě, kdy má být okno animováno pomocí tzv. fade efektu. Fade efekt znamená plynulé animování zobrazení okna, přičemž na začátku je okno takřka průhledné a postupně se „zaplňuje“ do standardního barevného schématu a podoby.
|
|
Doposud jsem programoval ve Visual Basicu .NET. Myslíte, že bych se
mohl naučit programovat také ve Visual C#? |
Zcela jistě ano. Visual C# je velmi
zajímavý jazyk, který vhodně kombinuje rychlý návrh aplikací, jenž je známý
právě z Visual Basicu a flexibilní syntaxi, která je předností jazyka
C++. Protože všechny jazyky platformy .NET využívají společné integrované prostředí,
na první pohled možná ani nepostřehnete, že se nacházíte uvnitř Visual C#. Visual
C# taktéž nabízí vizuální vývoj aplikací, takže grafickou podobu aplikace
můžete navrhnout přesně tak, jak to děláte ve Visual Basicu .NET. Na druhé
straně je pravdou, že zápis programového kódu se od Visual Basicu podstatně
liší a zde vám zcela určitě pomůže předcházející zkušenost s jazykem C
nebo C++ (případně s Javou). Pokud neznáte uvedené jazyky, nemusíte
zoufat. Seriál Začínáme s jazykem
Visual C# vám poskytne všechny potřebné informace nato, abyste mohli psát
své první programy i ve Visual C#. Ve skutečnosti jsou jazyky Visual C# a
Visual Basic .NET do velké míry podobné ve smyslu implementace stejných, nebo
přinejmenším velmi podobných programovacích prvků. Ku příkladu, oba jazyky umožňují
řídit tok programu pomocí rozhodovací konstrukce If (if v C#), ovšem zápis této
konstrukce je v C# jiný než ve Visual Basicu (podobně je tomu také třeba při
cyklech či prvcích OOP). Sečteno a podtrženo, jestliže dobře ovládáte Visual
Basic .NET, při pilném studiu se do tajů Visual C# můžete dostat velmi
rychle. Poněkud problematičtější je ovšem přechod k Visual C# pro
programátory, kteří pracují s Visual Basicem verze 6. Ty totiž budou
muset nejdříve zvládnout všechny inovace a modifikace, jež přináší vývojová
platforma .NET. |
|
Pracuji s formulářem, který bych potřeboval vertikálně i
horizontálně vycentrovat na obrazovce počítače. Vím, že v režimu návrhu
mohu nastavit vlastnost StartPosition na hodnotu CenterScreen,
co mám však udělat, když chci provést vystředění formuláře za běhu programu? |
Událostní proceduru Load formuláře upravte do níže uvedené podoby: private
void Form1_Load(object
sender, System.EventArgs e) { this.Top
= (Screen.PrimaryScreen.Bounds.Height - this.Height)
/ 2; this.Left
= (Screen.PrimaryScreen.Bounds.Width - this.Width)
/ 2; } Abyste mohli provést vystředění formuláře,
musíte znát rozměry zobrazovací plochy obrazovky a rozměry samotného
formuláře. Rozměry formuláře jsou známé, nakolik Visual C# implicitně vytváří
formuláře s velikostí 300x300 obrazových bodů neboli pixelů. Při zrození
instance formuláře jsou do vlastností Height a Width této
instance uloženy hodnoty 300 pixelů. Pro zjištění rozměrů viditelné plochy
obrazovky použijeme třídu Screen. Konstruktor třídy Screen ovšem
není veřejný a proto není možné přímo vytvářet instance této třídy. Pokud
ovšem zavoláme statickou vlastnost PrimaryScreen, podaří se nám získat
instanci třídy Screen. Vrácená
instance představuje hlavní zobrazovací jednotku počítačového systému (pokud
systém pracuje jenom s jednou obrazovkou, bude vrácena tato,
v opačném případě bude vrácena ta zobrazovací jednotka, která je
nakonfigurována jako primární). Dále pokračujeme zavoláním vlastnosti Bounds
vytvořené instance třídy Screen. Vlastnost Bounds vrací
instanci třídy Rectangle (jde o objekt, jenž popisuje obdélníkový
region hlavní zobrazovací jednotky). Máme-li instanci třídy Rectangle,
můžeme konečně použít vlastnost Height (resp. Width) pro
zjištění výšky (resp. šířky) viditelné plochy obrazovky. Předpokládejme, že
vlastníte 17-palcový monitor a používáte rozlišení 1024x768 obrazových bodů.
Za těchto podmínek vrátí příkaz Screen.PrimaryScreen.Bounds.Height hodnotu
768 a příkaz Screen.PrimaryScreen.Bounds.Width zase vrátí hodnotu 1024. Od
takto získaných hodnot posléze odečteme hodnoty, které reprezentují výšku
(resp. šířku) formuláře a finální výsledky vydělíme dvěma. Do vlastnosti Top
formuláře bude po provedení všech nastíněných operací uložena hodnota 234,
zatímco vlastnost Left bude obsahovat hodnotu 362 (viz obrázek). |
|
Chtěl bych se zeptat, jak je možné ve Visual C# vytvářet zpracovatele
události, a to jak v režimu návrhu, tak i za běhu aplikace. |
V režimu návrhu aplikace vytvoříte
zpracovatele události takto: 1.
Na formuláři vyberte ten ovládací prvek, pro
který chcete vytvořit zpracovatele události. 2.
V okně Properties Window klepněte na
tlačítko Events ( 3.
Vyhledejte položku s názvem požadované
události (např. Click) a poklepejte na tuto položku. 4.
Visual C# okamžitě vygeneruje programovou kostru
událostní procedury a otevře okno editoru pro zápis programového kódu. 5.
Vyplňte tělo vytvořené událostní procedury
vhodným programovým kódem. Jestliže budete chtít spojit událost
s jejím zpracovatelem za běhu programu, budete muset všechny potřebné
operace provést přímo z programového kódu. Aby bylo možné vytvořit
spojení mezi událostí a příslušným zpracovatelem, musíte vytvořit systémového
delegáta, který bude zodpovědný za to, aby událost našla svého zpracovatele. Následující fragment kódu byl vyňat ze
třídy Form1. Můžete si v něm všimnout událostní proceduru button1_Click.
V této proceduře dochází k vytvoření nové instance třídy Button (s názvem btnTlačítko1), dále
jsou nastaveny některé klíčové vlastnosti nově vytvořené instance a instance
je přidána do kolekce ovládacích prvků formuláře. Spojení mezi události Click tlačítka
btnTlačítko1 a zpracovatelem této události (btnTlačítko1_Click)
zabezpečuje systémový delegát EventHandler. Při této programové
operaci je velmi důležité správné použití operátorů += a new. Nakonec už stačí jenom napsat událostní
proceduru btnTlačítko1_Click, která ovšem musí disponovat stejnými
prvky, jako systémový delegát (nesmí vracet hodnotu a musí pracovat se dvěma
parametry typu object a System.EventArgs). Pokud máte
zkušenosti s jazykem C nebo C++, můžete si delegáta představit jako
funkční ukazatel, který je ale v jazyce Visual C# zcela typově bezpečný.
Delegát tak ve skutečnosti obsahuje runtime adresu funkce, v našem
případě zpracovatele události, a tohoto zpracovatele aktivuje vždy, když je o
to požádán (neboli vždy, když uživatel klepne na tlačítko). private void button1_Click(object
sender, System.EventArgs
e) { Button btnTlačítko1 = new Button(); btnTlačítko1.Text = "Tlačítko"; btnTlačítko1.Location = new Point(10, 100); btnTlačítko1.Size = new Size(200, 100); this.Controls.Add(btnTlačítko1); btnTlačítko1.Click += new System.EventHandler(btnTlačítko1_Click); } private
void btnTlačítko1_Click(object sender, EventArgs
e) { MessageBox.Show("Právě jste klepli na tlačítko."); } Graficky můžeme vztah mezi událostí, jejím
zpracovatelem a systémovým delegátem znázornit takto: |
Ján Hanák