תמונה ראשית עבור 'שלוש דרכים לממש NotifyPropertyChanged' שלוש דרכים לממש NotifyPropertyChanged

כידוע, WPF מממשת Observer על מנת ליידע את ה- UI שהמידע שקשור (Bound) אליו משתנה. ה- Interface שמממש את ה- Observer הזה נקרא INotifyPropertyChanged, והמימוש שלו נתון לשיקול המפתח.


private int _age;public int Age{get { return _age; }set{_age = value;// NotifyPropertyChanged....}}

ה- XAML יראה כך:

[xml][/xml]

גם אם אתם כבר מכירים את 2 המימושים הבסיסיים, האחרון יכול לחדש לכם, ולכן לקרוא אותו.

Magic String

נתחיל מהמימוש הכי בסיסי, שהוא גם המימוש הסטנדרטי - לפחות אם תשאלו את מייקרוסופט - ונקרא Magic String. כשמו - הפונקציה שבו מקבלת את שם ה- Property על ידי string.

public void NotifyPropertyChanged(string info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } }

 

השימוש ייראה כך:

public int Age { get { return _age; } set  {  _age = value; NotifyPropertyChanged("Age"); }}

כמו שניתן לראות, ה- Event שנקרא PropertyChanged (ונמצא במקור ב- Interface), מקבל EventArgs שנקרא PropertyChangedEventArgs ומצפה לקבל כפרמטר string את שם ה- Property שהשתנה. במימוש בסיסי זה, אנו פשוט מעבירים את שם ה- Property ל- Event. העובדה שמדובר ב- string יכולה לייצר מספר בעיות, כשהגדולה מבינהן היא טעות בשם ה- Property: שגיאת דקדוק, שגיאת כתיב או אפילו בלבול בין אות קטנה לגדולה - כל טעות קטנה תביא למצב שבו ה- UI לא יידע שהמידע השתנה, ולא יתעדכן בהתאם. לכן, שתי הדרכים הבאות חוסכות את השימוש ב- string.

אם זאת, זו היא השיטה הכי יעילה, כיוון ששאר השיטות מוצאות את שם ה- Property בזמן ריצה. אבל כמובן שבמקרים מסויימים עדיף לשחרר קצת יעילות לטובת קריאות קוד והמנעות מטעויות שיאריכו את זמן הפיתוח.

Expression

בעזרת המחלקה Expression, ניתן להמיר את ה- string מהשיטה הקודמת בביטוי למבדה שמחזיר את הנתיב אל ה- Property:

protected void NotifyPropertyChanged(Expression property) { var lambda = (LambdaExpression)property; MemberExpression memberExpression; if (lambda.Body is UnaryExpression) { var unaryExpression = (UnaryExpression)lambda.Body; memberExpression = (MemberExpression)unaryExpression.Operand; } else { memberExpression = (MemberExpression)lambda.Body; }if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name)); } }

החלק הראשון בפונקציה מחלץ את שם ה- Property מביטוי הלמבדה, והחלק השני מרים את ה- Event.

השימוש ייראה כך:

public int Age { get { return _age; } set  {  _age = value; NotifyPropertyChanged(() => Age); } }

בצורה זו, הסיכוי שנטעה בשם של ה- Property יורד משמעותית, כיוון שכל טעות קטנה תגרום לקומפיילר לזרוק שגיאה ששם ה- Property לא נכון. הטעות היחידה שיכולה להווצר היא אם נרשום בטעות שם של Property אחר שקיים גם הוא במחלקה שלנו. ובכל זאת, לפעמים השיטה הזו ארוכה מידי; היא חיונית לחלוטין רק במידה ואנו רוצים להודיע על שינוי ב- Property מחוץ ל- Setter של ה- Property. במקרה שלנו, הדרך השלישית תיהיה הכי קצרה והכי נטולת טעויות.

שימוש ב- CallerMemberName

הדרך הזו היא בוודאות הדרך האהובה עלי. על ידי שימוש ב- Attribute על פרמטר בפונקציה, נוכל לקבל את שם ה- Property בצורה אוטומטית, כך שכל מה שנצטרך לעשות כשנרצה להודיע על שינוי ב- Property מתוך ה- Setter שלו, זה רק לקרוא לפונקציה ללא פרמטרים!

השיטה הזו נטולת סיכוי לטעויות והכי קלה לתחזוקה, כיוון שאפילו אם תשנו את ה- Property, הפונקציה עדיין תקבל את השם המעודכן שלו.

public void NotifyPropertyChanged([CallerMemberName] string info = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } }

 

והשימוש:

public int Age { get { return _age; } set  {  _age = value; NotifyPropertyChanged(); } }

ה- Attribute שנקרא CallerMemberName מייבא את השם של הפונקציה ש- NotifyPropertyChanged נקרא מתוכה (ה- Setter של ה- Property במקרה שלנו), ומכניס אותו לתוך המשתנה info. לאחר מכן, הפונקציה מרימה את ה- Event עם שם ה- Property שנמצא במשתנה info.

שימו לב שהמימוש הזה מכיל בתוכו גם את המימוש הראשון, כיוון שאם תקראו לפונקציה עם ערך למשתנה info, ה- Attribute לא יעבוד וה- Event יורם עם הנתון שהכנסתם.

תגובות

האחרון מעולה!!! מדהים
דשגדש הגב
תודה
דן הגב
יש לזה שימוש בעוד טכנולוגיות חוץ מC#?
Agrigator הגב

הוסף תגובה