أرجو مساعدتكم في كيفية حفظ الشجرة (treeview) إلى ملف
وشكرا
أرجو مساعدتكم في كيفية حفظ الشجرة (treeview) إلى ملف
وشكرا
اقتباسحفظ عناصر الشجرة في ملفّ:تتوقّع أنّي سأقول لك إنّ الأمر في غاية البساطة، فكلّ ما عليك هو استخدام عمليّة السلسلة Serialization التي شرحناها في فصل "التعامل مع المفّات والمجلّدات"، وبذلك يمكنك حفظ الشجرة وكلّ محتوياتها في ملفّ بجملة واحدة!
للأسف: ليس الأمر بهذه البساطة، فعملية السلسلة لا تستطيع حفظ الكائنات المتراكبة "الشجريّة"، التي يحتوى كلّ كائن منها على كائن آخر من نوعه، يحتوى على كائن ثالث من نوعهما .. وهكذا... وإنّما يمكن استخدام هذه العمليّة في حفظ الكائنات الخطّيّة، التي تحتوى على خصائص ووسائل، ولكن لا تمتلك تركيبا شجريّا متراكبا.
إذن ما العمل؟
يمكننا ببعض التحايل، أن نخزّن كلّ محتويات الشجرة في أيّ مجموعة قابلة للسلسلة (مثل المصفوفة القائمة ArrayList)، ثمّ بعد ذلك نحفظ هذه المجموعة في ملفّ!
لاحظ أنّنا سنحتاج في هذه الطريقة ـ ليس لتخزين قيمة كلّ عنصر فحسب ـ بل كذلك مستوى كلّ عنصر في الشجرة، وذلك حتّى نستطيع أن نعيد كلّ عنصر لفرعه في الشجرة عند تحميل العناصر من الملفّ مرّة أخرى.
من أجل هذا سنعرّف سجلا قابلا للسلسلة Serializable، بحيث يحتوى على خاصيّتين: نصّ العنصر ومستواه في الشجرة:
<Serializable()> Structure sNode
Dim node As String
Dim level As Integer
End Structure
لاحظ استخدامنا للسمة Serializable قبل تعريف السجلّ، وذلك حتّى يكون قابلا للسلسلة عند تخزينه في المصفوفة القائمة.
وبالنسبة لمستوى العنصر، فسيكون صفرا إذا كان العنصر أحد جذور الشجرة، و 1 إذا كان العنصر أحد العناصر المتفرّعة من جذر الشجرة، و 2 إذا كان العنصر متفرّعا من عنصر مستواه = 1، وهكذا... مستوى كلّ عنصر يزيد عن مستوى الفرع الذي يحتويه بواحد.
وعند تحميل الملفّ ستستخدم هذا المستوى، فكلّما زادت قيمته أضفنا عنصرا فرعيّا جديدا للعنصر الحاليّ، وكلّما نقص عدنا إلى الفرع الذي يحتوى على العنصر الحاليّ... وهكذا.
والآن سنكتب كود حفظ الشجرة.. إنّ كلّ ما سنفعله، هو كتابة إجراء اسمه SaveTree لإضافة نصّ العنصر المرسل إليه ومستواه في سجل من النوع sNode، وإضافة هذا السجلّ إلى المصفوفة القائمة TreeList، التي سنعرّفها على مستوى النموذج كالتالي:
Dim TreeList As New ArrayList()
ها هو ذا كود الإجراء SaveTree:
Sub SaveTree(ByVal node As TreeNode)
Dim myNode As sNode
myNode.level = node.FullPath.Split("\"c).GetUpperBound(0)
myNode.node = node.Text
TreeList.Add(myNode)
End Sub
هل أدركت ما يفعله السطر الثاني في الإجراء السابق؟
إنّه يستخدم خاصيّة المسار الكامل للعنصر FullPath ليستنتج منها مستواه، وذلك بتقطيع هذا المسار إلى عناصره.. هذه العناصر تفصل بينها في المسار العلامة "\".. إنّ العناصر الناتجة ستوضع في المصفوفة التي تعيدها الدالة Split.. رقم الخانة الأخيرة في هذه المصفوفة هو مستوى العنصر (لأنّه أقلّ من عدد الخانات بواحد).
الآن كلّ ما علينا فعله هو استدعاء الإجراء ScanNode ـ الذي كتبناه من قبل ـ وإرسال الإجراء SaveTree كمعامل له، بحيث توضع كلّ عناصر الشجرة في المصفوفة القائمة:
Dim RootNode As TreeNode
For Each RootNode In TreeView1.Nodes
ScanNode(RootNode, AddressOf SaveTree)
Next
والآن لم يتبقّ إلا حفظ المصفوفة القائمة بالطريقة التي تعلّمناها من قبل.. افترض أنّ FileName هو متغيّر يحتوى على اسم ومسار الملفّ الذي اختاره المستخدم بواسطة مربّع حوار "حفظ ملفّ".. هذا هو الكود الذي يحفظ المصفوفة القائمة:
Dim formatter As New BinaryFormatter
Dim saveFile As FileStream
saveFile = File.Create(FileName)
formatter.Serialize(saveFile, TreeList)
saveFile.Close()
ولتحميل هذه البيانات من الملفّ إلى المصفوفة القائمة مرّة أخرى، استخدم الكود التالي:
Dim formatter As New BinaryFormatter
Dim openFile As FileStream
openFile = File.Open(FileName, FileMode.Open)
TreeList = CType(formatter.Deserialize(openFile), ArrayList)
openFile.Close()
لم يبقَ إلا إعادة العناصر إلى أفرعها على الشجرة.. هذا هو الكود الذي يقوم بهذا:
Dim o As sNode
Dim currNode As TreeNode
Dim CurrLevel As Integer = 0
Dim i As Integer
For i = 0 To TreeList.Count - 1
o = CType(TreeList(i), sNode)
If o.level = CurrLevel Then
If CurrLevel = 0 Then
' هذا العنصر من جذور الشجرة
currNode = TreeView1.Nodes.Add(o.node)
Else ' العنصران في مستوى واحد،
' أضف العنصر الجديد لنفس فرع العنصر الحالي
' العنصر الذي سنضيفه سيصبح هو العنصر الحالي
currNode = currNode.Parent.Nodes.Add(o.node)
End If
ElseIf o.level > CurrLevel Then
' مستوى العنصر الجديد أكبر من مستوى العنصر الحالي
' إذن فالعنصر الجديد متفرّع من العنصر الحالي
' وسيصبح العنصر الجديد بعد إضافته هو العنصر الحالي
currNode = currNode.Nodes.Add(o.node)
' لا تنس أن تزيد مستوى العنصر الحالي بواحد
CurrLevel = o.level
Else ' إذا كان مستوى العنصر الجديد أصغر من المستوى الحالي
' فيجب أن ننقص المستوى الحالي بمقدار الفرق بينَهما
' إلى أن نصل للفرع الذي سنضع فيه العنصر الجديد
While o.level <= CurrLevel
currNode = currNode.Parent
CurrLevel -= 1
End While
' أضف العنصر الجديد للفرع الذي وصلنا إليه،
' واجعل العنصر الجديد هو العنصر الحالي
currNode = currNode.Nodes.Add(o.node.ToString)
' لا تنس أن تزيد مستوى العنصر الحالي بواحد
CurrLevel = o.level
End If
Next
ويحتوى النموذج الرئيسيّ في المشروع Globale في مجلّد برامج هذا الفصل على القائمة File، التي تمنحك الأمران Save و Load، وهما يقومان بالوظيفتين اللتين شرحناهما توّا.. الجدير بالذكر أنّ هذا التطبيق قد وضع دالتي الحفظ والتحميل في خليّة Class، حتّى يمكنك استخدام هذه الخليّة بسهولة من أيّ مشروع آخر.
منتقول من كتاب ... احتراف Visual Basic .Net لمحمد حمدي غانم