سؤال

لديّ TreeView مرتبط بمثيلات Tree of ViewModel. المشكلة هي أن بيانات النموذج تأتي من مستودع بطيء ، لذا أحتاج إلى محاكاة افتراضية للبيانات. يجب تحميل قائمة ViewModel الفرعية أسفل العقدة فقط عند توسيع عقدة عرض الشجرة الأصل ويجب تفريغها عند انهيارها.

كيف يمكن تنفيذ ذلك أثناء الالتزام بمبادئ MVVM؟ كيف يمكن أن يتم إخطار ViewModel بأنه يحتاج إلى تحميل أو تفريغ الفرعية؟ وذلك عندما تم توسيع عقدة أو انهيار دون معرفة أي شيء عن وجود Treeview؟

شيء ما يجعلني أشعر أن المحاكاة الافتراضية للبيانات لا تسير على ما يرام مع MVVM. نظرًا لأن المحاكاة الافتراضية للبيانات ، يحتاج ViewModel عمومًا إلى معرفة الكثير عن الحالة الحالية لواجهة المستخدم و Aslo للتحكم في الكثير من الجوانب في واجهة المستخدم. خذ مثالًا آخر:

قائمة عرض مع البيانات الافتراضية للبيانات. ستحتاج ViewModel إلى التحكم في طول ScrolLthumb الخاص بـ ListView لأنه يعتمد على عدد العناصر الموجودة في النموذج. أيضًا عندما يقوم المستخدم بالتمرير ، سيحتاج ViewModel إلى معرفة الموقف الذي تم تمريره إليه ومدى حجم ListView (عدد العناصر التي تناسب حاليًا) لتكون قادرة على تحميل الجزء المناسب من بيانات النموذج من المستودع.

هل كانت مفيدة؟

المحلول

الطريقة السهلة لحل ذلك هي تطبيق "مجموعة الافتراضية" التي تحافظ على مراجع ضعيفة إلى عناصرها جنبًا إلى جنب مع خوارزمية لجلب / إنشاء عناصر. رمز هذه المجموعة معقدًا إلى حد ما ، مع جميع الواجهات المطلوبة وهياكل البيانات لتتبع نطاقات البيانات المحملة بكفاءة ولكن هنا واجهة برمجة تطبيقات جزئية لفئة تم تصنيفها على أساس الفهارس:

public class VirtualizingCollection<T>
  : IList<T>, ICollection<T>, IEnumerable<T>,
    IList, ICollection, IEnumerable,
    INotifyPropertyChanged, INotifyCollectionChanged
{
  protected abstract void FetchItems(int requestedIndex, int gapStartIndex, int gapEndIndex);
  protected void RecordFetchedItems(int startIndex, int count, IEnumerable items) ...
  protected void RecordInsertOrDelete(int startIndex, int countPlusOrMinus) ...
  protected virtual void OnCollectionChanged(CollectionChangedEventArgs e) ...
  protected virtual void Cleanup();
}

بنية البيانات الداخلية هنا هي شجرة متوازنة من نطاقات البيانات ، مع كل نطاق بيانات يحتوي على فهرس البدء ومجموعة من المراجع الضعيفة.

تم تصميم هذه الفئة ليتم فئة فرعية لتوفير المنطق لتحميل البيانات فعليًا. إليكم كيف يعمل:

  • في مُنشئ الفئة الفرعية ، RecordInsertOrDelete يتم استدعاؤه لتعيين حجم المجموعة الأولي
  • عندما يتم الوصول إلى عنصر باستخدام IList/ICollection/IEnumerable, ، يتم استخدام الشجرة للعثور على عنصر البيانات. إذا وجدت في الشجرة وهناك مرجع ضعيف وما زال المرجع الضعيف يشير إلى كائن حياة ، يتم إرجاع هذا الكائن ، وإلا يتم تحميله وإعادته.
  • عندما يحتاج عنصر ما إلى تحميل نطاق ، يتم حساب نطاق الفهرس عن طريق البحث إلى الأمام والعودة على الرغم من أن بنية البيانات للعنصر التالي/السابق الذي تم تحميله بالفعل ، ثم الملخص FetchItems يسمى بحيث يمكن للفئة الفرعية تحميل العناصر.
  • في الفئة الفرعية FetchItems التنفيذ ، يتم جلب العناصر ثم RecordFetchedItems يتم استدعاؤه لتحديث شجرة النطاقات مع العناصر الجديدة. مطلوب بعض التعقيد هنا لدمج العقد المجاورة لمنع الكثير من نمو الأشجار.
  • عندما تحصل الفئة الفرعية على إشعار بتغييرات البيانات الخارجية ، يمكنها الاتصال RecordInsertOrDelete لتحديث تتبع الفهرس. تبدأ هذه التحديثات فهارس. بالنسبة لإدراج ما ، قد يؤدي ذلك أيضًا إلى تقسيم نطاق ، وللحذف قد يتطلب ذلك إعادة إنشاء واحدة أو أكثر من النطاقات أصغر. يتم استخدام هذه الخوارزمية نفسها داخليًا عند إضافة العناصر / حذفها من خلال IList و IList<T> واجهات.
  • ال Cleanup تسمى الطريقة في الخلفية للبحث بشكل متزايد عن شجرة النطاقات WeakReferences والنطاقات الكاملة التي يمكن التخلص منها ، وكذلك للنطاقات المتفجرة للغاية (على سبيل المثال واحد فقط WeakReference في نطاق مع 1000 فتحة)

لاحظ أن FetchItems يتم تمرير مجموعة من العناصر التي تم تفريغها حتى يتمكن من استخدام مجريات الأمور لتحميل عناصر متعددة في وقت واحد. هناك مثل هذا الاستدلال البسيط هو تحميل 100 عنصر التالي أو حتى نهاية الفجوة الحالية ، أيهما يأتي أولاً.

مع VirtualizingCollection, ، سيؤدي المحاكاة الافتراضية المدمجة في WPF إلى تحميل البيانات في الأوقات المناسبة ListBox, ComboBox, ، إلخ ، طالما أنك تستخدم EG. VirtualizingStackPanel بدلاً من StackPanel.

ل TreeView, ، خطوة أخرى مطلوبة: في HierarchicalDataTemplate تعيين أ MultiBinding ل ItemsSource هذا يرتبط بحقك ItemsSource وكذلك ل IsExpanded على الوالد الملموس. المحول ل MultiBinding إرجاع قيمته الأولى ( ItemsSource) إذا كانت القيمة الثانية ( IsExpanded القيمة) صحيحة ، وإلا فإنه يعود فارغة. ما يفعل TreeView يتم إسقاط جميع الإشارات إلى محتويات التجميع على الفور بحيث VirtualizingCollection يمكن تنظيفها.

لاحظ أنه لا يلزم القيام بالمحاكاة الافتراضية على أساس الفهارس. في سيناريو شجرة ، يمكن أن يكون كل شيء أو لا شيء ، وفي سيناريو القائمة يمكن استخدام عدد تقديري وملء النطاقات حسب الضرورة باستخدام آلية "بدء" / "مفتاح النهاية". يكون هذا مفيدًا عندما تتغير البيانات الأساسية ويجب أن يتتبع العرض الظاهري موقعه الحالي بناءً على المفتاح الموجود في الجزء العلوي من الشاشة.

نصائح أخرى

    <TreeView
        VirtualizingStackPanel.IsVirtualizing = "True"
        VirtualizingStackPanel.VirtualizationMode = "Recycling" 
VirtualizingStackPanel.CleanUpVirtualizedItem="TreeView_CleanUpVirtualizedItem">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
        </TreeView>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top