[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 773
  • Last Modified:

SDI - Updating Student Record

I have created a dialog based MFC program, which will read in a student record (name, num of grades,type of test, letter grade and number grade) and then edit each of the data members (by calling a custom dialog through a button). The way I handled the "reading from file" and writing to file" from the Student class is by defining:
istream & operator >> (istream& is,CStudent& s) to input data and
stream & operator<<(ostream& os, const CStudent& s) to output data.
I would then open a file from the Dialog application this way:
ifstream ifl;
ifl.open("student1.txt");
Then I would call ifl >> s from the Project1Dlg.cpp to load data into the object "s" (which is of type CStudent), then would retrieve each of the data members and format them to be displayed onto the text boxes and list boxes. In the same way, I would call ifl<<s  from Project1Dlg.cpp to restore the file with the modifed data members.

The student record in the file student1.txt looks like this(the variables are listed along with it):
-----------------------------------------------------------------------------------------------------------
Joe Green      // mFirstname    mLastname
14                 // mNumofGrade
Test1 A 100   // mTestaname   mLetGrade   mNumGrade
Test2 A 99
Test3 A 98
Test4 A 97

Question:
Now, I would like to use the student record program but with an SDI application (doc view architecture).
I would like to make files of type .rec to be only displayed when selecting "open" (which I know how to do by changing the properties in the String Table).
The text of the file has to be displayed in the View window (raw format), and I would like to call a custom dialog from the menu choice to edit all the data in the single record and update them in memory.
I want to eliminate the Save/Exit buttons that I used in the dialog box, and want to save using the "Save" and "Save as" menu choices already available in the SDI app.
I've played around well with dialog boxes but am a little confused about how to handle SDI (doc/view and synchronization) ?? Should I used "CEditView" as my base class here? If so, how would I read the data from the CEditView and then store it into variables, which would need to be updated when I modify all the data in the record with the Custom dialog box?? How could I write back to the CEditView from the Control variables in the Custom dialog box??

Any sample code would be really useful. thanks for your help!

-shan
0
Testsubbu
Asked:
Testsubbu
  • 8
  • 7
2 Solutions
 
Jaime OlivaresCommented:
Hi shan
If your are working with MFC Doc/View framework then I suggest you not to use ifstream but CArchive.
CDocument derived classes are ready made to support serialization (load/save operations) by implementing the Serialize() function member. If you have created your application with AppWizard then it is there right now.

For a simple, first example of serialize have a look to:
http://www.codeproject.com/docview/asciiserialization.asp

To make your app use .rec files (assuming you have created your app with AppWizard, search in your resource a resource string called IDR_MAINFRAME, and read this article:
http://support.microsoft.com/default.aspx?scid=kb;en-us;129095

Good luck,
Jaime
0
 
TestsubbuAuthor Commented:
Thanks for the input.
But in my case, should I use CEditView , CView or CFormView as the base class for the View Class ??
If I use CView, will the seriliaze() function(with customcode to readdata and writedata) automatically read and write the data?? Wouldn't CEditView be a better choice ??
0
 
Jaime OlivaresCommented:
Wouldn't CEditView be a better choice ??
CEditView will not provide you better serializing, since it is made in CDocument class, but for displaying purposes is a better choice for you.
To use CEditView, you have to create a new app with AppWizard, and ensure to replace CView with CEditView before finishing the Wizard.

At CDocument, your serialize function must read all file contents into a CString member, then you will call UpdateAllView, this will generate an OnUpdate() event in every view (just 1 in MDI), so you have to implement CEditView::OnUpdate() to read the doc's string and store into view with something like:
      GetEditCtrl()->SetText(yourdoc->thestring);
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
TestsubbuAuthor Commented:
I tried reading the file contents into a CString member in the CDocument: Serialize function, and looks like it's not working right, and also I am getting an error when I try to save the opened file: (that did not happen before I added the piece of code at the bottom)
Here's the code:
void CApuDoc::Serialize(CArchive& ar)
{
      // CEditView contains an edit control which handles all serialization
      reinterpret_cast<CEditView*>(m_viewList.GetHead())->SerializeRaw(ar);
      CString theString;
      while(ar.ReadString(theString))
      {
                   AfxMessageBox(theString); //Display in Messagebox
      }      
}
0
 
Jaime OlivaresCommented:
reinterpret_cast<CEditView*>(m_viewList.GetHead())->SerializeRaw(ar);
this is not a good practice (and doesn't work)

You need something like:

void CApuDoc::Serialize(CArchive& ar)
{
  if (ar.IsLoading()) {
     CString  line;
     while(ar.ReadString(theString))
     {
                   AfxMessageBox(theString); //Display in Messagebox
                   m_Contents += line;   // m_Contents is a doc's member
     }    
     UpdateAllViews(NULL);
   }
}
This will produce a OnUpdate event in your CView, you have to implement it and read m_Contents from view object, as I have described you.
0
 
TestsubbuAuthor Commented:
Hi Jaime,

Thankx for the help..I think am almost there...I am now able to read and display the file in the Edit view :
void CApuDoc::Serialize(CArchive& ar)
{
    CString strTemp;
   if (ar.IsLoading())
   {
     CString line;
     while(ar.ReadString(strTemp))
     {
                m_Contents += strTemp + "\r\n";   // m_Contents is a doc's member
      AfxMessageBox(m_Contents); //Display in Messagebox
     }    
    UpdateAllViews(NULL);
   }
}

void CApuView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
      CEdit& Edit =  CEditView::GetEditCtrl();
      Edit.SetReadOnly();
               Edit.SetWindowText(GetDocument()->m_Contents);      
}

Now, this works great but I still have a problem! The doc member variable m_Contents looks like this after I have read it
from the file:
Joe Green      
6                
Test2 A 99
Test3 A 98
Test4 A 97
Test5 B 80
Test6 C 60
Test7 A 100

I would like to extract from the CString (Joe as firstname, Green as lastname, 6 into the integer NumofGrades, all Test2,Test3 into an CString* array , LetterGrades into a char* array and NoGrades into an int* array). How can that be done & where could it be done?
This is a piece of code I tried, but doesn't work.

 CStringArray Array;
 int nNext,nPos;
 if(m_Contents.Right(1)!=" ") //to make sure we get the last variable
 m_Contents+=" ";
 while((nNext=m_Contents.Find(" ", nPos+1))>-1)
 {
     Array.Add(m_Contents.Mid(nPos, nNext-nPos));
      nPos=nNext+1;
 }
}
0
 
TestsubbuAuthor Commented:
Hi Jaime,

I just figured out that I do not need to parse the CString and get the individual Strings from it.
I could use the algorithm I used before with ifstream and ofstream to directly read the file once it's open and then use my already written CStudent functions to read the variables.

CString DocName = GetDocument()->GetPathName();
AfxMessageBox(DocName);
ifstream ifl;
ifl.open(DocName);
ifl>>s;  // where >> is defined in the CStudent class

I just dont' know where to stick this code, should I put it on the" Menu Item Event which calls the Custom Dialog box" (which is going to update each of these variables using editcontrol boxes??)
0
 
Jaime OlivaresCommented:
>I would like to extract from the CString (Joe as firstname, Green as lastname, 6 into the integer NumofGrades, all
> Test2,Test3 into an CString* array , LetterGrades into a char* array and NoGrades into an int* array).

I suggest you to define some variables instead of simple m_Contents, by example:
CString m_FirstName, m_LastName;      // to store the name;
int m_NumOfGrades;  // to store array size

Define a structure like:
struct TGrade {
    CString TestName;
    char LetterGrade;
    int    NumericGrade;
}
So you can define an Array member like:
TGrade *m_Grades;   // will point to an array of grades
Also will be useful a flag variable to know if all data has been read correctly
bool m_DataValid;


Now, you can modify your serialize function to fill members properly:
void CApuDoc::Serialize(CArchive& ar)
{
  if (ar.IsLoading()) {
     m_DataValid = false;   // will be false until end of loading
     CString  line;
     if (!ar.ReadString(line))
             return;
     m_FirstName = line.SpanExcluding(" ");  // up to first space
     m_LastName = line.Right(line.GetLength()-m_FirstName.GetLength()-1);   // easy way to extract 2nd name, works in most cases

     if (!ar.ReadString(line))
             return;
     m_NumOfGrades = atoi(line);
     if (m_NumOfGrades<=0)
             return;

     m_Grades = new TGrade[m_NumOfGrades];    // Creates array dinamically according to size
     TGrade newGrade;
     for (int i=0; i<m_NumOfGrades; i++) {
           if (!ar.ReadString(line))
                return;
           newGrade.TestName = line.SpanExcluding(" ");  // up to first space
           line = line.Right(line.GetLength()-m_newGrade.TestName .GetLength()-1);   // similar to above
           sscanf( (LPCTSTR)line, "%c %i", &newGrade.LetterGrade, &newGrade.NumericGrade);          
     }
     m_DataValid = true;   // all data has been read correctly
     UpdateAllViews(NULL);
   }
}

Now all your data is correctly stored in many doc's members, at update function,  you will have to reconstruct all in a single string to pass it to the edit control.
Now is your turn, go ahead!
0
 
TestsubbuAuthor Commented:
Thanks so much...u've been a lot of help to me !
I reconstructed the individual members into a single CString (m_contents) and was able to display it into the EditView. I also passed the individual doc members to the editboxes my Dialog (which is called from a menu). This works fine !!
But I just found a bug. When I say File->Open and load my file, the file get's loaded once, but when I say File->Open again, I do not want it to append the loaded contents into the existing file. I would like it to load the contents fresh again ??
Should I modify the OnUpdate function??
This is what I have in the OnUpdate function:

               Edit.SetWindowText(GetDocument()->m_Contents);    
0
 
Jaime OlivaresCommented:
You have to clear m_Contents before filling it:

OnUpdate(....) {
     m_Contents = "";
     // Do all your operations to fill it
     Edit.SetWindowText(GetDocument()->m_Contents);
}

Also, to avoid a memory leak, you have to destroy previous information by handling the OnNewDocument() and the destructor function. So, I suggest you to create a doc's Clean() function so you can invoke it from both mentioned functions. Something like:

CYourDoc::Clean()
{
    if (m_Grades != NULL)
          delete [] m_Grades;
    m_Grades = NULL;
}

CYourDoc::~CYourDoc()
{
      Clean();
}

BOOL CCartaDoc::OnNewDocument()       // Create it using ClassWizard (by pressing Ctrl-W)
{
      Clean();
      return CDocument::OnNewDocument();
}
0
 
Jaime OlivaresCommented:
Also you will need:
CYourDoc::CYourDoc()
{
      m_Grades = NULL;   // ensure pointer is correctly initializated
}
0
 
itsmeandnobodyelseCommented:
>>>> should I use CEditView , CView or CFormView as the base class for the View Class ??

I would use a CFormView, where you could design an appropriate form using VC Resource Editor.

You would have edit fields for any student attribute. A menu or buttons to define what to do next. The students grid could be displayed by using a CListCtrl in 'Report Style'. You would add any new student entered or read from file to the grid.

I don't think that using CArchive has any advantages before file streams. CArchive is used to stream 'documents' but your prog has records rather than one SDI document. You could use CMyDoc::OnSaveDocument to write all student records of the grid to a text file and ofstream is a godd choice for that.

Regards, Alex




0
 
TestsubbuAuthor Commented:
Experts..sorry for the short break..am back with some more questions on the same topic..
Completed the following:

- able to read and display the file in the edit view "CApuView" (SDI application with CEditView as base class for the View)
- able to select a menu option to go to a dialog box (EditDlg)
- able to update the edit controls and listbox fields in the EditDlg by reading from the current file that is open in the view
 I did this by -->
           a.   declaring CApuDoc* m_pDoc in the public section of EditDlg.h
           b.   In the Event Handler (menu) which opens the EditDlg, I wrote:   m_dlg.m_pDoc = GetDocument()
                 (where m_dlg is an instance of EditDlg)
           c.  Then in the OnInitDialog of the EditDlg, I wrote:
               CString DocName = m_pDoc->GetPathName();
      CStudent s;      // this is the main object class which is updated with every change              
      ifstream ifl;
      ifl.open(DocName);
                ifl.close();
      m_FirstEdit = s.m_strFirstName;
      m_LastEdit = s.m_strLastName;
   
- I also provided buttons and respective event handlers within the EditDlg box to track the change of data in the controls
and update the CStudent class respectively.

But now I just have one problem.  Once the user presses, "Save and Exit" button from this dialog box, I would like to capture all the changes made in the Dialog box controls and update the Primary View (which is CApuView) with these changes, and then close the dialog box.
Am able to save the changes back to the file, and close the dialog box but the changes do not reflect in the View. What should I do:
Here is what I have for the "Save and Exit" button:
      s.m_strFirstName = m_FirstEdit;
      s.m_strLastName = m_LastEdit;
      CString DocName = m_pDoc->GetPathName();
      ofstream ofl;
      ofl.open(DocName);
      if (ofl)
      ofl << s;
      ofl.close();
       m_pDoc->UpdateAllViews(NULL);
      OnOK();   // NOw exit the application
'

CAN Someone PLEASE HELP ME ON THIS!!!

-shan
0
 
TestsubbuAuthor Commented:
Just thought wud also let u know what am doing in CApuDoc and CApuView -->

void CApuDoc::Serialize(CArchive& ar)
{
   CString strTemp;
   m_Contents = "";
   if (ar.IsLoading())
   {
     CString line;
     while(ar.ReadString(strTemp))
     {
     m_Contents += strTemp + "\r\n";   // m_Contents is a doc's member
     }    
    UpdateAllViews(NULL);
   }
}


void CApuView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
      CEdit& Edit =  CEditView::GetEditCtrl();
      Edit.SetReadOnly();
    Edit.SetWindowText(GetDocument()->m_Contents);  
}
0
 
TestsubbuAuthor Commented:
Experts,
This is the final question I have..so if this is resolved..I shud be done...sorry for the long wait....any input would be greatly appreciated.
-shan
0
 
Jaime OlivaresCommented:
>Here is what I have for the "Save and Exit" button:
>     s.m_strFirstName = m_FirstEdit;
>     s.m_strLastName = m_LastEdit;
>     CString DocName = m_pDoc->GetPathName();
>     ofstream ofl;
>     ofl.open(DocName);
>     if (ofl)
>     ofl << s;
>     ofl.close();
>      m_pDoc->UpdateAllViews(NULL);
>     OnOK();   // NOw exit the application

Well, this is not a standard practice, better you can update proper Doc's members, and use OnSaveDocument() method to save the file.
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

  • 8
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now