PDA

View Full Version : سریال سازی کردن کلاس CTREE



taze kar
یک شنبه 09 اسفند 1383, 15:18 عصر
با سلام

آقا ما هر کار میکنیم اطلاعات این کلاس رو بریزیم توی یه فایل نمیشه

میشه لطف کنید و بگید چه جوری میتونم این کار رو انجام بدم؟ :mrgreen:

Aminm_666
جمعه 14 اسفند 1383, 01:02 صبح
تا منظور از اطلاعات چی باشه؟ مثلاْ نام تمام آیتمهای این کلاس(منظورم پنجره شه) با همون ترتیب درختی از والد و فرزند و نوه و ... تو یه فایل ذخیره شه؟ که بعدها قابل بازیابی باشه. یا...

taze kar
یک شنبه 23 اسفند 1383, 14:02 عصر
سلام

قیقا منظورم همینه

:mrgreen:

Aminm_666
دوشنبه 15 فروردین 1384, 04:26 صبح
این پایین یه برنامه نمونه است که آیتمهای کنترل tree رو سریالی می‌کنه. در نوشتن این برنامه فرض رو بر این گذاشتم که کنترل tree فقط و فقط یک آیتم ریشه دارد و تمام آیتمها از این ریشه نشات گرفتن. این برنامه هم طوری عمل می‌کنه که اجازه نمیده بیش از یک ریشه اصلی در کنترل درج بشه. در غیراینصورت برنامه نمیتونه اون رو درست سریالی کنه.
مثلاْ در این نمای درختی MFC ریشه اصلی‌ه و آیتم دیگری همتراز با اون نمی تونه گنجانده بشه. (در این مثال CObject و CPoint و CSize و CRect همترازن):

_ MFC
|_ CObject
| |_ CCmdTarget
| | |_ CWinThread
| | | |_ CWinApp
| | |_ CWnd
| | |_ CFrameWnd
| | |_ CMDIChildWnd
| | |_ CMDIFrameWnd
| |_ CView
|_ CPoint
|_ CSize
|_ CRect


برای سریالی کردن کل آیتمها اینطوری عمل می‌کنیم. ابتدا نام آیتم بعد تعداد فرزندانش. اینکار برای هر آیتم تکرار میشه تا به انتها برسیم. برای اینکار یک کلاس قابل سریالی کردن ایجاد می‌کنیم مثل این:

/////////////////////////////////////////////////////////////////////////////
// header file

class CTreeNode : public CObject
{
DECLARE_SERIAL(CTreeNode)

public:
CTreeNode();
CTreeNode(LPCTSTR lpszName, int nChilds);

CString m_strName; // نام آیتم
int m_nChilds; // تعداد فرزندان

// Overrides
virtual void Serialize(CArchive& ar);
};

/////////////////////////////////////////////////////////////////////////////
// source file

IMPLEMENT_SERIAL(CTreeNode, CObject, 1)

CTreeNode::CTreeNode()
{
m_nChilds = 0;
}

CTreeNode::CTreeNode(LPCTSTR lpszName, int nChilds)
{
m_strName = lpszName;
m_nChilds = nChilds;
}

void CTreeNode::Serialize(CArchive& ar)
{
CObject::Serialize(ar);

if (ar.IsStoring())
ar << m_strName << m_nChilds;
else
ar >> m_strName >> m_nChilds;
}

اینا حداقل اطلاعاتیه که لازمه تا بشه آیتمها رو سریالی کرد. میشه این کلاس رو گسترش داد و اطلاعات دیگری مثل شاخص تصویر(image index) یا هر اطلاعات دلخواه دیگری به کلاس افزود. فقط کافیه موقع جمع آوری اطلاعات از tree این اعضا رو مقداردهی کرد و تابع Serialize رو بطور مناسبی دستکاری کرد. مثلاْ اگر m_nImageIndex رو شاخص تصویر آیتم فرض کنیم میشه Serialize رو اینجوری دستکاری کرد:

if (ar.IsStoring())
ar << m_strName << m_nChilds << m_nImageIndex;
else
ar >> m_strName >> m_nChilds >> m_nImageIndex;

