ICollectionView – למתקדמים WPF

28 בJune 2013

תגיות: , ,
אין תגובות
מומלץ בחום לעשות היכרות עם הקלאס CollectionView על הנגזרות השונות. ICollectionView , CollectionView, CollectionViewSource.
 
מה הרעיון? יש לנו מודל, יש לנו נתונים , יש לנו גם UI שמחצין את הנתונים, אנחנו רוצים לצרכי UI בלבד לבצע איטרציות שונות לצרכי סינון, חיפוש, וכו’ אבל בלי משמעות אמיתית על המודל,
                 כמו גם בלי הכבדה על ביצועים וזיכרון.
 
נניח שאני מנסה לבצע סינון כזה בדוגמא של איזשהו ListView שמציג רשימת מוצרים שבהתאם לבחירת המשתמש הרשימה הולכת וקטנה, לדוגמא:
סנן מוצרים שמחירם גדול מ: X וכן סנן מוצרים ששמם מתחיל ב: Y וגם סנן מוצרים בקטגוריה :Z . עכשיו אם נצטרך לממש דבר כזה היינו צריכים להגדיר אובייקט מסוג רשימה.
ובכל פעם לבצע foreach לנקות את הרשימה ולסנן מחדש, ומאחר שבכל פעם אנחנו מבצעים סינון על רשימה מסוננת(X , Y ,Z ) מן הסתם היו לנו שני אובייקטים בזיכרון. נניח שקוד כזה ייראה בערך כך:
   1: private void FilterCombo(List<string> source, string searhcingValue)

   2:         {

   3:             List<string> list = new List<string>(source);

   4:  

   5:             foreach (string item in source)

   6:             {

   7:                 if (item.Contains(searhcingValue))

   8:                 {

   9:                     list.Add(item);

  10:                 }

  11:             }

  12:             source = list;

  13:         }

  14:  

אוקיי זה אולי נחמד אבל הבעיה היא בכמויות עצומות של Data. ממספרים של 5000 אלמנטים מסוג רפרנס והלאה, תרגישו את המעבד שלכם נטחן (לאבק) ואת האפליקציה נתקעת.
נכון יכולנו לחשוב על BackgroundWorker אבל שימו לב שאנו בצד הUI והמשתמש מצפה לראות תוצאות ולא שזו משימה צדדית. על הדרך הייתי ממליץ לנסות את הקוד הבא בXaml שלכם (עם או בלי קשר לפוסט הנוכחי).
   1: <ComboBox.ItemsPanel>

   2:                                                               

   3:      <ItemsPanelTemplate>

   4:                                                                  

   5:           <VirtualizingStackPanel />

   6:                                                              

   7:       </ItemsPanelTemplate>

   8:  

   9: </ComboBox.ItemsPanel>   

  10:                  

מדובר בפורפטי ייחודי של ItemsControl שאם הפקד טוען מסה של נתונים ולא נרצה שהThreadUI ייתקע, תתבצע וירטואליזציה של הנתונים והם ימשיכו להיטען בזמן הפתיחה בלי לדפוק את הUI.
 
נחזור לנושא, יש לי Items control עם חיבור לData בוא נראה אך אני מפלטר נתונים ברמת UI נניח כדי להציג Autocompletetion על שדה החיפוש שזה אומר:
נניח יש לנו שלשה נתונים ברשימה כשנלחץ על מקש A יופיעו כולם, כשנלחץ גם על 1, הרשימה תצומצם ל2 נתונים וכן הלאה. נתבונן בקוד הבא:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.ComponentModel;

   4: using System.Linq;

   5: using System.Text;

   6: using System.Threading.Tasks;

   7: using System.Windows.Data;

   8:  

   9: namespace WpfApplication1

  10: {

  11:     public class MainViewModel : INotifyPropertyChanged

  12:     {

  13:         public MainViewModel()

  14:         {

  15:             init();

  16:         }

  17:  

  18:         public List<string> Data { get; set; }

  19:  

  20:         protected void init()

  21:         {

  22:             Data = new List<string>();

  23:             for (int i = 0; i < 10; i++)

  24:             {

  25:                 if (i <= 5)

  26:                     Data.Add("A" + i.ToString());

  27:                 else

  28:                     Data.Add("B" + i.ToString());

  29:             }

  30:  

  31:             MyCollectionView = CollectionViewSource.GetDefaultView(Data);

  32:             MyCollectionView.Filter = MyFilter;

  33:         }

  34:  

  35:  

  36:         public bool MyFilter(object value)

  37:         {

  38:             string val = value as string;

  39:             if (UserText != null)

  40:             {

  41:                 return val.Contains(UserText);

  42:             }

  43:             return true;

  44:         }

  45:         private ICollectionView _myCollectionView;

  46:         public ICollectionView MyCollectionView

  47:         {

  48:             get

  49:             { return _myCollectionView; }

  50:  

  51:             set

  52:             {

  53:                 _myCollectionView = value;

  54:                 RaisePropertyChanged("MyCollectionView");

  55:             }

  56:         }

  57:  

  58:         private string _userText;

  59:         public string UserText

  60:         {

  61:             get { return _userText; }

  62:  

  63:             set

  64:             {

  65:                 _userText = value;

  66:                 RaisePropertyChanged("UserText");

  67:                 MyCollectionView.Refresh();

  68:             }

  69:         }

  70:  

  71:  

  72:         public void RaisePropertyChanged(string propName)

  73:         {

  74:             if (PropertyChanged != null)

  75:             {

  76:                 PropertyChanged(this, new PropertyChangedEventArgs(propName));

  77:             }

  78:         }

  79:         public event PropertyChangedEventHandler PropertyChanged;

  80:     } 

  81: }

  82:  

 
 
CollectionView נותן לי המון יכולות והכיף הגדול זה הנוחות שבשימוש. מה שחשוב בדוגמא זה לשים לב לפרופרטי UserText שבעת שינוי שלו אני מרפרש את הרשימה.
מה שעוד רלוונטי זאת כמובן הפונקציה MyFilter שמסננת בכל שינוי בלי העתקות ורפרנסים מיותרים. קוד הxaml שמריץ את הviewModel הזה נראה כך:
 
   1: <Window x:Class="WpfApplication1.MainWindow"

   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   4:         Title="MainWindow" Height="350" Width="525">

   5:     <Grid>

   6:         <StackPanel VerticalAlignment="Top" >

   7:             <ComboBox ItemsSource="{Binding MyCollectionView}" IsTextSearchEnabled="True" IsEditable="True" 

   8:                       Text="{Binding UserText}" PreviewKeyUp="ComboBox_PreviewKeyUp_1" >

   9:                 </ComboBox>

  10:  

  11:         </StackPanel>

  12:     </Grid>

  13:                                                                       </Window>

ואם ממש חסר לנו אז הCodeBehind נראה כך:
   1: public partial class MainWindow : Window

   2:     {

   3:         public MainWindow()

   4:         {

   5:             InitializeComponent();

   6:             DataContext = new MainViewModel();

   7:         }

   8:  

   9:         private void ComboBox_PreviewKeyUp_1(object sender, KeyEventArgs e)

  10:         {

  11:             ComboBox comb = sender as ComboBox;

  12:             comb.IsDropDownOpen = true;

  13:         }

  14:                                                                            } 

  15:  

הוסף תגובה
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *