تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
[مقال] Notify Collection Changed
#1
السلام عليكم ورحمة الله وبركاته

كيف يمكن عمل Enumerate لأي Collection بحيث عندما نقوم بحذف أو عندما نقوم بإضافة عناصر الي هذه Collection يتم عمل Update لواجهة التطبيق بشكل أتوماتيكي ..... بصراحة السؤال هنا قد يكون غير واضحا و ربما لا يكون مفهوما للبعض علي الإطلاق
لذلك من الأفضل أن نصنع مثالا أو نكتب كودا بسيطا لتوضيح المغزي من السؤال و كيفية الإجابة عليه

1-: لنفنح مشروع ثم نضيف الي الفورم ListBox و عدد إثنان Button
هذا الفورم بالاضافة الي الكونترول الموجودة به هو ما يمكن أن نطلق عليه واجهة التطبيق

2-: نقوم بتعريف Collection وهنا سوف استخدم Generic ObservableCollection و طبقا لموقع MSDN الخاص بشركة مايكروسوفت سيتضح لنا أن هذه  Collection  قد تكون مصممة للاستخدام مع WPF و ليس مع الويندوز فورم ..... لكن و بشكل عام هذا لن يمنعنا إطلاقا من استخدامها مع الويندوز فورم  لأنها بالنهاية مثلها مثل أي Collection اخري ..... وعلي العموم فإن مايكروسوفت تنصح بإستخدام  Generic BindingList[/url] أو Generic List او Generic Collection

3-: في الحدث Load الخاص ب الفورم نقوم ب تعبئة Collection ببعض String مثلا
4-: نقوم بإضافة Handler لكي نطلق الحدث  CollectionChanged
5-: نقوم بتعبئة ListBox الموجودة علي الفورم ب البيانات الموجودة داخل Collection
6-: في الحدث Click الخاص بالباتون رقم 1 نحاول ان نحذف Item من Collection
7-: في الحدث Click و الخاص بالباتون رقم 2 نحاول ان نضيف Item جديدة الي Collection
8-: في الكود الخاص بالحدث CollectionChange نقوم بإعلام ListBox أن هناك بعض التغييرات قد حدثت داخل  Collection  و من ثم و باستخدام Collection نقوم بتعبئة هذا ListBox بالبيانات

في الكود التالي ستجدون الكود الكامل للخطوات السابقة


كود :
Public Class Form1

   ' نقوم بتعريف المصفوفة
   Private myCollection As System.Collections.ObjectModel.ObservableCollection(Of String) = New System.Collections.ObjectModel.ObservableCollection(Of String)

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
       ' نقوم بتعبئة المصفوفة
       Me.myCollection.Add("Omar")
       Me.myCollection.Add("Amin")
       Me.myCollection.Add("Ibrahim")

       ' نقوم بإضافة Handler
       AddHandler myCollection.CollectionChanged, New System.Collections.Specialized.NotifyCollectionChangedEventHandler(AddressOf OnCollectionChanged)
       ' نقوم بتعبئة البيانات في الليست بوكس
       Me.FillListBox()

   End Sub

   ' لنعيد تعبئة الليست بوكس بالبيانات من المصفوفة عندما يحدث حذف او اضافة
   Private Sub OnCollectionChanged(sender As Object, e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
       Me.FillListBox()
   End Sub

   '  نملا الليست بوكس بالبيانات الموجودة في المصفوفة
   Private Sub FillListBox()
       Me.ListBox1.Items.Clear()
       For Each s As String In myCollection
           Me.ListBox1.Items.Add(s)
       Next
   End Sub

   ' لنحاول حذف احد البيانات من المصفوفة
   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
       Dim item As String = "Ibrahim"
       If Me.myCollection IsNot Nothing Then
           Me.myCollection.Remove(item)
       End If
   End Sub

   ' لنحاول اضافة عنصر جديد الي المصفوفة
   Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
       Dim item As String = "Mahmoud"
       If Not myCollection.Contains(item) Then
           Me.myCollection.Add(item)
       End If

   End Sub

End Class

الأن قم بتجربة المشروع لتري ماذا سيحدث عندما تقوم بحذف او اضافة عنصر معين من Collection

كنوع من التمرين .... حاول أن تستخدم Generic BindingList بدلا من Generic ObservableCollection ستجد انها سوف تعطيك نفس النتائج


الأن  نأتي للهدف من المقال و لنسأل انفسنا هذه الاسئلة البسيطة:

1-: لماذا كل Generic Collection الموجودة في الدوت نت لا تدعم نفس الأسلوب ?
مثلا Generic HashSet و Generic List لا تدعمان هذا الأسلوب المستخدم مع Generic ObservableCollection

الاجابة علي هذا السؤال عند مايكروسوفت و للأسف انا ليس لدي اجابة واضحة بل علي العكس كثرة Generic Collection يصيبني أحيانا بنوع من عدم الفهم فأحيانا لا استطيع ان احدد متي استخدم هذه Collection  او متي استخدم Collection أخري مختلفة
و هنا غالبا ما اقوم بقراءة التفاصيل الخاصة بكل Collection لأفهمها بشكل معقول يساعدني علي استخدامها في احد مشروعاتي

2:- هل استطيع ان اقوم باستخدام نفس الاسلوب مع اي Generic Collection او مع اي Collection  عادية؟

نعم تستطيع استخدام نفس الاسلوب لكن يجب ان يكون لديك فهم بكيفية بناء EventArgs و كيفية بناء EventHandler
بل تستطيع ان يكون لديك اسلوبك الخاص لبناء مصفوفات مشابهة خاصة أن هذا الأسلوب يوفر علي المبرمج الكثير من الوقت و الجهد

3:- كيف اصمم أو كيف أقوم بتطوير بعض Generic Colletion لأجعلها تدعم نفس الاسلوب؟
هذا السؤال سنجيب عليه في المشاركة التالية

المراجع:

[url=https://msdn.microsoft.com/en-us/library/system.collections.specialized.inotifycollectionchanged%28v=vs.110%29.aspx]INotifyCollectionChanged


Generic List

Generic ObservableCollection

NotifyCollectionChangedAction

NotifyCollectionChangedEventArgs

Generic HashSet

Generic BindingList


تقبلوا تحياتي
اخوكم عمر
الرد }}}
تم الشكر بواسطة: khodor1985 , khodor1985 , الشاكي لله
#2
بارك الله فيك اخوي عمر

لفتة رائعة منك

لكن السؤال هنا . في WPF انت لست بحاجة الى عمل Handler لحدث collection changed
مما يعني انك لست بحاجة الى دالة تقوم بعمل لووب لاضافة العناصر عند كل تحديث (اقصد دالة FillListbox)

لان الbinding في wpf يتكفل بموضوع التحديث أوتوماتيكيا ..
فهل يمكن عمل ذلك فيWinForm؟

لان اللوب ممكن يتاخر لو كانت البيانات كبيرة
الرد }}}
تم الشكر بواسطة:
#3
السلام عليكم ورحمة الله وبركاته

في هذه المشاركة سنوضح كيفية بناء l Generic Listو كيفية بناء مصفوفة من النوع Generic تدعم نفس الأسلوب اعلاه أي اننا هنا سنجعل المصفوفة الجديدة تعلم متي يحدث حذف عنصر منها و متي يحدث إضافة عنصر جديد لها

و لسوف أطلق عليها ObservableList حيث سنقوم بتوريث Generic List لها
اي ان أساس المصفوفة الجديدة سيكون  Generic List

و الشكل المبدئي للمصفوفة الجديدة سيكون كالتالي :

كود :
Public Class ObservableList(Of T)
   Inherits List(Of T)

End Class ' ObservableList(Of T)

الأن لنحدد نوعية التغييرات التي نحتاجها عندما تتغير هذه Collection و هي ستكون عبارة عن Enum
و هنا نحن نحتاج فقط أن نعرف متي سيجدث اضافة أو متي سوف يحدث جذف بيانات من Collection الجديدة

كود :
Public Enum ObservableChangeAction
   Add
   Remove
End Enum

الأن نحتاج لكتابة الكلاس ObservableEventArgs


كود :
Public Class ObservableEventArgs
   Inherits EventArgs

   Public Sub New(item As Object, changeAction As ObservableChangeAction)
       Me.Item = item
       Me.ChangeAction = changeAction
   End Sub

   Public Property Item As Object
   Public Property ChangeAction As ObservableChangeAction

End Class ' ObservableEventArgs

الأن لنقوم بالتعديل علي الكلاس الخاص بالمصفوفة و نضيف لها Handler
و نمرر له الكلاس ObservableEventArgs ثم نكتب الكود الخاص باطلاق الحدث الخاص به

فيكون شكل الكلاس بعد التعديل كالتالي:

كود :
Public Class ObservableList(Of T)
   Inherits List(Of T)

#Region " Method "

   Protected Sub OnChanged(item As T, action As ObservableChangeAction)
       RaiseEvent Changed(Me, New ObservableEventArgs(CType(item, Object), action))
   End Sub

#End Region

#Region " Event "

   Public Event Changed As EventHandler(Of ObservableEventArgs)

#End Region

End Class ' ObservableList(Of T)

و بما أننا نحتاج فقط الي أن تكون المصفوفة لديها دراية بعمليات الحذف و الاضافة عند حدوثها لذلك فنحن هنا لا نحتاج سوي انا نقوم باطلاق الحدث الذي يخبرنا ان المصفوفة تغيرت و هذا سيتم بعمل OverLoad لكل من Add Method و Remove Method فقط

و هنا أيضا نحن في حاجة أن نتأكد ان العنصر المراد حذفه موجود بالفعل في المصفوفة و أيضا نحتاج ان نتاكد أن العنصر المراد اضافته ليس موجودا بالفعل داخل المصفوفة و لنفعل ذلك نحتاج الي قراءة  جميع العناصر الموجودة في المصفوفة و من ثم نقارنها بالعنصر المراد حذفه او المراد اضافته و من ثم نحذف هذا العنصر او نضيفه للمصفوفة

و بهذ سيكون الشكل النهائي للكلاس كالتالي:

كود :
Public Class ObservableList(Of T)
   Inherits List(Of T)

#Region " Constructor "

   Public Sub New()

   End Sub

#End Region

#Region " Property "

   Public ReadOnly Property InnerList As IEnumerable(Of T)
       Get
           Return MyBase.ToList.AsReadOnly
       End Get
   End Property

#End Region

#Region " Method "

   Public Overloads Sub Add(item As T)
       If Not Me.InnerList.Contains(item) Then
           MyBase.Add(item)
           Me.OnChanged(item, ObservableChangeAction.Add)
       End If
   End Sub

   Public Overloads Sub Remove(item As T)
       If Me.InnerList.Contains(item) Then
           MyBase.Remove(item)
           Me.OnChanged(item, ObservableChangeAction.Remove)
       End If
   End Sub

   Protected Sub OnChanged(item As T, action As ObservableChangeAction)
       RaiseEvent Changed(Me, New ObservableEventArgs(CType(item, Object), action))
   End Sub

#End Region

#Region " Event "

   Public Event Changed As EventHandler(Of ObservableEventArgs)

#End Region

End Class ' ObservableList(Of T)


الأن لنعطي مثالا لكيفية استخدام المصفوفة الجديدة و هنا سوف اقوم باستخدام نفس فكرة المثال الذي تم استخدامه في بداية المقال

كود :
Public Class Form1

   ' نقوم بتعريف المصفوفة
   Private myCollection As ObservableList(Of String) = New ObservableList(Of String)

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
       ' نقوم بتعبئة المصفوفة
       Me.myCollection.Add("Omar")
       Me.myCollection.Add("Amin")
       Me.myCollection.Add("Ibrahim")

       ' نقوم بإضافة Handler
       AddHandler myCollection.Changed, AddressOf OnCollectionChanged
       ' نقوم بتعبئة البيانات في الليست بوكس
       Me.FillListBox()

   End Sub

   ' لنعيد تعبئة الليست بوكس بالبيانات من المصفوفة عندما يحدث حذف او اضافة
   Private Sub OnCollectionChanged(sender As Object, e As ObservableEventArgs)
       Me.FillListBox()
   End Sub

   '  نملا الليست بوكس بالبيانات الموجودة في المصفوفة
   Private Sub FillListBox()
       Me.ListBox1.Items.Clear()
       For Each s As String In myCollection
           Me.ListBox1.Items.Add(s)
       Next
   End Sub

   ' لنحاول حذف احد البيانات من المصفوفة
   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
       Dim item As String = "Ibrahim"
       If Me.myCollection IsNot Nothing Then
           Me.myCollection.Remove(item)
       End If
   End Sub

   ' لنحاول اضافة عنصر جديد الي المصفوفة
   Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
       Dim item As String = "Mahmoud"
       If Not myCollection.Contains(item) Then
           Me.myCollection.Add(item)
       End If

   End Sub

End Class


الخلاصة:

من كل ما سبق هو ان هذا الأسلوب قد وفر لي الكثير من الجهد و الوقت و علي ما أعتقد أن هنا تكمن اهمية المصفوفات الجديدة التي تكتبها مايكروسوفت

و لمزيد من المصفوفات .....  قمت بإتباع نفس الاسلوب و كتابة مصفوفة أخري و لكنها موروثة من Generic HashSet و الكود الخاص بهذه المصفوفة سيكون كالتالي

كود :
Public Class ObservableHashSet(Of T)
   Inherits HashSet(Of T)

#Region " Constructor "

   Public Sub New()

   End Sub

#End Region

#Region " Property "

   Public ReadOnly Property InnerList As IEnumerable(Of T)
       Get
           Return MyBase.ToList.AsReadOnly
       End Get
   End Property

#End Region

#Region " Method "

   Public Overloads Sub Add(item As T)
       If MyBase.Add(item) Then
           Me.OnChanged(item, ObservableChangeAction.Add)
       End If
   End Sub

   Public Overloads Sub Remove(item As T)
       If MyBase.Remove(item) Then
           Me.OnChanged(item, ObservableChangeAction.Remove)
       End If
   End Sub

   Protected Sub OnChanged(item As T, action As ObservableChangeAction)
       RaiseEvent Changed(Me, New ObservableEventArgs(CType(item, Object), action))
   End Sub

#End Region

#Region " Event "

   Public Event Changed As EventHandler(Of ObservableEventArgs)

#End Region

End Class ' ObservableHashSet(Of T)

تمرين :

حاول تستخدم Generic ObservableHashSet في المثال اعلاه و ستحصل علي نفس النتائج

في المرفقات ستجدون الكود بتسخة الفيجوال استوديو 2012

تقبلوا تحياتي

أخوكم عمر

أخي الفاضل الشاكي لله

هذا كان مجرد مثالا لتوضيح الأمر لا غير و حتي الأن لم أحاول ان اقوم بعمل Binding للكلاسات الجديدة مع اي كونترول عموما ساحاول ان ادرس الامر لاحقا و لسوف أبلغك بما حدث معي


الملفات المرفقة
.rar   Test_ObservableList.rar (الحجم : 75.76 ك ب / التحميلات : 35)
الرد }}}
#4
^_^
نعم ارجو ان تخبرنا بطريقة عمل biniding بين الكلاس وبين الكونترولز

موفق
الرد }}}
تم الشكر بواسطة: khodor1985


المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  Collection & DataBase Part 2 silverlight 0 2,089 02-12-15, 08:42 PM
آخر رد: silverlight
  Collection & DataBase Part 1 silverlight 1 2,572 25-07-15, 07:55 AM
آخر رد: silverlight

التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم