Image
26.6.2016 0 Comments

C++ pod Windows / Document/View a ladenie / 16. časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Opäť sa stretávame pri ďalšom, už šestnástom pokračovaní seriálu. Tentoraz si podrobnejšie preberieme interakciu dokument/pohľad a povieme si niečo o pokročilej ladiacej technike, tzv. dumpingu.

DOCUMENT/VIEW. Keď sme sa začínali zaoberať knižnicou MFC, uviedli sme si aj základné informácie o dokumentoch a pohľadoch. Už vieme, že dokument tvorí obsah toho, čo sa zobrazí v pohľade. Z toho vyplýva, že pohľad závisí od dokumentu (naopak to neplatí). Môže tiež existovať viacero pohľadov na jeden dokument [otázka na zamyslenie: Platí to aj naopak? Môže existovať viacero dokumentov, ktoré majú jeden pohľad? Odpoveď nájdete v helpe (zadajte v indexe document/view), ale skúste najprv dočítať túto časť do konca a odpovedať si sami]. V programoch MFC predstavuje dokument trieda odvodená od triedy MFC Cdocument (alebo jej potomkov) a pohľad predstavuje trieda odvodená od triedy Cview (alebo jej potomkov). Keď programujete aplikáciu MFC, často potrebujete vymeniť dáta medzi dokumentom a pohľadom alebo zmeniť pohľad, keď sa zmenili dáta v dokumente (čo je logické). Na tieto operácie nám slúžia viaceré funkcie, ktoré si teraz opíšeme.

CView::GetDocument – táto funkcia vracia ukazovateľ na dokument, a tým nám pomocou neho umožňuje pristupovať z pohľadu k členským funkciám alebo premenným dokumentu. Nevracia však ukazovateľ na CDocument, ale konkrétne na náš objekt dokumentu, ktorý je odvodený od Cdocument (majme napríklad triedu class CMojDokument : public  CDocument{…}, potom funkcia GetDocument vráti ukazovateľ na CMojDokument, podrobnejšie pozri funkciu GetDocument v zdrojovom súbore triedy pohľadu v príklade Študenti).

CDocument::UpdateAllViews – funkcia UpdateAllViews informuje pohľady (či všetky, to závisí od jej parametra, podrobne v helpe), že dáta v dokumente sa zmenili a bolo by dobré, keby aktualizovali pohľad na tieto dáta (napríklad ak pohľadom je graf, bude prekreslený podľa aktuálnych dát). Zo svojho tela volá funkciu CView::OnUpdate.

CView::OnUpdate – funkcia zmení pohľad podľa dát z dokumentu (ak zvolíme predchádzajúci príklad, realizovala by prekreslenie grafu). Môže, samozrejme, slúžiť aj na iné veci (napríklad zruší platnosť pohľadu, čím vyvolá funkciu OnDraw). OnUpdate je na rozdiel od predchádzajúcich dvoch funkcií virtuálnou funkciou, takže si ju môžeme prispôsobiť našim potrebám. 

Triedy CDocumentCView obsahujú aj funkcie, ktoré aplikačný systém volá ako prvé po nejakej akcii, ako je napríklad spustenie aplikácie, vytvorenie nového dokumentu alebo pohľadu. Tieto funkcie sú dobré napríklad na úvodné naplnenie objektu dokumentu nejakými prvotnými dátami.

CView::OnInitialUpdate – je virtuálna členská funkcia triedy CView a je volaná pri spustení aplikácie (presnejšie pri prvom pripojení pohľadu k dokumentu, ale ešte pred zobrazením pohľadu), alebo po vybratí File-New, File-Open z menu. Základnou úlohou tejto funkcie je volať funkciu CView::OnUpdate (čo, ak ju neprepíšeme, je jediné, čo robí). Pri jej prepisovaní musíme zabezpečiť volanie funkcie OnUpdate alebo aspoň základnej funkcie OnInitialUpdate (a tá zavolá OnUpdate).

CDocument::OnNewDocument – podobne ako pri predchádzajúcej funkcii je volaná, ak používateľ vyberie z menu File-New. Rovnako je volaná pri prvom vytváraní objektu dokumentu. Jej základná implementácia volá funkciu CDocument::DeleteContents, ktorá zmaže obsah dokumentu, ale nezničí ho. Pri aplikáciách SDI sa totiž namiesto vytvorenia nového dokumentu a zrušenia starého (napr. pri výbere File-New z menu) radšej reinicializuje starý dokument (vymaže sa jeho obsah a je označený ako čistý).

Celý proces spustenia a prevádzku štandardnej aplikácie MFC založenej na architektúre Document/View s jedným pohľadom môžete vidieť na obr. 1 a s viacerými pohľadmi na obr. 2.  Ak sa v obrázkoch nachádza formulácia vytvorenie/zrušenie objektu CDocument/Cview, vždy sa myslí objektu odvodeného od objektu CDocument/CView. Rovnako informácie v obrázkoch a vyššie v texte platia len pre aplikácie SDI!

Dlho som uvažoval a vymýšľal, aký ukážkový príklad použiť na ozrejmenie opísanej funkčnosti. Nakoniec som sa rozhodol použiť upravený, časom už akýsi „štandardizovaný“ príklad z knihy Inside Visual C++, ktorej autorom je David J. Kruglinski. Podobný príklad nájdete aj na MSDN Library k Visual C++ (tuším sa volá DAOENROL a je tutorialom k programovaniu databáz pomocou DAO) a aj v iných knihách a na internete. Tento príklad sa mi zdá najvhodnejší a na jeho základoch sa dá vysvetliť viacero programovacích techník vrátane serializácie, dumpingu, kolekcií a už spomenutých databáz, ku ktorým sa dostaneme v ďalších častiach. Príklad uvedený v tejto časti ukazuje, ako spolu komunikujú dokument s pohľadom a akým spôsobom si vymieňajú dáta. Používa dve triedy, s ktorými ste ešte priamo neprišli do kontaktu – CFormView CObject – a používa aj pokročilú ladiacu techniku (tzv. diagnostic dumping). Všetky tieto relatívne nové prvky si teraz opíšeme.

Obr. 1 Spúšťanie štandardnej aplikácie MFC (jeden pohľad)

Obr. 2 Spúšťanie štandardnej aplikácie MFC (viacero pohľadov)

Obr. 3  Konzolová aplikácia s podporou MFC

Trieda CFormView

TRIEDA CFORMVIEW. Je triedou pohľadu, ale skôr sa podobá na dialóg ako na pohľad. Táto trieda je dokonca aj priamo spojená s konkrétnym zdrojom dialógu, podporuje mechanizmy DDX a DDV a zobrazovanie rovnakých ovládacích prvkov, aké boli uvedené v častiach o dialógoch. Od dialógov odlišuje túto triedu viacero faktorov. V prvom rade nie je odvodená od spoločnej „materskej“ triedy všetkých dialógov CDialog, ale od triedy CScrollView, čo, ako vieme, je trieda pre pohľady. Ďalej má schopnosť spracúvať príkazové správy z hlavného menu, resp. z toolbaru, čo pri dialógoch nebolo priamo možné. Na druhej strane sa nám však pri používaní tejto triedy bude hodiť veľa vecí, čo sme sa naučili pri dialógoch. Okrem už, dúfam, rutinného používania ovládacích prvkov môžeme uplatniť vedomosti z prenosu dát medzi ovládacími prvkami a premennými v programe (funkcia UpdateData, DDX, DDV), aj keď tieto mechanizmy sa v triede CFormView implementujú mierne odlišne.

TRIEDA COBJECT. Ako ste mali možnosť vidieť v dvanástej časti, táto trieda tvorí základ takmer celej knižnice MFC. Priamo v sebe má zabudovanú podporu serializácie, dumpingu, kolekcií a run-time informácie o triede. Tým, že drvivá väčšina tried MFC je odvodená od tejto triedy, takisto získavajú túto funkčnosť.

Diagnostic dumping. Ako som už uviedol, ide o metódu ladenia aplikácie. Keďže ide o ladenie, je prístupná podobne ako makro TRACE len v ladiacom (Debug) režime. Môže sa používať podobne ako makro TRACE (kým syntax TRACE bola podobná funkcii printf z jazyka C, syntax dumpingu je zase podobná výpisu pomocou cout) na výpis nejakých ladiacich informácií, ale sféra jeho využitia je podstatne širšia. Používa sa napríklad na zisťovanie tzv. memory leaks, čo sú diery v pamäti, ktoré vznikajú, ak zabudneme zrušiť nejaký objekt (pomocou new vytvoríme, ale na delete zabudneme), pomocou neho môžeme zisiť adresu objektu a hodnoty jeho členských premenných.

Príklad 1 – dumping. Po dlhom čase si teraz vytvoríme aplikáciu bez AppWizardu, ktorá podporuje MFC. Z menu File vyberte New a na karte Projects vyberte typ projektu: Win32 Console Application. Do mena projektu vpíšte Dumpovanie a kliknite na OK. V prvom kroku, ktorý je zároveň posledným, vyberte možnosť An application that supports MFC (obr. 3) a kliknite na Finish. Odklepnite aj informácie o novom projekte.

Pozrite sa na kód tejto aplikácie, nemalo by vám robiť problém pochopiť ho. Projekt preložte a spustite. Z pohľadu používateľa je jediné, čo tento program teraz robí, vypísanie textu na obrazovku a v tomto smere mu ani my nepridáme nijakú funkčnosť. Do súboru dumpovanie.h pripíšte túto deklaráciu triedy:

class CDumpEx : public CObject
{
public:
      int m_nPom;
};

Vo funkcii _tmain v súbore dumpovanie.cpp vytvorte objekt typu CDumpEx príkazom:

CDumpEx dumpEx;

A vo vetve else podmienky pridajte nasledujúce riadky:

dumpEx.m_nPom=10;
 
afxDump << dumpEx;
afxDump << "Hodnota premennej m_nPom=" << dumpEx.m_nPom << "\n";

Teraz nastavte Breakpoint na riadok, kde premennej m_nPom priraďujete hodnotu a debuggujte program (F5). Keď dosiahnete Breakpoint, krokujte aplikáciu (Step Into, Step Over). Keď sa dostanete za riadok afxDump << „Hodnota premennej...“, skončite debuggovanie (SHIFT+F5) a pozrite sa na kartu Debug v okne Output. Okrem iných tam budú riadky:

a CObject at $12FF58

Hodnota premennej m_nPom=10

Pomocou preddefinovaného objektu kontextu dumpu (podrobne pozri help) sme vypísali potrebné informácie. Samozrejme, nemusíte pridávať breakpointy a krokovať, ak nechcete; stačí, ak spustíte aplikáciu pod Debuggerom (F5). Ale nie je na škodu občas si zopakovať aspoň tie klávesové skratky na ladenie .

MAKRÁ DECLARE_DYNAMICIMPLEMENT_DYNAMIC. Keď vo svojom programe odvodzujete nejakú triedu od Cobject (priamo či cez jej potomka), tieto makrá vám sprístupnia run-time informácie o odvádzanej triede. Skúste si v našom príklade Dumpovanie do súboru dumpovanie.h do triedy CDumpEx pridať makro:

DECLARE_DYNAMIC(CDumpEx)

A do súboru dumpovanie.cpp niekde pred funkciu _tmain makro:

IMPLEMENT_DYNAMIC(CDumpEx, CObject)

Zmenil sa nejako debug výstup v okne Output? Ďalšie informácie o dumpingu a týchto makrách nájdete v helpe.

Príklad 2 – Študenti. Teraz sa konečne môžeme pustiť do programovania sľúbeného príkladu na ozrejmenie spolupráce dokumentu s pohľadom. Vytvorte nový projekt s názvom StudentList typu AppWizard EXE. V prvom kroku zvoľte SDI aplikáciu a pokračujte až do kroku 6, pričom nechajte všetky nastavenia default. V 6. kroku zmeňte základnú triedu pohľadu z CView na CFormView. Kliknite na Finish a dokončite tvorenie aplikácie.

Zobrazí sa vám zdroj dialógu, na ktorý budete pridávať ovládacie prvky. Preložte a spustite aplikáciu v takejto forme a chvíľu ju preskúmajte, aby ste jasne videli rozdiel medzi triedami CFormViewCView. Rovnako sa pozrite na kód triedy CStudentListView. Teraz pridáme triedu, ktorá bude uchovávať informácie o študentovi (jeho meno a priezvisko, ročník a aktuálny počet kreditov za daný ročník). Z menu Project vyberte Add To Project a New... Na karte Files vyberte položku C/C++ Header File a do textového poľa File name vpíšte student.h (obr. 4).

Obr. 4 Pridanie hlavičkového súboru

Po jeho pridaní napíšte doň tento kód:

class CStudent : public CObject
{
      DECLARE_DYNAMIC(CStudent)
public:
      CString m_strMeno;
      CString m_strPriezvisko;
      int m_nRocnik;
      int m_nKredity;
 
      // prepísaná Dump funkcia pre naše potreby
      void Dump(CDumpContext& dc) const;
};

Zopakujte postup na pridanie nového súboru do projektu, ibaže teraz pridajte C++ Source File s názvom student.cpp. Vpíšte doňho nasledujúci kód:

#include "stdafx.h"
#include "student.h"
 
IMPLEMENT_DYNAMIC(CStudent, CObject)
 
void CStudent::Dump(CDumpContext& dc) const
{
      // zavoláme funkciu základnej triedy
      CObject::Dump(dc);
     
      dc <<"Meno študenta: " << m_strMeno << "\n";
      dc <<"Priezvisko študenta: " << m_strPriezvisko << "\n";
      dc <<"Ročník štúdia: " << m_nRocnik << "\n";
      dc <<"Počet kreditov: " << m_nKredity;
}

Teraz pridajte do pohľadu (presnejšie do zdroja dialógu IDD_STUDENTLIST_FORM) ovládacie prvky podľa obr. 5. ID a premenné jednotlivých ovládacích  prvkov (vrátane triedy, do ktorej ich pomocou ClassWizardu pridajte) máte v tabuľke 1.

ID ovládacieho prvku

Jeho členská premenná

Trieda

Umiestnenie

IDC_MENO

m_strMeno

CStudentListView

Vedľa popisu Meno

IDC_PRIEZVISKO

m_strPriezvisko

CStudentListView

Vedľa pop. Priezvisko

IDC_ROCNIK

m_nRocnik

CStudentListView

Vedľa pop. Ročník štú.

IDC_KREDITY

m_nKredity

CStudentListView

Vedľa pop. Počet kred.

IDC_WRITEDATA

-

CStudentListView

Tlačidlo s hlavičkou Zapíš údaje(default button)

IDC_GETDATA

-

CStudentListView

Tlačidlo s hlavičkou Získaj údaje

IDC_CLEARVIEW

 

CStudentListView

Tlačidlo s hlavičkou Vyčisti pohľad

Tab. 1 Ovládacie prvky pohľadu aplikácie StudentList

Do hlavičkového súboru triedy CStudentListDoc vložte túto include direktívu:

#include “student.h”
 
a jednu verejnú členskú premennú typu CStudent:
 
public:
      CStudent m_student;

Táto členská premenná nám umožňuje (ako už určite všetci tušíte) pristupovať z dokumentu k členským premenným triedy CStudent. Objekt triedy CStudent je vytvorený pri konštrukcii dokumentu a zrušený pri rušení dokumentu.

Pomocou ClassWizardu pridajte obsluhu správy BN_CLICKED tlačidlu IDC_WRITEDATA do triedy CStudentListView. Táto funkcia nám bude slúžiť na zapísanie dát z pohľadu (pomocou premenných pohľadu m_strMeno...) cez dokument až do členských premenných triedy CStudent. K dokumentu pristupujeme pomocou ukazovateľa naň, ktorý získame pomocou funkcie GetDocument. Do tela obslužnej funkcie kliknutia na tlačidlo IDC_WRITEDATA napíšte tento kód:

void CStudentListView::OnWritedata()
{
      // získame ukazovateľ na dokument
      CStudentListDoc* pDoc=GetDocument();
      // rovnako ako v dialógoch, volá DDX
      UpdateData(TRUE);
      // vpíšeme dáta do premenných triedy CStudent
      pDoc->m_student.m_strMeno=m_strMeno;
      pDoc->m_student.m_strPriezvisko=m_strPriezvisko;
      pDoc->m_student.m_nRocnik=m_nRocnik;
      pDoc->m_student.m_nKredity=m_nKredity;
}

Keď už máme funkciu, ktorá zapíše dáta z pohľadu do dokumentu, bolo by asi vhodné napísať funkciu, ktorá robí opačnú operáciu. Tlačidlu IDC_GETDATA priraďte do triedy pohľadu obsluhu správy BN_CLICKED a kód obslužnej funkcie zmeňte takto:

void CStudentListView::OnGetdata()
{
      CStudentListDoc* pDoc=GetDocument();
      m_strMeno=pDoc->m_student.m_strMeno;
      m_strPriezvisko=pDoc->m_student.m_strPriezvisko;
      m_nRocnik=pDoc->m_student.m_nRocnik;
      m_nKredity=pDoc->m_student.m_nKredity;
      UpdateData(FALSE);
}

Funkcia tlačidla IDC_CLEARVIEW je jasná. Po jej stlačení sa vymažú dáta z pohľadu (nie však z objektu CStudent!). Napísanie jej kódu vám nemôže robiť problém, preto ho ani neuvádzam (považujte to za drobnú domácu úlohu ).

Ako to testovať? Keď chcete odskúšať funkčnosť programu CStudentList, napíšte do textových polí nejaké informácie, potom zapíšte tieto údaje do premenných objektu CStudent (tlačidlo IDC_WRITEDATA), vymažte text z textových polí (tlačidlo IDC_CLEARVIEW) a tlačidlom IDC_GETDATA vyvolajte z objektu CStudent dáta späť do pohľadu. Je však aj iná metóda – využitím dumpingu (predsa sme neprepisovali funkciu Dump v triede CStudent zbytočne!). Pridajte nasledujúci riadok do deštruktora triedy dokumentu:

Dump(afxDump);

čo spôsobí zavolanie funkcie CStudentListDoc::Dump, ktorej kód zmeňte:

void CStudentListDoc::Dump(CDumpContext& dc) const
{
      CDocument::Dump(dc);
      dc << "\n" << m_student << "\n";
}

Teraz môžete funčnosť programu „odsledovať“ aj na výstupe v karte Debug. Vrelo vám odporúčam odkrokovať tento kód do podrobností, aby ste pochopili, čo a kedy sa volá.

FAQ

Q: Chcem z môjho programu lockovať počítač. Dá sa to?

A: Áno, samozrejme, ale len vo Windows 2000 (a vyššom). Sú dve možnosti, ako to urobiť. Buď použitím funkcie LockWorkStation, ktorú z programu zavoláte (bližšie informácie o potrebných knižniciach a hlavičkových súboroch v helpe), alebo využitím locknutia z príkazového riadka. V druhom prípade by  lockovanie v programe vyzeralo takto:

system("rundll32 user32.dll,LockWorkStation");

Tipy na doma. Zlepšite program StudentList tak, aby hneď po spustení, keď ešte nie sú prístupné žiadne dáta v triede CStudent ani v pohľade, zneprístupnil tlačidlo IDC_WRITEDATA (keďže nie je čo zapisovať) a sprístnupnite ho až po zadaní prvého znaku do textových polí. V helpe si pozrite použitie podmieneného prekladu dumpingových funkcií pomocou  #ifdef _DEBUG a #endif a pridajte ich do tohto programu. Využitím funkcie CView::OnInitialUpdate „naplňte“ pohľad základnými dátami už pri jeho vytvorení (napr. menu a priezvisku priraďte text: Default a pod.). 

Nabudúce. V ďalšej časti vyriešime „nedostatok“ aplikácie StudentList, ktorým je jej schopnosť spracúvať a uchovávať informácie len o jednom študentovi. Pridáme jej podporu spracovania viacerých študentov (využitím kolekcií) a možno prejdeme aj k problému, ako tieto dáta uložiť na disk (serializácii). Nabudúce si tiež povieme, čo nás ešte v seriáli čaká a približne kedy budeme končiť.

Zobrazit Galériu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá