Міністерство освіти і науки України
Національний університет „Львівська політехніка”
Кафедра ЕОМ
Звіт
з навчальної практики
з дисципліни: “Програмування (Об’єктно-орієнтоване)”
Мета практики:
Ознайомитися з бібліотекою MFC (Microsoft Fundation Clasess) і розробити програмне забезпечення для операційних систем Windows на основі MFC.
Ознайомися з діалоговою, однодокументною та багатодокументною архітектурами побудови програм.
Ознайомитися з архітектурою документ-вигляд.
Завдання практики:
Розробити програму “Калькулятор”, яка б працювала з вісімковими беззнаковими числами. Передбачити операції додавання віднімання множення і ділення. Передбати отримання остачі від ділення, якщо вона є. Розробити діалоговий, однодокументний і багатодокументний варіанти програми.
Аналіз завдання та опис вирішення задачі:
Далогова програма
Для створення діалогової програми я вибрав відповідний тип DialogBased. Зробивши ще деякі налаштування, було отримано проект із згенерованим шаблонним кодом програми. Для створення калькулятора, я розмістив на ньому елементи керування кнопки та поля вводу/виводу. Також додав обробники подій для кнопок і зв’язав поля з змінними типу Cstring. Передбачив можливі помилки вводу даних і отримання невірних результатів.
Текст програми
/////////////////////////////////////////////////////////////////////////////
// CDialogDlg dialog
class CDialogDlg : public CDialog
{
// Construction
public:
CDialogDlg(CWnd* pParent = NULL); // standard constructor
BOOL CheckEdit(CEdit *); // Перевірка правильності вводу
BOOL GetResult(char ch); // Обчислити результа
// Dialog Data
//{{AFX_DATA(CDialogDlg)
enum { IDD = IDD_DIALOG_DIALOG };
CEdit m_ostacha;
CEdit m_result;
CEdit m_second;
CEdit m_first;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDialogDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CDialogDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnButton1();
afx_msg void OnButton2();
afx_msg void OnButton3();
afx_msg void OnButton4();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_DIALOGDLG_H__4C9FF1F1_8D07_408B_BF1A_F60145DDF3B7__INCLUDED_)
// DialogDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Dialog.h"
#include "DialogDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDialogDlg dialog
CDialogDlg::CDialogDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDialogDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDialogDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDialogDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDialogDlg)
DDX_Control(pDX, IDC_EDIT4, m_ostacha);
DDX_Control(pDX, IDC_EDIT3, m_result);
DDX_Control(pDX, IDC_EDIT2, m_second);
DDX_Control(pDX, IDC_EDIT1, m_first);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CDialogDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
ON_BN_CLICKED(IDC_BUTTON3, OnButton3)
ON_BN_CLICKED(IDC_BUTTON4, OnButton4)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDialogDlg message handlers
BOOL CDialogDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CDialogDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CDialogDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
CDialog::OnPaint();
}
HCURSOR CDialogDlg::OnQueryDragIcon(){return (HCURSOR) m_hIcon;}
BOOL CDialogDlg::GetResult(char ch) {
int i,k = 1;int f=0,s=0,r,o=0;CString tmp;
if(CheckEdit(&m_first) && CheckEdit(&m_second)){ m_first.GetWindowText(tmp);
if (tmp!=""){
for(i=m_first.GetWindowTextLength()-1;i >-1 ;i--){
f+=(tmp[i]-48)*k;
k*=8;
}
}
k=1;
m_second.GetWindowText(tmp);
if (tmp!=""){
for(i=m_second.GetWindowTextLength()-1;i >-1 ;i--){
s+=(tmp[i]-48)*k;
k*=8;
}
}
if (ch=='+')
r=s+f;
if (ch=='-')
if(s>f){
m_result.SetWindowText("Error: Can't calculate");
m_ostacha.SetWindowText("");
return false;
}
else
r=f-s;
if (ch=='*')
r=s*f;
if (ch=='/'){
if (s!=0){
o = f % s;
r = (f - o) / s;
m_result.SetWindowText("");}
else{
m_ostacha.SetWindowText("");
m_result.SetWindowText("Error: Division by zero");
return false;}
}tmp="";
while(r > 7){
tmp+=(char)(r % 8 + 48);
r = (r - ( r % 8 ))/8;
}
tmp+=(char)r+48;
tmp.MakeReverse();
m_result.SetWindowText(tmp);
tmp="";
for(;o > 7;){
tmp+=(char)(o % 8 + 48);
o = (o - ( o % 8 ))/8;
}
tmp+=(char)(o+48);
tmp.MakeReverse();
m_ostacha.SetWindowText(tmp);
}
return true;
}
//Перевіряє правильність вводу
BOOL CDialogDlg::CheckEdit(CEdit *Edit)
{ CString txt;int i;
Edit->GetWindowText(txt);
if(txt!="")
for(i=0;i<Edit->GetWindowTextLength();i++)
if(txt[i] > 55 || txt[i] < 48){
m_result.SetWindowText("Error: Wrong value");
return false;
}
m_result.SetWindowText("");
return true;
}
void CDialogDlg::OnButton1() {GetResult('+');}
void CDialogDlg::OnButton2() {GetResult('-');}
void CDialogDlg::OnButton3() {GetResult('*');}
void CDialogDlg::OnButton4() {GetResult('/');}
Програма в дії
Однодокументна програма
Для створення однодокументної програми я вибрав відповідний тип Single Document. Зробивши налаштування вигляду CEditView, було отримано проект із згенерованим шаблонним кодом програми. Для створення калькулятора, я розмістив на панелі інструментів додаткову кнопку і організував додаткове меню. Організував читання та збереження даних у файл в функції Serialize() ініціалізував дана в функції OnNewDocument(). У класі документа ввід потрібні змінні і продублював їх в класі вигляду. Там же розмістив код у функції OnInnitialUpdate() для дублювання а для обрахунку і виводу результатів функції OnCalc() і Print(). Також задав маску файлу “*.clc” для вікна відкриття і збереження.
Текст програми
/*----------------------------------------------------------------------------*/
class CSecDoc : public CDocument
{
protected: // create from serialization only
CSecDoc();
DECLARE_DYNCREATE(CSecDoc)
// Attributes
public:
CString m_first;
CString m_second;
char m_diya;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSecDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CSecDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CSecDoc)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/*----------------------------------------------------------------------------*/
BOOL CSecDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);
m_first = "0";
m_second = "0";
m_diya = '+';
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CSecDoc serialization
void CSecDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
ar << m_first << m_diya << m_second;
}
else
{
// TODO: add loading code here
ar >> m_first >> m_diya >> m_second;
}
// CEditView contains an edit control which handles all serialization
//((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
/*----------------------------------------------------------------------------*/
class CSecView : public CEditView
{
protected: // create from serialization only
CSecView();
DECLARE_DYNCREATE(CSecView)
// Attributes
public:
CSecDoc* GetDocument();
CString m_first;CString m_second;CString m_result;
CString m_ostacha;char m_diya;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSecView)
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void OnInitialUpdate();
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
void Print();
void GetParams();
virtual ~CSecView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CSecView)
afx_msg void OnCalc();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/*----------------------------------------------------------------------------*/
void CSecView::OnCalc()
{
CString txt;
int i=0,k=1,f=0,r=0,s=0,o=0;
GetWindowText(txt);
if (txt!=""){
if (txt[txt.GetLength()-1]!=' ')
txt+=' ';
m_first="";m_second="";m_result="";m_ostacha="";
while (txt[i] != ' '){
m_first+=txt[i];
i++;
}
txt+=m_first;
m_diya=txt[++i];
i+=2;
while (txt[i] != ' '){
m_second+=txt[i];
i++;
}
GetDocument()->m_first = m_first;
GetDocument()->m_second = m_second;
GetDocument()->m_diya = m_diya;Print();
for(i=m_first.GetLength()-1;i >=0 ;i--)
{f+=(m_first[i]-48)*k;k*=8;}
k=1;
for(i=m_second.GetLength()-1;i >=0 ;i--)
{s+=(m_second[i]-48)*k;k*=8;}
if (m_diya=='+')
r=s+f;
if (m_diya=='-')
if(s>f){
m_result="Error: Can't calculate";
m_ostacha="";
}
else
r=f-s;
if (m_diya=='*')
r=s*f;
if (m_diya=='/'){
if (s!=0){
o = f % s;r = (f - o) / s;
}
else{m_ostacha=""; m_result="Error: Division by zero";}
}
if(m_result!="Error: Division by zero" && m_result!="Error: Can't calculate"){
for(;r > 7;){
m_result+=(char)(r % 8 + 48);
r = (r - ( r % 8 ))/8;
}
m_result+=(char)r+48;
m_result.MakeReverse();
}
if (o!=0){
while(o > 7){
m_ostacha+=(char)(o % 8 + 48);
o = (o - ( o % 8 ))/8;
}
m_ostacha+=(char)(o+48);
m_ostacha.MakeReverse();
}
GetWindowText(txt);
txt+= " = "; txt+= m_result;
if(m_ostacha!="")
{txt+="(";txt+=m_ostacha;txt+=")";}
SetWindowText(txt);
}
else
SetWindowText("Error:Wrong Value");
}
void CSecView::OnInitialUpdate()
{
CEditView::OnInitialUpdate();
m_first = GetDocument()->m_first;
m_second = GetDocument()->m_second;
m_diya = GetDocument()->m_diya;
m_result = "0";
m_ostacha= "0";
Print();
}
void CSecView::Print()
{
CString txt;
txt=""; txt+=m_first; txt+=' '; txt+=m_diya; txt+=' '; txt+=m_second;
SetWindowText(txt);
}
Програма в дії
Багатодокументна програма
Для створення багатодокументної програми я вибрав відповідний тип Multi Document. Зробивши налаштування вигляду CДшіеView, було отримано проект із згенерованим шаблонним кодом програми. Я організував читання та збереження даних у файл в функції Serialize() ініціалізував дана в функції OnNewDocument(). У класі документа ввід потрібні змінні і продублював їх в класі вигляду. Там же розмістив код у функції OnInnitialUpdate() для обрахунку результатів і виводу їх у вигляді таблиці. Також задав маску файлу “*.clc” для вікна відкриття і збереження. У функції OnDraw() задав вигляд таблиці.
Текст програми
/*----------------------------------------------------------------------------*/
class CMdiDoc : public CDocument
{
protected: // create from serialization only
CMdiDoc();
DECLARE_DYNCREATE(CMdiDoc)
// Attributes
public:
CString m_first;CString m_second;char m_diya;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMdiDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMdiDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMdiDoc)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/*----------------------------------------------------------------------------*/
void CMdiDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
ar << m_first << m_diya << m_second;
}
else
{
// TODO: add loading code here
ar >> m_first >> m_diya >> m_second;
}
}
BOOL CMdiDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
m_first ="0";
m_second="0";
m_diya ='+';
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
return TRUE;
}
/*----------------------------------------------------------------------------*/
class CMdiView : public CListView
{
protected: // create from serialization only
CMdiView();
DECLARE_DYNCREATE(CMdiView)
// Attributes
public:
CMdiDoc* GetDocument();
CString m_first;
CString m_second;
char m_diya;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMdiView)
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual void OnInitialUpdate(); // called first time after construct
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMdiView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMdiView)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/*----------------------------------------------------------------------------*/
#define NUM_COLUMNS 5
static _TCHAR * g_szColumnLabel[NUM_COLUMNS] = {
_T("First"), _T("Operation"), _T("Second"), _T(""), _T("Result")
};
static int g_nColumnFmt[NUM_COLUMNS] = {
LVCFMT_LEFT, LVCFMT_CENTER, LVCFMT_RIGHT, LVCFMT_CENTER, LVCFMT_RIGHT
};
static int g_nColumnWidth[NUM_COLUMNS] = {
100, 50, 100, 50, 100
};
void CMdiView::OnDraw(CDC* pDC)
{
CMdiDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
void CMdiView::OnInitialUpdate()
{
CListView::OnInitialUpdate();
CListCtrl & ListCtrl = GetListCtrl();
ListCtrl.SetExtendedStyle(LVS_EX_FULLROWSELECT);
int i=0,k=1,f=0,r=0,s=0,o=0;
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
for(i = 0; i < NUM_COLUMNS; i++) {
lvc.iSubItem = i;
lvc.pszText = g_szColumnLabel[i];
lvc.cx = g_nColumnWidth[i];
lvc.fmt = g_nColumnFmt[i];
ListCtrl.InsertColumn(i, &lvc);
}
m_first=GetDocument()->m_first;
m_diya=GetDocument()->m_diya;
m_second=GetDocument()->m_second;
CString m_result="";
CString m_ostacha="";
for(i=m_first.GetLength()-1;i >=0 ;i--)
{f+=(m_first[i]-48)*k; k*=8;}
k=1;
for(i=m_second.GetLength()-1;i >=0 ;i--)
{s+=(m_second[i]-48)*k;k*=8;}
if (m_diya=='+')
r=s+f;
if (m_diya=='-')
if(s>f){
m_result="Error: Can't calculate";
m_ostacha="";
}
else
r=f-s;
if (m_diya=='*')
r=s*f;
if (m_diya=='/'){
if (s!=0){
o = f % s;
r = (f - o) / s;
}
else{
m_ostacha="";
m_result="Error: Division by zero";
}
}
if(m_result!="Error: Division by zero" && m_result!="Error: Can't calculate"){
for(;r > 7;){
m_result+=(char)(r % 8 + 48);
r = (r - ( r % 8 ))/8;
}
m_result+=(char)r+48;
m_result.MakeReverse();}
if (o!=0){
while(o > 7){
m_ostacha+=(char)(o % 8 + 48);
o = (o - ( o % 8 ))/8;
}
m_ostacha+=(char)(o+48);
m_ostacha.MakeReverse();
}
if(m_ostacha!=""){
m_result=m_result+"("+m_ostacha+")";
}
ListCtrl.InsertItem(0,m_first);
ListCtrl.SetItemText(0,1,(CString)m_diya);
ListCtrl.SetItemText(0,2,m_second);
ListCtrl.SetItemText(0,3,"=");
ListCtrl.SetItemText(0,4,m_result);
// TODO: You may populate your ListView with items by directly accessing
// its list control through a call to GetListCtrl().
}
Програма в дії
Висновки:
Бібліотека MFC є базовим потужним засобом для створення GDI програм для операційних систем Windows. На її основі побудовано багато складніших архітектур. Вона забезпечує підтримку діалогової однодокументної і багатодокументної архітектури побудови програм, що було продемонстровано на прикладі програми калькулятор. Дана бібліотека також підтримує архітектуру документ-представлення, яка є важливою при створенні одно і багатодокументних программ що працюють з файлами-документами. Вона забезпечує широкий вибір засобів виводу документів в файл на екран і на друк.