(و البته باید این عضو قبل از سریالی کردن داخل تابع WriteToArchive که در پی میاد مقدار دهی بشه. و در تابع ReadFromArchive بعنوان شاخص تصویر آیتم بیاد.)

تابع Serialize یه تابع مجازیه که توسط کلاس document فراخوانی میشه و توابع ReadFromArchive و WriteToArchive توابع کمکی هستن که توسط ما به کلاس اضافه می‌شن:


void CProTreeView::Serialize(CArchive& ar)
{
ASSERT_VALID(this);

if (ar.IsStoring())
{
// شروع سریالی کردن با آیتم ریشه
WriteToArchive(ar, GetTreeCtrl().GetRootItem());
// در انتها علامت پایان-آیتمها سریالی می شود
afxEndOfNodes.Serialize(ar);
}
else
{
.
.
.
// حذف تمام آیتمهای کنترل
GetTreeCtrl().DeleteAllItems();
// شروع خواندن از سریال ابتدا با درج ریشه
ReadFromArchive(ar, TVI_ROOT);
.
.
.
}
}

void CProTreeView::ReadFromArchive(CArchive& ar, HTREEITEM hParent)
{
CTreeNode node;

node.Serialize(ar); // خواندن از سریال
if (node.m_nChilds == afxEndOfNodes.m_nChilds) // به انتهای آیتمها رسیده؟
return;

// درج آیتم خوانده شده
HTREEITEM hItem = GetTreeCtrl().InsertItem(node.m_strName, 0, 0, hParent);
for (int n = 0; n < node.m_nChilds; n++)
ReadFromArchive(ar, hItem); // درج فرزندان
}

void CProTreeView::WriteToArchive(CArchive& ar, HTREEITEM hItem)
{
CTreeNode node;

while (hItem != NULL)
{
node.m_strName = GetTreeCtrl().GetItemText(hItem);

if (GetTreeCtrl().ItemHasChildren(hItem)) // اگر آیتم(های) فرزندی دارد
{
// بدست آوردن تعداد آیتمهای فرزند
HTREEITEM hChildItem = GetTreeCtrl().GetChildItem(hItem);
for (int n = 0; hChildItem != NULL; n++)
hChildItem = GetTreeCtrl().GetNextSiblingItem(hChildItem);

node.m_nChilds = n;
}
else
node.m_nChilds = 0;

// قبل از رسیدن به این نقطه باید جمع آوری اطلاعت به پایان رسیده باشد
node.Serialize(ar);

if (node.m_nChilds > 0)
WriteToArchive(ar, GetTreeCtrl().GetChildItem(hItem));

hItem = GetTreeCtrl().GetNextSiblingItem(hItem);
}
}
توابع کمکی که سریالی کردن و از سریال خواندن رو بعهده دارن بصورت بازگشتی نوشته شدن. بازگشتی بودنشون باعث میشه که هر چه عمق یه آیتم بیشتر باشه(دارای فرزند و نوه و نتیجه و نبیره و ندیده باشه) حافظه بیشتری مصرف بشه. راه غیر بازگشتی به ذهنم نرسید.
afxEndOfNodes یک شی از CTreeNodeه که عضو m_nChildsش برابر با منفی یک ه. چون هیچ آیتمی نمیتونه تعداد فرزندانش منفی یک باشه. ازش بعنوان پایان دهنده استفاده شده تا موقع خواندن از آرشیو وقتی به این آیتم رسید بفهمه که به انتها رسیده و کار خواندن رو پایان بده.

یک فایل دیگه هم ضمیمه این پروژه است به اسم mfc.ptd که با این برنامه ایجاد شده و تمام کلاسهای MFC رو بصورت درختی در بر داره.

در VC++6 یک اشکال بوجود میآد. اولین آیتم وقتی اضافه میشه نامرییه ولی با اضافه شدن یک آیتم دیگه خودش رو نشون میده. البته این اشکال در VC++7 بوجود نمیآد. دلیلش رو هم نفهمیدم. در هر صورت به اصل قضیه ربطی نداره.

taze kar
چهارشنبه 17 فروردین 1384, 18:19 عصر
آقا دستت درد نکنه

:تشویق: :flower: