10.
Tele utan dokument
· Telefonlistan utan CDocument.
· Klassen ‘CTeleobjekt’.
· Klassen ‘COblist’.
· DeleteContents().
· Koppla uppdateringar.
· Serialisering av lista.
· SetWindowText().
Telefonlistan utan CDocument.
Om vi tänker oss att vi ville utnyttja möjligheten att skapa ett projekt av typen ‘Dialog based’ för Telefonlistan i förra kapitlet, vad skulle det då få för konsekvenser? Till att börja med får vi inte någon meny, och inte heller något dokument. En meny kan man ju lägga till, men vare sig vi lägger till en meny eller tryckknappar för funktionerna har vi samma situation: den automatik vi normalt får via valen i ‘File’-menyn saknas.
Liksom i förra projektet kommer det att krävas en hel del skrivarbete, och det är bara bra att kompilera och länka litet då och då, även om det vi lagt till inte kan testas för stunden. Det är som sagt lättare att hitta felen när man just skrivit koden, och inte har så mycket ny kod att leta i.
Så här kan vi göra dialogrutan:
Övningsuppgift:
· Skapa projektet Tele.
· Ange ‘Dialog based’ på AppWizards första sida.
· Designa dialogrutan enligt ovanstående.
· Använd vårt vanliga sätt att ange namn på variabler och identifikationer. Textrutorna har medlemsvariabler knutna till sig. Knapparna '<' och '>' ska heta IDC_PREV respektive IDC_NEXT.
· Id för knapparna i gruppen ‘Arkiv’ ska ‘kvalificeras’, t.ex. IDC_ARKIV_NYTT.
· Kompilera, länka och kontrollera att det ser ut som det ska. Det fungerar dock inte än, det fixar vi i de kommande avsnitten.
Klassen 'CTeleObjekt'.
Vi deklarerar klassen CTeleObjekt exakt på samma sätt som i föregående kapitel, inklusive serialiseringsmakrona. Glöm inte att teleobj.cpp ska inkluderas i projektet.
Vi har inget dokument, alltså kan vi inte använda oss av SetModifiedFlag(). Därför bör vi införa den hanteringen själva. För stunden lägger vi bara in flaggan m_Modified i dilogruteklassen (typ BOOL).
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Lägg till klassen CTeleObjekt.
· Lägg till en flagga m_Modified i dialogruteklassen.
· Lägg till en CString m_Titel som vi kan använda till att lagra dokumentnamn i så småningom.
Klassen 'CObList'.
Vi använder klassen COblist på samma sätt som tidigare. Vi initierar på samma sätt som tidigare, men i stället för att anropa SetModifiedFlag() från CTeleDlg::NewDocument() sätter vi bara flaggan m_Modified till FALSE. Vi har ju inget äkta dokument, så vi saknar funktionen OnNewDocument. Vi skapar i stället en egen funktion NewDocument() i dialogruteklassen:
void
CTeleDlg::NewDocument()
{
// Skapa
ett objekt av klassen CTeleObjekt till vilket vi
// kan
referera med pekaren pTeleObjekt:
CTeleObjekt* pTeleObjekt = new CTeleObjekt;
//
Initiera det nya objektets medlemsvariabler:
pTeleObjekt->m_Namn = "";
pTeleObjekt->m_Nummer = "";
// Lägg
till objektet i slutet av objektlistan:
m_TeleLista.AddHead(pTeleObjekt);
//
Initiera m_Position till att peka på den tomma posten:
m_Position = m_TeleLista.GetHeadPosition();
// Sätt markören i namnfältet:
((CDialog*)this)->GotoDlgCtrl(GetDlgItem(IDC_NAMN));
// Sätt
Modified-flaggan = FALSE:
m_Modified = FALSE;
}
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Lägg till m_TeleLista.
· Skapa funktionen NewDocument() och anropa den från InitDialog().
· Glöm inte ‘#include
"TeleObj.h"’.
DeleteContents().
I CDocument finns ju funktionen DeleteContents(). Den som vi bytte ut för att ha en plats där man tömmer minnet när ett nytt dokument ska skapas, eller ett dokument ska läsas in från disk. Tyvärr har vi ju inget dokument i vårt projekt. Vi skapar ju våra ‘dokument’ själva, och då får vi också själva skriva en funktion som kan tömma minnet, och den kan vi ju kalla DeleteContents() om vi vill. Vi kan skriva funktionsprototypen i dialogrutans klassdeklaration, och själva funktionen i dialogrutans implementeringsfil, eller använda ClassView som har en snabbmeny (höger mustangent) där menyval finns för bland annat ny medlemsfunktion.
Det vi skriver i DeleteContents() blir exakt likadant som i föregående kapitel.
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Lägg till DeleteContents().
· Lägg till uppdateringar för såväl m_Namn som m_Nummer.
Koppla uppdateringar.
Uppdateringarna skiljer sig från föregående exempel så till vida att vi inte behöver kopplingen till dokumentklassen. Listan ligger ju i dialogruteklassen:
void
CTeleDlg::OnChangeNamn()
{
// Hämta data från skärmen:
UpdateData(TRUE);
// m_TeleLista har ett tomt objekt som vi just nu redig-
// erar. Skapa en pekare mot det objektet:
CTeleObjekt* pTeleObjekt =
(CTeleObjekt*)m_TeleLista.GetAt(m_Position);
// Överför variabelns data till listans objekt:
pTeleObjekt->m_Namn = m_Namn;
}
Samma sak gäller naturligtvis fältet för nummer.
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Lägg till uppdateringar för såväl m_Namn som m_Nummer.
· Gör motsvarande ändringar för att skapa funktioner till knapparna ‘<’, ‘>’, ‘Ny’ och ‘Ta Bort’.
· Kompilera, länka och testa:
Knapparna i grupprutan ‘Arkiv’ fungerar naturligtvis inte ännu, men det ska vi titta på i nästa avsnitt.
Serialisering av lista.
Vi har ju redan deklarerat klassen CTeleObjekt exakt som i föregående uppgift, så den är redan förberedd för serialisering. Men vi har inte CDocuments automatiska anrop. Vi blir själva tvungna att öppna filvalsdialogrutan i de funktioner vi knyter till de tre knappar i Arkiv-gruppen som använder filer. Vi måste även känna av m_Modified när användaren klickar på ‘Ny’, så att denne får en chans att spara sitt data. Samma sak gäller ‘Öppna’ och vid programslut.
I CTeleObjekt finns den kod som serialiserar ett objekt. Vi behöver anropa den för alla objekt i listan. Detta gör vi precis som tidigare genom att helt enkelt anropa serialiseringsfunktionen i objektet m_TeleLista, men denna gång från våra egna ‘Arkiv’-knappsfunktioner.
Så här skulle vi till exempel kunna spara:
void
CTeleDlg::OnArkivSpara()
{
// Skapa ett CFileDialog-objekt som heter FileDialog.
// Slå upp CFileDialog i hjälpen och läs om argumenten!
CFileDialog FileDialog(FALSE,
"tel",
NULL,
OFN_OVERWRITEPROMPT|
OFN_HIDEREADONLY|
OFN_PATHMUSTEXIST,
"Tele Filer
(*.tel)|*.tel||");
// Översätt titelraden i dialogrutan (se hjälpen!):
FileDialog.m_ofn.lpstrTitle =
"Spara";
// Ge dialogrutan tillgång till filnamnet:
FileDialog.m_ofn.lpstrFile =
m_Title.GetBuffer(FILENAME_MAX);
// Öppna dialogrutan:
int iSvar = FileDialog.DoModal();
// Släpp Title:s buffert:
m_Title.ReleaseBuffer(-1);
// Testa om vi tryckt på OK:
if(iSvar == IDOK)
{
// Testa att skapa eller öppna filen:
CFile File;
if(File.Open(m_Title,
CFile::modeCreate|
CFile::modeWrite))
{
// Skapa ett arkivobjekt:
CArchive
Archive(&File, CArchive::store);
// Serialisera från listan:
m_TeleLista.Serialize(Archive);
// Stäng arkivet:
Archive.Close();
// Stäng filen:
File.Close();
// Sätt Modified-flaggan FALSE:
m_Modified = FALSE;
}
// Det gick inte att öppna filen:
else
{
AfxMessageBox("Det gick inte att spara!",
MB_ICONEXCLAMATION);
}
}
}
Man borde även lägga till en test som kontrollerar att vi inte oavsiktligt skriver över en fil.
Om vi öppnar ett arkiv med flaggan m_Modified = TRUE ska vi naturligtvis fråga användaren om han vill spara sitt aktuella dokument först.
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Lägg till funktioner för alla ‘Arkiv’-knappar.
· Observera att man måste leta upp första post när man har läst in ett dokument, annars har pekaren m_Position ett felaktigt värde.
· Testa så att allt fungerar som det ska.
SetWindowText().
Till sist fixar vi till titelraden. Redigera en text bestående av programnamnet ‘Tele’ och namnet på dokumentet. Använd funktionen SetWindowText() för att ändra titelraden när den ändrar värde. Tomt nytt dokument ska automatiskt få namnet ‘Namnlös’. Filnamnet har du i en CString som heter m_Titel.
Kompilera, länka och testa. Om du glömt något måste du repetera tidigare kapitel.
Så här kan det se ut om vi till exempel skapat en telefonlista för firmor:
Övningsuppgift:
· Öppna projektet Tele om det inte redan är öppet.
· Ändra titelraden enligt ovan.
· Testa så att allt fungerar som det ska.
· Vi kommer inte att använda Tele mer, så du kan ta bort det när du är klar.