דילוג לתוכן
  • דף הבית
  • קטגוריות
  • פוסטים אחרונים
  • משתמשים
  • חיפוש
  • חוקי הפורום
כיווץ
תחומים

תחומים - פורום חרדי מקצועי

💡 רוצה לזכור קריאת שמע בזמן? לחץ כאן!
  1. דף הבית
  2. תכנות
  3. האצת TreeView ב-WPF על ידי וירטואליזציה עצמית

האצת TreeView ב-WPF על ידי וירטואליזציה עצמית

מתוזמן נעוץ נעול הועבר תכנות
2 פוסטים 2 כותבים 116 צפיות
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • pcinfogmachP מנותק
    pcinfogmachP מנותק
    pcinfogmach
    כתב נערך לאחרונה על ידי pcinfogmach
    #1

    אם יצא לכם לעבוד עם TreeView ב-WPF ולהציג בו הרבה נתונים, אולי נתקלתם בהאטה בביצועים — במיוחד כשיש הרבה Nodes טעונים מראש.

    בפקדים מבוססי רשימות (כמו ListBox), ניתן לפתור זאת על ידי שימוש ב-Virtualization, שמונעת טעינה של פריטים שלא מוצגים כרגע. אך TreeView, בשל המבנה ההיררכי שלו, לא תומך ב-Virtualization בצורה יעילה.

    לכן, יצרתי פתרון פשוט שמדמה את ההתנהגות הזו על ידי טעינה עצלנית (Lazy Loading) של התוכן. זה לא רעיון חדש, אבל שווה להזכיר למי שלא מכיר או שכח שיש דרך לשפר את הביצועים בצורה ניכרת.

    איך זה עובד בפועל:
    כשפריט נטען לראשונה (Loaded), מוצג רק placeholder כדי להימנע מטעינה מיותרת.
    רק בעת פתיחה (Expanded), נטענים בפועל הנתונים מה-ViewModel.
    בעת סגירה (Collapsed), הנתונים מתחלפים שוב ב-placeholder — שחרור פשוט של משאבים.

    שימו לב! במקרה זה אל תשתמשו ב-itemsource עבור HierarchicalDataTemplate ב- Xaml

    להלן קוד דוגמא שמיישם את ההתנהגות הזו:
    אשמח לקבל הצעות לשיפור:
    כמו"כ אשמח לשמוע רעיונות איך להפוך את זה למשהו יותר כללי (כרגע הוא מגבל לסוג datacontext מסויים מאוד.

    using System.Windows.Controls;
    using System.Windows;
    using Otzarnik.FsViewer;
    using System.Collections.Generic;
    
    namespace Oztarnik.Helpers
    {
        public static class TreeItemVirtualizationBehavior
        {
            public static bool GetEnableVirtualization(DependencyObject obj) =>
                (bool)obj.GetValue(EnableVirtualizationProperty);
    
            public static void SetEnableVirtualization(DependencyObject obj, bool value) =>
                obj.SetValue(EnableVirtualizationProperty, value);
    
            public static readonly DependencyProperty EnableVirtualizationProperty =
                DependencyProperty.RegisterAttached("EnableVirtualization", typeof(bool), typeof(TreeItemVirtualizationBehavior),
                    new PropertyMetadata(false, OnEnableVirtualizationChanged));
    
            private static void OnEnableVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if (d is TreeViewItem item && e.NewValue is bool enabled)
                {
                    if (enabled)
                    {
                        item.Expanded += OnItemExpanded;
                        item.Collapsed += OnItemCollapsed;
                        item.Loaded += Item_Loaded;
                    }
                    else
                    {
                        item.Expanded -= OnItemExpanded;
                        item.Collapsed -= OnItemCollapsed;
    item.Loaded -= Item_Loaded;
                    }
                }
            }
    
            private static void Item_Loaded(object sender, RoutedEventArgs e)
            {
                if (sender is TreeViewItem treeViewItem &&
                    treeViewItem.DataContext is TreeItem treeItem &&
                    treeItem.Items?.Count > 0)
                        treeViewItem.ItemsSource = new List<string> { "" };
            }
    
            private static void OnItemExpanded(object sender, RoutedEventArgs e)
            {
                if (sender is TreeViewItem treeViewItem &&
                     treeViewItem.DataContext is TreeItem treeItem &&
                     treeItem.Items?.Count > 0)
                        treeViewItem.ItemsSource = treeItem.Items;
            }
    
            private static void OnItemCollapsed(object sender, RoutedEventArgs e)
            {
                if (sender is TreeViewItem treeViewItem &&
                     treeViewItem.DataContext is TreeItem treeItem && 
                     treeItem.Items?.Count > 0)
                        treeViewItem.ItemsSource = new List<string> { ""};
            }
        }
    
    }
    
    

    גמ"ח מידע מחשבים ואופיס

    dovidD תגובה 1 תגובה אחרונה
    1
    • dovidD מנותק
      dovidD מנותק
      dovid ניהול
      השיב לpcinfogmach נערך לאחרונה על ידי dovid
      #2

      @pcinfogmach כתב בהאצת TreeView ב-WPF על ידי וירטואליזציה עצמית:

      כמו"כ אשמח לשמוע רעיונות איך להפוך את זה למשהו יותר כללי (כרגע הוא מגבל לסוג datacontext מסויים מאוד.

      public static class TreeItemVirtualizationBehavior
      {
          public static bool GetEnableVirtualization(DependencyObject obj) =>
              (bool)obj.GetValue(EnableVirtualizationProperty);
      
          public static void SetEnableVirtualization(DependencyObject obj, bool value) =>
              obj.SetValue(EnableVirtualizationProperty, value);
      
          public static readonly DependencyProperty EnableVirtualizationProperty =
              DependencyProperty.RegisterAttached("EnableVirtualization", typeof(bool), typeof(TreeItemVirtualizationBehavior),
                  new PropertyMetadata(false, OnEnableVirtualizationChanged));
      
          private static void OnEnableVirtualizationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              var item = (TreeViewItem)d;
              if ((bool)e.NewValue)
              {
                  item.Expanded += setSource;
                  item.Collapsed += setSource;
                  item.Loaded += setSource;
              }
              else
              {
                  item.Expanded -= setSource;
                  item.Collapsed -= setSource;
                  item.Loaded -= setSource;
              }
          }
      
          public static Func<object, bool> needPlaceHolders = (s) => false;
      
          private static void setSource(object sender, RoutedEventArgs e)
          {
              var treeViewItem = (TreeViewItem)sender;
              if (needPlaceHolders(treeViewItem.DataContext))
                  treeViewItem.ItemsSource = Enumerable.Repeat("", 1);
          }
      }
      

      צריך להפוך לספציפי בטעינת האפליקציה על ידי השמה של needPlaceHolders:

      TreeItemVirtualizationBehavior.needPlaceHolders = (dataContext) => 
                 dataContext is TreeItem treeItem && treeItem.Items?.Any() == true;
      

      יש מצב שלא הבנתי מה שהקוד צריך לעשות ואם ככה שטעיתי גם בהצעה.

      מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

      בכל נושא אפשר ליצור קשר dovid@tchumim.com

      תגובה 1 תגובה אחרונה
      2

      בא תתחבר לדף היומי!
      • התחברות

      • אין לך חשבון עדיין? הרשמה

      • התחברו או הירשמו כדי לחפש.
      • פוסט ראשון
        פוסט אחרון
      0
      • דף הבית
      • קטגוריות
      • פוסטים אחרונים
      • משתמשים
      • חיפוש
      • חוקי הפורום