DCSIMG
אלעד כץ | Elad Katz
Sign in | Join | Help

אלעד כץ | Elad Katz

לגו של גדולים

האקתון פיתוח לחלונות 8 ב הטמל5 - הכנה לגרסת ה RC של חלונות 8

פורסם בתאריך May 17 2012, 06:10 PM על ידי eladkatz

במידה ועדיין לא נרשמתם – בחמישי הבא יתקיים מרתון קוד ראשון לחלונות 8, שבוע לפני היציאה של ה Release Preview של חלונות. הפיתוח יהיה ב HTML5, ויהיה עם מנטורים שיעזרו לכל מי שלא שולט בחלונות 8 עדיין (כן, ידע חזק בתכנות צד לקוח ב HTML5 בהחלט מספיק – אם את/ה כריש JavaScript זה המקום בשבילך)

אני אישית אהיה שם כמנטור – אני מפתח כבר כמה חודשים טובים על הפלטפורמה, וקשה לי להפריז בכמה ש winJS היא ספרייה מעולה, אפילו כהשכלה כללית למפתחי Front End טובים. (אין ספרייה יותר טובה ממנה ל Single Page Applications שאני מכיר).

אני מאוד ממליץ לכל מי שמגיע שיתקין מראש את חלונות 8 בטא על VHD (זו הדרך הטובה ביותר לפיתוח כרגע, פיתוח ב VM לא נוח במיוחד)

הרשמו מהר לפני שיגמרו המקומות!

בתחרות שתתקיים בסוף המרתון (ביום השני) יחולקו גם פרסים, כשהפרס הראשון הוא אופני מרידה מטס 40V (גילוי נאות: הח”מ בעלים גאים של מרידה 120 יפהפיים Smile )

jQuery Mobile - עכשיו עם התאמה ל Windows Phone 7.5

פורסם בתאריך May 17 2012, 05:15 PM על ידי eladkatz

 

jQuery Mobile מותאם מעכשיו גם ל WP7.5, מה שאומר שפיתוח בHTML5 יכול להראות כאפליקציית נייטיב (או לפחות דומה לה) מעכשיו גם ל Windows Phone.
ממשחק מהיר במראה הזה זה אכן נראה מאוד דומה, אם כי התמיכה המעניינת שאנו מחכים לה היא תמיכה במראה מטרו של חלונות 8. על פי קצב העבודה של jQuery Mobile (שהוא פרוייקט קוד פתוח) אני מניח שכבר בחודשים הקרובים נראה גרסאות שהן לא תלויות פלטפורמה שמאפשרות עבודה עם הקונטרולים של חלונות 8 ( WinJS תלויה בהרבה מאוד מקומות בקוד ספיציפי של חלונות 8 לצערי).
ככל שאני עובד יותר עם WinJS אני מגלה כמה עבודה טובה נעשתה שם בכל מה שקשור לעולם ה “אפליקציות-דף-אחד” (Single Page Applications). באפילקציות מהסוג הזה יש צורך בכמה ספריות שיעבדו בצורה אינטגרטיבית ביחד: ספריית קונטרולים, ספריית MVC/ MVVM, וספריית ניווט. במידה והספריות לא עובדות בצורה אינטגרטיבית ביחד אז זו עבודה מאוד קשה רק לגרום לתשתית המינימאלית לעבוד (לדוגמא, לתאם בין jQuery UI, KnockoutJS, SammyJS).

WinJS מבצע את כולם ביחד, ומדהים כמה שזה מפשט פיתוח ל SPA. (יחד עם זאת, WinJS נופל ביכולתיו מKnockoutJS, ולמיטב ידיעתי גם בגרסה הסופית זה לא ישתנה, שזה ממש חבל, במיוחד בהתחשב בזה שיוצר KnockoutJS- סטיבן סנדרסון – הוא עובד מיקרוסופט…)

בשביל גרסאות המותאמות לכל הפלטפורמות של WinJS כנראה נצטרך לחכות עוד כמה חודשים. (ואולי התשועה תבוא בכלל מ asp.net mvc4?)

 

mango-phone

ויז'ואל סטודיו 11 מגיע לגרסת RC ומביא איתו המון שיפורים ב UI. והוא מהיר. ממש מהיר.

פורסם בתאריך May 13 2012, 10:55 AM על ידי eladkatz

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

ויז’ואל סטודיו 11 ממשיך להתקדם – מוקדם יותר פורסם בבלוג של ויז’ואל סטודיו על עדכונים לUI ועל הרבה שיפורים שמיקרוסופט הכניסו כתגובה לפידבק מאיתנו, ככל שמתקרבים לגרסת ה RC (צפוי לצאת עוד כשלושה שבועות).

ככה זה היה נראה אז:

Visual Studio 11 Beta

מבחינתי, אחרי כמה חודשים טובים של עבודה בגרסת הבטא, הבעיות היו כדלהלן:
1. האייקונים לא מספיק קריאים. צבע עוזר לבידול מהיר בין אלמנטים, וללא צבע הרבה יותר קשה לזהות מהר אייקונים.
2. ביותר מדי מקומות, הכותראות של הטאבים היו באותיות גדולות בלבד. ללא שום סיבה שאני מבין.
2. הקונטרסט היה נמוך מדי, כאילו שכל המסך באותו צבע. הרעיון מאחורי זה הוא שנוכל להתמקד בתוכן ולא בכלי, אבל נראה היה שהביצוע פשוט לא טוב. קשה להפריז בחשיבות של זה, היות וויז’ואל סטודיו הוא די ספינת הדגל של מיקרוסופט. כשכל כך הרבה משתנה בשפה הגראפית של חלונות, וכשמעצבים ומפתחים רוצים להבין איך לעבוד עם ההנחיות UI/UX החדשות, מיקרוסופט לא יכולה לפשל עם ויז’ואל סטודיו.
דוגמא מאוד טובה לאיך *כן* עושים את זה נכון, אפשר לראות בפוטושופ CS 6 – ספינת הדגל של אדובי:

image

ברמה הכי בסיסית, הכלי הזה הוא פשוט תאוה לעיניים. כיף להסתכל עליו.
גם כאן אפשר לראות שכמעט אין צבע בממשק, על מנת לאפשר להתרכז בתוכן ולא בכלי (שזה חשוב אף יותר בכלי כמו פוטושופ) אבל זה לא בא לרגע על חשבון השימושיות. אדרבה, כל מי שכבר יצא לו לגעת בCS6 יודע כמה שהכלי הזה נהיה אפילו יותר נוח. הדבר שלי אישית הכי בולט לעין זה הקונטרסט – החדות של הגרפיקה נראית הרבה יותר טוב בCS6 לעומת VS11.

לא רק לי זה הפריע. הרבה מאוד פידבק הגיע למיקרוסופט על כך בחודשים האחרונים.

 


 

אז מסתבר שהם הקשיבו.

ככה נראה הבטא של של VS11:

Visual Studio 11 Beta

וככה נראית גרסת ה Release Candidate:

 

Visual Studio 11 RC

אז מה יש לנו כאן?

1. שיפור מאוד משמעותי בקונטרסט. *הכל* פתאום נראה יותר חד וברור.

 

Visual Studio 11

2. הוספה מינימליסטית של צבעים לאייקונים תוך שמירה על שפת מטרו. האיקונים הרבה יותר קלים לזיהוי עכשיו:

Visual Studio 11

3. הוספת צבע לסטטוס בר
ויז’ואל סטודיו מספק לנו מעכשיו מידע על מצב העבודה בצורה מאוד נעימה ע”י הצבע של הסטטוס בר. פיצ’ר מאוד כיפי לטעמי:

Visual Studio 11

Visual Studio 11

4. הוספת צבע לחלקים חשובים ב UI על מנת לגרום להם לבלוט
שינוי קטן. מבחינתי הבדל גדול בפרודקטיביות:

 

Visual Studio 11

Visual Studio 11

4. כותרות של טאבים ללא אותיות גדולות

Visual Studio 11

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

Visual Studio 11

5. כרום החלון מקוסטם
ע”י ציור הכרום של החלון בצורה מיוחדת, גם חוסכים מעט מקום, וגם תורמים עוד יותר למראה המטרואי. גרום לכל הסיפור להיראות הרבה יותר הייטקי. מוסיף אבל עדיין מאוד עדין. (מאוד בדומה לפוטושופ CS6)

Visual Studio 11

בהשוואה לגרסאות קודמות, אפשר לראות כמה יש יותר מקום עבוד התוכן, וכמה המראה של VS11 הופך לנקי יותר:

Visual Studio 11

6. הוספת צבע להרבה אלמנטים על מנת להגדיל את הקריאות
כל האיקונים החשובים מקבלים צבע:

 

Visual Studio 11

אחד המקומות החשובים ביותר ב VS – ה Solution Explorer מקבל גם הוא צבע על מנת לעזור בזיהוי המהיר של הקבצים השונים:

Visual Studio 11 Solution Explorer

כזכור, ל Solution Explorer התוספה יכולת מאוד מרשימה של לראות את המחלקות כבר מהטאב הזה, כשאפשר לחפש בין כל הקבצים, מחלקות, מתודות ומאפיינים כבר מחלון ה Solution Explorer, כך שאם זה היה חלון שימושי עד עכשיו – מעכשיו זה נכון כפליים.

גם לאינטילנס התוסף צבע מאותה סיבה:

Visual Studio 11

8. הפיכת האייקונים לברורים יותר, ללא טשטוש
על מנת שהאייקונים יראו בצורה ברור גם על רקע בהיר וגם על רקע כהה, יש מסביבם קו מתאר לבן. בגרסה הקודמת הצבעים לא היו בקונטרסט מספיק גבוה ולפיכך זה יצר מראה מטושטש. גם כאן, הועלתה רמת הקונטרסט, והתוצאה הרבה יותר טובה:

 

Visual Studio 11

9. והכי חשוב, לסיום, *מהירות*

זה מהיר. ממש מהיר. VS11 עובד הכי מהר שאי פעם ראיתי את ויז’ואל סטודיו עובד, וזה מורגש מאוד. מהפתיחה עד לעבודה הסטנדרטית בכלי, הכל פשוט מגיב הרבה יותר מהיר וטוב. פשוט תענוג.. ויז’ואל סטודיו כפי שהיה צריך להיות כבר מזמן!

בהחלט יש למה לחכות.

הקלטות היום הפתוח במיקרוסופט על פיתוח לחלונות 8 ב HTML5

פורסם בתאריך May 03 2012, 01:48 AM על ידי eladkatz

בתאריך 19.3.2012 הרצאתי במיקרוסופט ישראל על פיתוח אפליקציות מטרו לסביבת Windows 8  -כיצד לפתח אפליקציות מטרו באמצעות שימוש בכלים שכל מפתח ווב מכיר: HTML5 ו- JavaScript.

Windows 8 עם ממשק המטרו המהפכני אשר מותאם למכשירים שונים, מציבה אתגרים חדשים ומלהיבים לחברות תוכנה ומפתחים. מטרת יום העיון היתה להציג את העקרונות הבסיסים והחשובים ביותר בבואנו לפתח אפליקציות מטרו וללמד מפתחים כיצד להשתמש בכלים ובטכנולוגיות מוכרות  על מנת להכנס לעולם חדש ומופלא של פיתוח אפליקציות Windows 8 שגם מאפשרת הזדמנות עסקית מצויינת לכל אחד ואחת ממכם: להפיץ ולמכור בקלות את האפליקציה שפיתחתם באמצעות ה- Windows 8 Store. אז בהצלחה!..

ביום עיון זה, למדנו על עקרונות הפיתוח ל- Windows 8 ב- HTML5, ראינו איך ניתן לקחת את הידע והניסיון מעולם ה- Web לעולם ה- Desktop והבנו את ההבדלים בין העולמות. כמו כן הכרנו את WinJS – ספריית ה- JavaScript של מיקרוסופט המכילה פקדים ורכיבים המותאמים לסוג החדש של האפליקציות, ואת WinRT – שכבת ה- API החדשה לגישה ליכולות של Windows מקוד JavaScript. 

 

חלק ראשון - מבוא לתכנות בחלונות 8

רוצים להוריד את הסרטון לממחשב, למובייל או לטאבלט? כאן תמצאו לינקים להורדה במגוון פורמטים.

במהלך ההרצאה סקרנו את מבנה אפליקציות HTML5 ו- JavaScript ואת האלמנטים הבסיסיים לפיתוח אפליקציות מטרו ל- Windows 8. בנוסף, הכרנו את WinJS ו WinRT – הספריות המשמעותיות ביותר עבור מפתחי HTML5 לחלונות 8 ולמדנו על הדרך בה הן משולבות באפליקציית JavaScript.

 

חלק שני - WinJS ו WinRT לעומק

 

רוצים להוריד את הסרטון לממחשב, למובייל או לטאבלט? כאן תמצאו לינקים להורדה במגוון פורמטים.

כאן צללנו לעומק של חלקים נרחבים בתוך WinJS ו- WinRT – למדנו איך לעבוד עם פקדים, DataBinding ו- Templates, וראינו איך מממשים contracts של חלונות 8 כמו חיפוש ו Share.

ספר חינמי של Apress - פיתוח לחלונות 8 ב HTML5

פורסם בתאריך May 02 2012, 01:41 AM על ידי eladkatz

הוצאת הספרים APress מחלקת הלילה ספר פיתוח לחלונות 8 עם HTML5 בחינם לגמרי!
ההצעה תקפה עד 12 בלילה, אני מניח זמן ניו יורק (ככה שיש עד הבוקר שלנו)

מהרו והורידו עכשיו!

http://www.apress.com/9781430244882

st_9781430244882

עשר הטעויות הנפוצות ביותר ב MVVM שכמעט כל אחד נופל בהן

פורסם בתאריך Apr 29 2012, 10:54 AM על ידי eladkatz

קיבלתי לא מעט תגובות על הפוסט הקודם שלי – הפיכת ה Code behind ל ViewModel ב WPF – כשחלקן גרמו לי לחשוב עוד פעם כמה חסר תיעוד מסודר של MVVM. יש המון חומר כתוב באינטרנט, אבל רובו המוחלט לא מסודר, לא מדוייק, ואפילו לפעמים ממש מטעה.
המצב עד כדי כך מורכב שכמעט לא יוצא לי לייעץ בחברה בה לא נופלים לטעות כזו או אחרת, היות ולא מעט טעויות השתרשו כפתרונות לגיטימיים במהלך השנתיים האחרונות.

שתי תגובות קלאסיות שקיבלתי יותר מפעם אחת היו:
1. “ב MVVM לפעמים ה ViewModel משרת כמה View-ים, והדרך שהצגת בפוסט מאפשרת רק יחס של 1:1”
2. “בפוסט למעשה הורדת את ה- Code Behind לחלוטין, ויש מקרים בהם כן נצטרך code-behind!”

שתי התגובות הללו מבוססות על טעויות נפוצות ב-MVVM שחשוב מאוד להמנע מהן.

להסביר לעומק כל טעות, ומה בעייתי בה זה סיפור לא פשוט בכלל.

בקרוב אעלה סדרת פוסטים מאוד רחבה על ארכיטקטורת UI בה אנסה לסדר חומר על כל הנושאים המרכזיים, כש MVVM זו רק ההתחלה (בשלב הזה התוכנית היא MVVM, Prism, Inversion of Control, SOLID principles in UI, Plugin Architecture, אבל זה עוד כפוף לשינויים..). היותר וסדרת פוסטים כזו זה לא משהו פשוט לכתוב (או לפחות יקח לי לא מעט זמן) חשבתי שלבינתיים רשימת טעויות ואינדיקציות יכול כבר לעזור בלא מעט פרוייקטים.

solid_thumb

 

עשר הטעויות הכי נפוצות ב- MVVM

1. שימוש ב ViewModel אחד לשרת כמה View-ים שונים. (הפרה של עקרון SRP י)
2. שימוש ב code-behind, ברמה כל שהיא. (מדרון חלקלק)
3. שימוש ברכיבי Pub/sub כמו Messenger או EventAggregator בשביל תקשורת בין ViewModels שונים בצורה חופשית מדי (אחת הטעויות הנפוצות ביותר) SRP
4. יצירת View בשביל קונטרול ריוזאבילי (reusable control)י
5. יצירת היררכיית מחלקות גדולה ב ViewModel (עקרונית, תמיד עדיף שה ViewModel לא יירש משום מחלקה אחרת עד כמה שאפשר)
6. שימוש בירושה בשביל reuse של התנהגות. (ירושה היא כמעט תמיד רק בשביל פולי-מורפיזם)
7. יצירת צימוד גבוה בין ViewModel-ים שונים (דומה ל 3#) ע”י החזקת רפרנס מ ViewModel אחד לשני.(כמעט תמיד טעות)
8. החזרת רפרנס בצורה ישירה ל ViewModel מתוך שכבת ה Services. (יצירת service-ים שלמעשה מנהלים את ה ViewModel-ים)
9. כתיבה ב-MVVM ללא הקפדה על בלנדאביליות (Blendability )י
10. הכנסת קוד של View לשכבת ה ViewModel. (הפרה של SRP)

 

כל אחת מן הטעויות הללו יכולות בקלות להפוך את האפליקציה שלנו לקשה מאוד לתחזוקה, ואת MVVM למיותר. אם אתם מזהים שאתם מבצעים את אחת מהטעויות הנ”ל שווה לעצור ולראות אם צריך לתקן את זה.

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

אינדיקציות לכך שיכול להיות שיש לנו טעות ארכיטקטונית/ניהולית בפרוייקט

1. יצירת ViewModel-ים עם יותר מ-500 שורות קוד.
2. מסך ה designer ב VS לא מראה שום דבר שימושי.
3. כתיבת פיצ’ר חדש בתוכנה לוקחת יותר מפי שניים זמן מאשר זה היה לוקח בהתחלה. (אסור להתפשר על זה!!)
4. יכול להיות מצב בו מפתח תקוע בעבודה בגלל שקוד של מפתח אחר לא עובד או לא גמור.
5. במידה ולא כל המפתחים מבינים את התשתיות.
6. במידה והתשתיות גדולות (עקרון KISS מאוד חשוב בתשתיות), ולכן יכול בכלל להווצר מצב ש-#5 יקרה.
7. לוקח יותר מ-20 שניות מהזמן שמתכנת מריץ את האפליקציה עד שהוא יכול לראות את הפיצ’ר שעליו הוא עובד באותו זמן (אחד הדברים החשובים ביותר, ולצערי משהו שאני רואה כמעט בכל מקום)
8. מחלקות עם השם Manager בתוכן (לעיתים קרובות אינדיקציה ליצירת god-objects, אם כי לא תמיד)

כאמור, אנסה בהמשך לתת הסבר מפורט על כל אחד מהנ”ל, אבל בלי להסביר בכלל אי אפשר, ולכן נתרכז בשתי התגובות המרכזיות על הפוסט הקודם..

 

1. בMVVM כל ViewModel ישרת View אחד, ו View אחד בלבד!

המטרה של ViewModel היא להחזיק אחריות מסוימת על חלק מסוים באפליקציה. האחריות הזו היא אחריות מאוד ספציפיות ולכן ה ViewModel לא אמור להתאים לכמה View-ים שונים. למעשה, יש כאן עקרון רחב יותר שבא לידי ביטוי. SRP – Single Responsibility Principle יכול להתנגש עם עקרון DRY – Don’t Repeat Yourself. ארחיב על כך בפוסטים הבאים, אך הנקודה המשמעותית היא שכל מחלקה אמורה להיות או עם אחריות מסוימת , ואז המחלקה לא תהיה ריוזאבילית (reusable), או שמחלקה כן תהייה ריוזאבילית, ואז לא תהייה לא אחריות ספציפית באפליציה. (כדוגמא קוד של קונטרול).

2. בMVVM אין צורך להשתמש אף פעם ב CodeBehind

MVVM מצריך לא מעט קוד של UI, אבל אף פעם לא כדאי לשים את הקוד ב CodeBehind אלא ב Behaviors או Control-ים. CodeBehind הוא מקום מאוד בעייתי היות וקל מאוד לשים שם גם לוגיקה ממש ולא רק UI, ותוך כדי צימוד גבוה ל UI (וצימוד בין UI לבין לוגיקה היא מטרת ה Design Pattern מלכתחילה).

איך ליצור Binding ללא כתיבת קוד–ע”י שימוש בBinding Wizard של VS2010

פורסם בתאריך Apr 25 2012, 06:22 PM על ידי eladkatz

שאלה שנשאלה בפורום WPF –

ש: כשכותבים Binding ב WPF יש אינטיליסנס (השלמת כתיבה) חלקית בלבד. האם יש דרך שויז’ואל סטודיו ישלים את מה שאנחנו כותבים?
ת: האמת היא שיש - החל מויז'ואל סטודיו 2010 - והדרך הכי טובה לראות איך זה עובד זה ע"י הדגמה של היכולת (מומלץ לראות באיכות גבוהה - 720p):


איך לגרום ל WPF להתנהג כאילו ש MVVM באמת נתמך מהקופסא - להחליף בין ה Code Behind ל ViewModel !

פורסם בתאריך Apr 24 2012, 07:07 PM על ידי eladkatz

יוצא לי ללמד MVVM לעיתים די קרובות, וכמעט תמיד עולה השאלה למה WPF לא תומך בזה “בילט אין”.
הרי CodeBehind ו ViewModel בסופו של יום מאוד דומים, ההבדל הוא רק ש ViewModel הוא מחלקה נפרדת לחלוטין ולפיכך ההפרדה בין הלוגיקה לבין הUI חזקה יותר.

כשמממשים MVVM, ויזו’ואל סטודיו לא יודע על הקישור בין ה View לבין ה ViewModel, ולפיכך אי אפשר לעבור ביניהם בקלות ע”י לחיצה על F7 כמו שאפשר בין ה View ל CodeBehind שלו. בנוסף, היה נחמד אם ויז’ואל סטודיו היה מציג את ה ViewModel “מתחת” ל View, כמו שהוא עושה ל CodeBehind.
בנוסף לכל זה, את ה CodeBehind עצמו אני בכלל לא צריך.. והייתי מעדיף אפילו להעיף אותו בשביל למנוע “כסתוחים” של מפתחים אצלי בצוות. אם אין קובץ CodeBehind, אי אפשר לכתוב CodeBehind!

 

להפוך את ה CodeBehind ל ViewModel

הרעיון הסתובב לי בראש תקופה לא קצרה, ונראה לי שעליתי על טריק נחמד בשביל לבצע בדיוק את זה. פשוט נהפוך את ה CodeBehind  ל ViewModel ונרקוד בשתי החתונות!

כדוגמא, נניח שיש לי View בשם FirstView.

הזאמל המקורי שלו יראה כך:

<UserControl x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
            
    </Grid>
</UserControl>

השורה שמעניינת אותנו היא השורה הזו:

x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"

שגורמת לפארסר של זאמל לייצר מחלקה בשם FirstView שיורשת מהמחלקה UserControl. עד כאן בסדר.

אם נעבור לCodeBehind, נראה את הקוד הבא, שכאמור אנחנו לא מעוניינים בו:

 

namespace ViewModelAsCodeBehindTrick.Views
{
    /// <summary>
    /// Interaction logic for FirstView.xaml
    /// </summary>
    public partial class FirstView : UserControl
    {
        public FirstView()
        {
            InitializeComponent();
        }
    }
}

מה שיש כאן זה החלק השני של המחלקה FirstView שמתחבר למחלקה שנוצרה אוטומאטית ע”י זאמל. כל מה שאנחנו צריכים לעשות, זה:

1. לשנות את השם של המחלקה מ FirstView ל FirstViewModel
2. לשנות את ה- namespace  ל-ViewModels.
3. להעיף את הקונסטרקטור שקורא ל- InitializeComponent (המתודה לא קיימת ב ViewModel )
4. לרשת מ INotifyPropertyChanged בשביל שה- ViewModel שלנו יהיה מוכן ל DataBinding מאוחר יותר.

בסופו של יום הקובץ יראה כך:

namespace ViewModelAsCodeBehindTrick.ViewsModels
{
    /// <summary>
    /// Interaction logic for FirstView.xaml
    /// </summary>
    public class FirstViewModel : INotifyPropertyChanged
    {
 
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

 

לצורך הדוגמא, נוסיף פרופרטי (Property) ל ViewModel ונשים בו סתם טקסט על מנת שנוכל לבדוק שהכל עובד מאוחר יותר כשנחבר הכל. בדוגמא הזו בחרתי להוסיף פרופרטי בשם SomeText, ובקונסטרטור קבעתי את הערך שלו להיות “Hello MVVM”:

 

public class FirstViewModel : INotifyPropertyChanged
{
    private string someText;
    public string SomeText
    {
        get { return someText; }
        set
        {
            someText = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("SomeText"));
        }
    }
 
    public FirstViewModel()
    {
        SomeText = "Hello MVVM";
    }
 
 
    public event PropertyChangedEventHandler PropertyChanged;
}

 

כל מה שנותר עכשיו לעשות זה לחבר את ה View ל ViewModel, ואת זה אפשר לעשות בכל הדרכים הסטנדרטיות. (בדרך כלל אני אשתמש ב ViewModelLocator בשביל לחבר אותם, בדוגמא הזו בחרתי בדרך פשוטה יותר:

<UserControl ...  >
    <UserControl.DataContext>
        <vm:FirstViewModel />
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding SomeText}" />
    </Grid>
</UserControl>

בנוסף, הוספתי TextBlock שקשור ל SomeText על מנת שאוכל לראות שהכל התחבר כמו שצריך, ואכן אם נסתכל בתצוגה המקדימה של העורך נראה שהקישור עובד כפי שרצינו!

image

 

 

לא כל כך מהר..

הסיפור קצת מסתבך, בגלל שבלי ששמנו לב יצרנו כאן באג בזמן ריצה. אם נשים את ה View שלנו בתוך החלון הראשי, כך:

 

<Window x:Class="ViewModelAsCodeBehindTrick.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:v="clr-namespace:ViewModelAsCodeBehindTrick.Views"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <v:FirstView x:Name="firstView1" />
    </Grid>
</Window>

 

ואז נריץ, לא נראה כלום. הקישור לא עובד משום מה…

image

 

וזה למרות שבדיזיין טיים רואים את הטקסט בדיוק כפי שאנחנו מצפים:

image

אז מה בדיוק קורה פה? למה זה לא עובד בזמן ריצה למרות שזה *כן* עובד זמן עיצוב?!
האינסטינקט המיידי של לבדוק את חלון ה- output ולראות אם יש שגיאות Binding לא יעזור לנו כאן. זו בעיה אחרת לחלוטין…

(שווה לקחת כמה שניות ולחשוב על זה ולנסות לעלות על זה לבד.. Smile )

.

.

.

.

.

.

.

.

.

.

 

להבין את תהליך פרסור הזמאל

אז ככה. מקובץ הזאמל נוצרים שני קבצים בזמן הקימפול:

1. FirstView.g.cs שבו יושבת המחלקה FirstView. המחלקה הזו טוענת את הקובץ השני -
2. FirstView.Baml שזהו קובץ הזאמל שלנו אחרי סוג של קמפול. (למעשה אחרי Tokenization – פרסור הקובץ כך שהטעינה תתבצע מחר יותר בזמן ריצה)

הטעינה והחיבור של שני הקבצים מתבצעות במתודה InitializeComponent שנמצאת ב FirstView.g.cs. רק.. שעכשיו שמחקנו את ה- CodeBehind אף אחד לא קורה לה. לפיכך, ה View שלנו פשוט לא טוען את קובץ הBaml ולפיכך הוא נשאר ריק. מה שמשעשע כאן זה שויז’ואל סטודיו בזמן העיצוב (Design Time) כן קורא למתודה הזו באופן אוטומאטי, וזו הסיבה שבזמן העיצוב כן ראינו שזה עובד.
כל מה שאנחנו צריכים לעשות בשביל שזה יעבוד, זה לודא שהמתודה הזו נקראת בזמן ריצה… אבל איך?

 

 

יצירת UserControl שקורא אוטומאטית ל InitializeComponent

 

הפתרון שאני מצאתי לזה הוא טריק די נחמד. במקום שהיוזר קונטרול יירש מ UserControl, נגרום לו לרשת ממחלקה חדשה בשם ViewBase.

ניצור מחלקה חדשה בשם ViewBase, שיורשת מ UserControl:

public class ViewBase : UserControl
{
}

 

ובמקום שהView שלנו יירש ישירות מ UserControl, ניתן לו לרשת מ ViewBase:

<v:ViewBase x:Class="ViewModelAsCodeBehindTrick.Views.FirstView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:ViewModelAsCodeBehindTrick.ViewsModels"
             xmlns:v="clr-namespace:ViewModelAsCodeBehindTrick.Views"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <vm:FirstViewModel />
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding SomeText}" />
    </Grid>
</v:ViewBase>

(כשמשנים את UserControl ל v:ViewBase ויז’ואל סטודיו לא נותן intellisense. זה בסדר. אנחנו עושים משהו שויז’ואל סטודיו לא מצפה שנעשה, אבל זה תקין).

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

image

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

שום דבר שקצת רפלקשן לא יפתור Smile

 

this.GetType().GetMethod("InitializeComponent").Invoke(this, null);

 

אם נריץ עכשיו את האפליקציה נראה שזה אכן עובד!

image 

 

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

 

image

מה שקורה עכשיו זה שאנחנו מנסים לקרוא למתודה InitilizeComponent בזמן העיצוב, שזה בלתי אפשרי.
את זה כבר יהיה קל לפתור – פשוט נודא שהקריאה הזו תתבצע אך ורק אם אנחנו לא בזמן העיצוב:

 

public class ViewBase : UserControl
{
    public ViewBase()
    {
        if (!DesignerProperties.GetIsInDesignMode(this))
            this.GetType().GetMethod("InitializeComponent").Invoke(this, null);
    }
}

 

 

ועכשיו זה יעבוד מושלם גם בזמן ריצה וגם בזמן עיצוב!

האם זה שווה את המאמץ?

בשורה התחתונה, אחרי שכתבנו את הViewBase פעם אחת, יהיה נורא קל להשתמש בזה כמה שנרצה, כשאנחנו מקבלים כמה פיצ’רים מאוד נחמדים:

1. קיבלנו ViewModel שנמצא בsolution explorer מתחת לview. מאוד עוזר לטעמי.

2. אין יותר CodeBehind מיותר!

3. אם נמצאים View ורוצים לעבור ל ViewModel, כל מה שצריך לעשות זה פשוט ללחוץ F7!
(לצערי בכיוון ההפוך זה לא עובד, אבל זה עדיין לא מעט)

 

לפי דעתי זה בהחלט שווה את המאמץ, אבל גם אם לא, זה עדיין טריק ממש נחמד, לא? Smile

 

שווה להוריד את הקוד המלא ולשחק עם זה. אשמח מאוד לשמוע חוות דעת – האם זה שימושי? האם זה באמת עוזר?

את הקוד המלא אפשר להוריד מכאן

טיפ WPF - איך לגרום ל ToggleButtons להתנהג כמו קבוצה? איך לממש את זה ב MVVM ?

פורסם בתאריך Apr 04 2012, 09:01 PM על ידי eladkatz

לקוח שאל אותי לאחרונה איך אפשר להשתמש ב Toggle Button כך שרק אחד יהיה בחור בכל רגע נתון, ושבמידה ולוחצים על אחר אז הכפתור הקודם לא יהיה בחור יותר, זה בערך המצב אליו הוא רצה להגיע:

SNAGHTML593b808

רק שבמימוש הנאיבי הראשוני שום דבר לא מונע מהכפתורים להיות לחוצים כמה ביחד:

<UniformGrid Rows="6" Columns="2" HorizontalAlignment="Left" >
    <UniformGrid.Resources>
        <Style TargetType="ToggleButton">
            <Setter Property="Margin" Value="2" />
            <Setter Property="Padding" Value="10,5" />
        </Style>
    </UniformGrid.Resources>
    <ToggleButton Content="tool1" />
    <ToggleButton Content="tool2" />
    <ToggleButton Content="tool3" />
    <ToggleButton Content="tool4" />
    <ToggleButton Content="tool5" />
    <ToggleButton Content="tool6" />
</UniformGrid>

 

SNAGHTML66f232c

 

 

 

להשתמש בקונטרול הנכון

ההתחלה החשובה ביותר היא כמובן להתחיל מהמחלקה הנכונה. למרות שמה שהוא רצה *נראה* כמו ToggleButton, ההתנהגות שהוא מבקש הרבה יותר קרובה ל RadioButton. אם נתחיל משם יהיה לנו הרבה יותר קל, ולפיכך זה יהיה הצעד הראשון:

 

<UniformGrid Rows="6" Columns="2" HorizontalAlignment="Left" >
    <UniformGrid.Resources>
        <Style TargetType="ToggleButton">
            <Setter Property="Margin" Value="2" />
            <Setter Property="Padding" Value="10,5" />
        </Style>
    </UniformGrid.Resources>
    <RadioButton Content="tool1"/>
    <RadioButton Content="tool2"/>
    <RadioButton Content="tool3"/>
    <RadioButton Content="tool4"/>
    <RadioButton Content="tool5"/>
    <RadioButton Content="tool6"/>
</UniformGrid>

 

SNAGHTML59abbfe

רק שכמובן, כרגע זה לא ממש נראה כמו שהוא ביקש.
קל לתקן את זה – פשוט נגדיר Style שישכתב את ה Template של ה RadioButton:

 

<Style TargetType="RadioButton">
    <Setter Property="Margin" Value="2" />
    <Setter Property="Padding" Value="10,5" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="RadioButton">
                <Border BorderThickness="1" BorderBrush="#999" CornerRadius="2"  x:Name="theBorder">
                    <Border.Background>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                            <GradientStop Color="#FFEBEBEB" Offset="0.492"/>
                            <GradientStop Color="#FFDDDDDD" Offset="0.518"/>
                            <GradientStop Color="#FFCFCFCF" Offset="0.968"/>
                        </LinearGradientBrush>
                        
                    </Border.Background>
                    <Border x:Name="shadowBorder" BorderThickness="0,0,0,0" BorderBrush="#66000000" >
                        <ContentPresenter x:Name="theCP" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                    </Border>
                    
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Background" TargetName="theBorder" >
                            <Setter.Value>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFC2E4F6" Offset="0.494"/>
                                    <GradientStop Color="#FFAADAF3" Offset="0.517"/>
                                    <GradientStop Color="#FF92CCEC" Offset="1"/>
                                </LinearGradientBrush>
 
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Margin" TargetName="theCP" Value="1,1,0,0" />
                        <Setter Property="BorderThickness" TargetName="shadowBorder" Value="1,1,0,0" />
                        
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

וקיבלנו ToggleButtons שאפשר לבחור בהם רק אחד בכל רגע נתון.

SNAGHTML66dd6a7

בשלב הזה, נהיה מעוניינים לעבוד עם הקונטרולים הללו בצורה הסטנדרטית – ב MVVM. היינו רוצים שה ViewModel יכיל רשימה של “כלים” ואליה נקשור את רשימת הכפתורים שלנו. בנוסף נרצה שה ViewModel יחזיק גם את ה “כלי” שנבחר – כך ששינוי בUI יעדכן את ה ViewModel, ושינוי ב ViewModel  ישנה את ה UI.
המטרה היא שה ViewModel יראה כך:

public class MainWindowViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<String> tools;
        public ObservableCollection<String> Tools
        {
            get { return tools; }
            set
            {
                tools = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Tools"));
            }
        }
 
        private string selectedTool;
        public string SelectedTool
        {
            get { return selectedTool; }
            set
            {
                selectedTool = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("SelectedTool"));
            }
        }
 
 
 
        public MainWindowViewModel()
        {
            Tools = new ObservableCollection<String>();
            Tools.Add("Tool1");
            Tools.Add("Tool2");
            Tools.Add("Tool3");
            Tools.Add("Tool4");
            Tools.Add("Tool5");
            Tools.Add("Tool6");
            Tools.Add("Tool7");
 
            SelectedTool = "Tool2";
        }
 
 
 
        public event PropertyChangedEventHandler PropertyChanged;
    }

 

אבל אין לנו איך לקשור את ה UI בצורה נכונה. אז איך ממשים את זה ב MVVM?

 

 

 

מימוש נכון ב MVVM

גם כאן, כמו בהרבה מקרים אחרים ב MVVM – במידה ורוצים להתאים את ה UI ל Binding של ה ViewModel אבל אין לנו איך לקשור את זה בצורה נכונה – הפיצ’ר החזק Behaviors יעזור לנו. נגדיר Behavior חדש למטרה זו בדיוק – Behavior שאפשר לשים רק על RadioButton:

 

public class RadioButtonSelectorBehavior : Behavior<RadioButton>
{ 
 
}

 

המטרה היא שה Behavior הזה יחשוף שני מאפיינים – Value – שייצג את הערך של ה - RadioButton,  ו SelectedValue – שיקשר לערך שכרגע נבחר. במידה וה SelectedValue ישתנה – הBehavior  ידאג לעדכן את ה RadioButton – וההפך – במידה וה RadioButton נלחץ – ה Behavior יעדכן את ה SelectedValue:

אלו יהיו המאפיינים שנוסיף:

public class RadioButtonSelectorBehavior : Behavior<RadioButton>
{
    public object Value
    {
        get { return (object)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(RadioButtonSelectorBehavior), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });
 
 
    public object SelectedValue
    {
        get { return (object)GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for SelectedValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register("SelectedValue", typeof(object), typeof(RadioButtonSelectorBehavior), new FrameworkPropertyMetadata(null, SelectedValueChanged) { BindsTwoWayByDefault = true });
 
}

והרישום הרלוונטי לאירועים:

 

 
        static void SelectedValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RadioButtonSelectorBehavior b = sender as RadioButtonSelectorBehavior;
 
            if (b.AssociatedObject != null)
                b.CheckSelection();
        }
 
        protected override void OnAttached()
        {
 
            this.AssociatedObject.Checked += new RoutedEventHandler(AssociatedObject_Checked);
            this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
            
            
        }
 
        void CheckSelection()
        {
            this.AssociatedObject.IsChecked =
                this.Value.Equals(this.SelectedValue);
        }
 
 
        void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            
            CheckSelection();
 
        }
 
        void AssociatedObject_Checked(object sender, RoutedEventArgs e)
        {
            SelectedValue = Value;
        }

 

הקוד המלא של ה Behavior יראה כך:

 

public class RadioButtonSelectorBehavior : Behavior<RadioButton>
{
    public object Value
    {
        get { return (object)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(RadioButtonSelectorBehavior), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });
 
 
    public object SelectedValue
    {
        get { return (object)GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for SelectedValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register("SelectedValue", typeof(object), typeof(RadioButtonSelectorBehavior), new FrameworkPropertyMetadata(null, SelectedValueChanged) { BindsTwoWayByDefault = true });
 
    static void SelectedValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        RadioButtonSelectorBehavior b = sender as RadioButtonSelectorBehavior;
 
        if (b.AssociatedObject != null)
            b.CheckSelection();
    }
 
    protected override void OnAttached()
    {
 
        this.AssociatedObject.Checked += new RoutedEventHandler(AssociatedObject_Checked);
        this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
        
        
    }
 
    void CheckSelection()
    {
        this.AssociatedObject.IsChecked =
            this.Value.Equals(this.SelectedValue);
    }
 
 
    void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        
        CheckSelection();
 
    }
 
    void AssociatedObject_Checked(object sender, RoutedEventArgs e)
    {
        SelectedValue = Value;
    }
 
}

 

בשלב הזה הכל מוכן, וכל מה שנותר לנו הוא להשתמש במה שבנינו ב XAML שלנו:

 

 

<ItemsControl ItemsSource="{Binding Tools}" HorizontalAlignment="Left" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <RadioButton GroupName="tools" Content="{Binding }" >
                <i:Interaction.Behaviors>
                    <my:RadioButtonSelectorBehavior Value="{Binding .}" SelectedValue="{Binding Source={StaticResource vm}, Path=SelectedTool}" />
                </i:Interaction.Behaviors>
            </RadioButton>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Rows="8" Columns="2" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

 

והקוד המלא של החלון יראה כך:

 

<Window x:Class="ToggleRadioButtons.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:ToggleRadioButtons"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <my:MainWindowViewModel x:Key="vm" />
        <Style TargetType="RadioButton">
            <Setter Property="Margin" Value="2" />
            <Setter Property="Padding" Value="10,5" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RadioButton">
                        <Border BorderThickness="1" BorderBrush="#999" CornerRadius="2"  x:Name="theBorder">
                            <Border.Background>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFEBEBEB" Offset="0.492"/>
                                    <GradientStop Color="#FFDDDDDD" Offset="0.518"/>
                                    <GradientStop Color="#FFCFCFCF" Offset="0.968"/>
                                </LinearGradientBrush>
 
                            </Border.Background>
                            <Border x:Name="shadowBorder" BorderThickness="0,0,0,0" BorderBrush="#66000000" >
                                <ContentPresenter x:Name="theCP" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </Border>
 
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="Background" TargetName="theBorder" >
                                    <Setter.Value>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#FFC2E4F6" Offset="0.494"/>
                                            <GradientStop Color="#FFAADAF3" Offset="0.517"/>
                                            <GradientStop Color="#FF92CCEC" Offset="1"/>
                                        </LinearGradientBrush>
 
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Margin" TargetName="theCP" Value="1,1,0,0" />
                                <Setter Property="BorderThickness" TargetName="shadowBorder" Value="1,1,0,0" />
 
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Window.DataContext>
        <StaticResourceExtension ResourceKey="vm" />
    </Window.DataContext>
 
        <Grid>
    
        <ItemsControl ItemsSource="{Binding Tools}" HorizontalAlignment="Left" >
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <RadioButton GroupName="tools" Content="{Binding }" >
                        <i:Interaction.Behaviors>
                            <my:RadioButtonSelectorBehavior Value="{Binding .}" SelectedValue="{Binding Source={StaticResource vm}, Path=SelectedTool}" />
                        </i:Interaction.Behaviors>
                    </RadioButton>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="8" Columns="2" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <!--<UniformGrid Rows="6" Columns="2" HorizontalAlignment="Left" >
            <UniformGrid.Resources>
            </UniformGrid.Resources>
            <RadioButton Content="tool1"/>
            <RadioButton Content="tool2"/>
            <RadioButton Content="tool3"/>
            <RadioButton Content="tool4" IsChecked="True"/>
            <RadioButton Content="tool5"/>
            <RadioButton Content="tool6"/>
        </UniformGrid>-->
    </Grid>
</Window>

 

 

בשורה התחתונה, שימוש בBehavior יכול מאוד להקל את העבודה של ה ViewModel ולהפוך אותו להרבה יותר ברור ופשוט.
יש לציין שעקרונית אפשר היה להתחיל מ ListBox ולא מרשימת RadioButtons – אבל זו דרך קשה יותר מבחינה טכנית מאשר הדרך בה בחרנו, למרות שהיא בפירוש נכונה. הדרך המוצעת לעיל היא הדרך הכי פשוטה לדעתי.

אפשר להוריד את הקוד כאן

קוד ומצגת להרצאה על ג'אווה סקריפט מתקדם - KnockoutJS, Script# , Dart - כנס סלע SDP12

פורסם בתאריך Mar 28 2012, 10:25 PM על ידי eladkatz

כנס סלע SDP12 סוגר יום נוסף של הרצאות, ומחר היום האחרון.
תודה לכל מי שבא היום להרצאה של גיל, רן ושלי על ג’אווה סקריפט מתקדם – אני מאוד נהניתי!

מצורפים להלן המצגת והקוד שהראיתי בכיתה:

ואת הקוד אפשר להוריד מכאן.

 

 

בשביל להוריד את Script Sharp יש ללחוץ כאן. שימו לב כי זו גרסה 0.7.4 – וניקיל קותארי עדיין מתחזק את הקוד.

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

כאמור, אני מאוד מאוד ממליץ ללמוד ג’אווה סקריפט לעומק במידה ורוצים להיות מפתחי צד-לקוח באמת טובים. חלק גדול מהמוניטין הבעייתי של ג’אווה סקריפט מגיע מזה שלמרות שהיא נראית דומה במבט ראשון לשפות רגילות, יש לה כמה נושאים טיפה מפתיעים (ראינו כמה בכיתה – כדוגמה “בריחת” ה Scope ל global namespace).
בהקשר הזה אני מאוד ממליץ על הספרים הבאים – שיגרמו לכם לאהוב את השפה מחדש:

1. JavaScript – the good parts
2. JavaScript Patterns

 

מחר מגיע היום האחרון והכי מעניין בכנס – אני הולך להרצות על פיתוח ב HTML5 לחלונות 8. אני לא זוכר מתי בפעם האחרונה הושקעו כל כך הרבה משאבים על מנת לספק את המקסימום לבאי הכנס – כל מי שיגיע מחר הולך לראות הרבה מאוד קוד, והרבה מאוד דברים מגניבים. יהיה מעניין!

 

image

קוד ומצגת ליום הפתוח שהתקיים ב19.3 במיקרוסופט - פיתוח לחלונות 8 ב HTML5

פורסם בתאריך Mar 19 2012, 07:41 PM על ידי eladkatz

תודה לכל מי שבא היום ליום הפתוח במיקרוסופט – תמיד כיף לדבר לפני אנשים שבאמת מעוניינים בטכנולוגיה – וכבר הרבה זמן לא היה נושא חם ומרתק כמו פיתוח לחלונות 8.

כפי שהבטחתי – מצורפת להלן המצגת, לצד הקוד שבנינו במהלך ההרצאה.

 

והקוד שבנינו אפשר להוריד מכאן. (כמובן, יש צורך בVS11 וחלונות 8 על מנת לפתוח את הקבצים)

איך מוסיפים עמודות בזמן ריצה לגריד ב - MVVM, ואיך Behaviors מאפשר לנו לפתור בעיות מורכבות ב MVVM

פורסם בתאריך Mar 12 2012, 04:18 PM על ידי eladkatz

איך מוסיפים עמודות בזמן ריצה לגריד ב – MVVM?

בפוסט הקודם הראיתי איך אפשר להוסיף עמודות דינאמיות, שידועות רק בזמן ריצה לגריד.
הבעיה היא שהמאפיין (Property) של העמודות – Columns לא תומך ב Data binding, ולפיכך אי אפשר לשלוט בזה מה ViewModel… אז מה עושים במידה ורוצים לכתוב ב MVVM ?

הדרך הטובה ביותר לפתור בעיות מהסוג הזה היא או לרשת מ DataGrid ולהוסיף לו פונקציונאליות, או להוסיף את הפונקציונאליות ע”י הגדרת Behavior.
עקרונית, שתי הדרכים די מקבילות. בדרך כלל אני משתמש ב Behavior על מנת להוסיף פונקצינאליות פשוטה ו”קטנה”. במידה ואני מוסיף הרבה אז כבר אצור קונטרול חדש שיורש מהקונטרול שאותו אני רוצה להרחיב.

למי שלא מכיר Behaviors – זה הפיצ’ר החשוב ביותר שהתוסף ב WPF4, וזה פיצ’ר שמאפשר לפתור הרבה מאוד בעיות בMVVM, כפי שכבר כתבתי בעבר.
במקרה שלנו, כל מה שצריך לעשות זה לכתוב Behavior שמתלבש על גריד, ומאפשר ע”י DataBinding לשלוט בעמודות של הגריד.

הגדרת ה Behavior

ניצור Behavior חדש שמתלבש על גריד.:

public class BindableColumnsBehavior : Behavior<DataGrid>
{ 
    protected override void OnAttached()
    {
        this.AssociatedObject.Loaded += ...
    }
}

ל Behavior יש גישה לגריד דרך המאפיין AssociatedObject. דרכו אפשר להרשם לאירוע Loaded של הגריד, שבו נחבר הכל עוד מעט.

בשלב הזה נוסיף מאפיין בשם ColumnsSource שמקביל למאפיין Columns בגריד, רק שלהבדיל מבגריד, המאפיין שלנו יתמוך ב DataBinding ולפיכך יתאים לשליטה מה ViewModel על פי עקרונות MVVM.
בשביל זה נגדיר Dependency Property, מסוג ObservableCollection של DynamicColumn. אנחנו מגידירים DependencyProperty על מנת שזה יתמוך ב DataBinding, והוא מסוג ObservableCollection על מנת שנדע כאשר מתוספת עמודה חדשה (ע”י קישור DataBinding שיגיע מאוחר יותר מה ViewModel).

public class BindableColumnsBehavior : Behavior<DataGrid>
{
    public ObservableCollection<DynamicColumn> ColumnsSource
    {
        get { return (ObservableCollection<DynamicColumn>)GetValue(ColumnsSourceProperty); }
        set { SetValue(ColumnsSourceProperty, value); }
    }
 
    // Using a DependencyProperty as the backing store for ColumnsSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnsSourceProperty =
        DependencyProperty.Register("ColumnsSource", typeof(ObservableCollection<DynamicColumn>), typeof(BindableColumnsBehavior), new UIPropertyMetadata(null));
 
}

המחלקה DynamicColumn היא מחלקה שמוגדרת כך:

public class DynamicColumn
{
    public string Header { get; set; }
    public string BindingPath { get; set; }
}

היא מכילה את Header שיהיה הכותרת של העמודה, ו BindingPath.

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

 

void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    if (ColumnsSource != null)
    {
        ColumnsSource.CollectionChanged +=
            (s, args) =>
            {
                foreach (var item in args.NewItems)
                {
                    DynamicColumn dc = item as DynamicColumn;
 
                    AssociatedObject.Columns.Add(new DataGridTextColumn
                    {
                        Header = dc.Header,
                        Binding = new Binding(dc.BindingPath)
                    });
                }
            };
    }
}

וסיימנו עם ה Behavior. כל מה שנותר לנו לעשות עכשיו זה להשתמש להגדיר את ה ViewModel בצורה הנכונה, ולהשתמש בBehavior בצד ה View.

 

שליטה מה ViewModel על העמודות

נגדיר ב ViewModel מאפיין של עמודות בשם StationColumns. הוספה של עמודות אליו תוסיף עמודות לגריד מאוחר יותר.
בנוסף נגדיר מתודה שתוסיף עמודה בזמן ריצה -

 

public ObservableCollection<DynamicColumn> StationColumns { get; set; }
 
 
void AddStationAction()
{
    Random r = new Random();
    int StationID = r.Next(1000);
 
    foreach (var item in TheList)
    {
 
        item.Comments.Add(StationID, "Station: " + StationID);
    }
 
    StationColumns.Add(new DynamicColumn 
    {
        Header = "Dynamic Station " + StationID,
        BindingPath = "Comments[" + StationID + "]"
    });
 
}

כאשר המתודה AddStationAction תקרא (ע”י הפעלת Command מן הסתם), אנו מוסיפים גם מידע דינאמי לתחנות שלנו, ומוסיפים עמודה על ידי הוספה למערך העמודות. כל מה שנותר לנו זה להוסיף את ה Behavior ולקשור אותו בצד ה View:

<i:Interaction.Behaviors>
    <b:BindableColumnsBehavior ColumnsSource="{Binding StationColumns}" />
</i:Interaction.Behaviors>

והקוד המלא של הגריד נראה כך:

 

<DataGrid ItemsSource="{Binding Stations}" AutoGenerateColumns="False" x:Name="TheGrid">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" Binding="{Binding ID}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="dynamic 1" Binding="{Binding Events[1]}" />
        <DataGridTextColumn Header="dynamic 2" Binding="{Binding Events[3]}" />
    </DataGrid.Columns>
    <i:Interaction.Behaviors>
        <b:BindableColumnsBehavior ColumnsSource="{Binding StationColumns}" />
    </i:Interaction.Behaviors>
</DataGrid>

כשזו התוצאה הסופית.

 

image

 

למה Behaviors הם פתרון כל כך טוב ב MVVM ?

שאלה שאני שומע לעיתים קרובות היא האם כל הקוד הזה הוא לא קצת עבודה בעיניים. הרי במקום לכתוב קוד ב Code behind, העברנו אותו ל Behavior – האם זה שינוי כזה גדול?
אז האמת היא שזה שינוי קטן, אבל סופר משמעותי. קוד שמוסיף בצורה דינאמית עמודות הוא קוד של View, וקוד שמקומו ב View. עקרונית לכתוב את זה בCodeBehind זה מאוד נכון מבחינת שמירת על עקרונות MVVM – עקרונות בהם יש לשים קוד *לוגי* ב ViewModel, וקוד של UI ב View.
לעיתים קרובות אנשים מפרשים את MVVM כ “לשים את כל הקוד ב ViewModel” ואף פעם לא ב CodeBehind, וזו טעות רצינית. מטרת MVVM היא הפרדה טובה בין *סוגי הקוד*, ולשים קוד שאחראי על UI ב ViewModel זו הפרדה מאוד לא טובה בין רכיבים. מקומו של קוד UI הוא ב View ולא ב ViewModel וזו נקודה שאי אפשר להדגיש אותה מספיק.

אז למה ידוע שלא כדאי להשתמש ב CodeBehind?

הבעייתיות עם CodeBehind  היא מאוד פשוטה. קל מאוד לכתוב שם גם לוגיקה. במידה וכותבים שם קוד כלשהו, מאוד בקלות נמצא את עצמנו כותבים שם גם דברים שמקומם לא בView. (ואם לא אנחנו אז מישהו אחר בצוות שלנו). ולכתוב לוגיקה ב View זה רע בדיוק כמו לכתוב UI ב ViewModel. זו הפרדה רעה. (הפרה של עקרון SRP – Single Responsibility Principle).
כתיבת Behaviors מדלגת מעל הבעיה הזו באלגנטיות מרשימה. ב Behaviors די קשה להכניס לוגיקה אפליקטיבית – הקוד של ה Behaviors נוטה להיות UI נטו. בנוסף, אחד הדברים הכיפים ב Behaviors זה שהם נוטים להיות מאוד ניתנים לשימוש חוזר (reusable). את ה Behavior שכתבנו עכשיו מאוד קל לקחת ולחבר גם על מקומות אחרים באפליקציה. אחת הסיבות לכך, אגב, היא ש Behavior מבצע כימוס (encapsulation) לבדיוק אחריות UI אחת.
פיצ’ר שהוא גם חזק וגם מקשה על טעויות ארכיטקטורה? בודאות הפיצ’ר שאני הכי אוהב ב WPF Smile

 


את הקוד המלא ניתן להוריד מכאן

איך מוסיפים עמודות דינאמיות לגריד ב WPF ?

פורסם בתאריך Mar 12 2012, 11:32 AM על ידי eladkatz

שאלה שנשאלה בפורום WPF: איך מגדירים עמודות דינאמיות שלא ידועות בזמן התכנון ב Data Grid של WPF?

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

החלק הסטאטי הוא פשוט – את המחלקה נגדיר כך:

 

public class Station : INotifyPropertyChanged
{
    private int id;
    public int ID
    {
        get { return id; }
        set
        {
            id = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("ID"));
        }
    }
 
    private int name;
    public int Name
    {
        get { return name; }
        set
        {
            name = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Name"));
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
}

ואת הגריד נוכל להגדיר כך:

<DataGrid ItemsSource="{Binding Stations}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" Binding="{Binding ID}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
    </DataGrid.Columns>
</DataGrid>

ואם נחבר את הכל, זה יראה כך:

image

 

אבל איך מוסיפים עמודות בזמן ריצה בצורה דינאמית?

אחת התכונות הפחות מוכרת של Binding ב WPF היא היכולת לקשור לא רק למאפיינים (Properties) רגילים, אלא גם למאפיינים מיוחדים, מסוג מילון (Dictionary). ב WPF, כשמגדירים Binding מספקים Path שיכול לגשת בצורה מלאה למילונים, מה שיאפשר לנו לעבוד בצורה דינאמית לחלוטין.

נוסיף למחלקה מאפיין של אירועים, מסוג מילון:

private Dictionary<int,string> events;
public Dictionary<int,string> Events
{
    get { return events; }
    set
    {
        events = value;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Events"));
    }
}

נוסיף למילון מידע:

private List<Station> GetDummyList()
{
    List<Station> list = new List<Station>();
 
    list.Add(new Station { ID = 1, Name = "First Station" });
    list.Add(new Station { ID = 2, Name = "Second Station" });
    list.Add(new Station { ID = 3, Name = "Third Station" });
    list.Add(new Station { ID = 4, Name = "Forth Station" });
    list.Add(new Station { ID = 5, Name = "Fifth Station" });
 
    for (int i = 0; i < list.Count; i++)
    {
        list[i].Events = new Dictionary<int, string>();
        list[i].Events.Add(1, "Event #1");
        list[i].Events.Add(3, "First #3");
    }
 
    return list;
}

ועכשיו יהיה אפשר לגשת לנתוניםשנמצאים במילון ע”י הגדרת עמודות באופן הבא בגריד:

<DataGridTextColumn Header="dynamic 1" Binding="{Binding Events[1]}" />
<DataGridTextColumn Header="dynamic 2" Binding="{Binding Events[3]}" />

כשכמובן אפשר להוסיף כאלו עמודות גם תוך כדי ריצה בצורה דינאמית:

TheGrid.Columns.Add(new DataGridTextColumn 
{ 
    Header = "From Code", 
    Binding = new Binding("Events[9]") 
});

 

ויצרנו עמודות דינאמיות תוך כדי ריצה!

image

 

איך מבצעים את זה ב MVVM ?

עמודות של גריד לא תומכות ב DataBinding ולכן “מתוך הקוספא” לא נוכל להוסיף עמודות מה ViewModel… אז איך פותרים את זה?
בפוסט הבא נראה איך אפשר להוסיף עמודות בצורה MVVM-ית מלאה, ע”י הפיצ’ר האהוב עלי ביותר Behaviors.

המצגת להרצאה על חלונות 8 וממשק המשתמש מטרו

פורסם בתאריך Feb 24 2012, 05:44 PM על ידי eladkatz

מצורפת המצגת שהראיתי בהרצאה על חלונות 8 וממשק המשתמש מטרו בקהילת המפתחים של דוט נט ביום רביעי -

 

 

לכל מי שהתעניין על ימי הרצאות נוספות בנושא – בקרוב אעביר יום פתוח במיקרוסופט על הנושא, לצד הרבה מאוד הרצאות שנעביר אני ושאר המומחים של סלע בכנס הקרוב. אני ממליץ להתעדכן בבלוג שלי לתאריכים ולפרטים המדוייקים. (דרך קלה לעשות זאת זה להרשם לעדכונים דרך פייסבוק)
דרך נוספת להתעדכן היא כמובן דרך הפורום החדש ב MSDN ישראל שהזכרתי – אני זמין שם לעיתים מאוד קרובות, וזו דרך טובה לשמור איתי על קשר.

כמו תמיד, תודה לכל מי שבא, ותודה מיוחדת לאריאל בן חורש ושי בר-לב על הארגון!

הבטא של ויז'ואל סטודיו11 - UI ו UX חדשים, ופיצ'ר אחד שלא נבין איך הסתדרנו בלעדיו

פורסם בתאריך Feb 24 2012, 03:50 PM על ידי eladkatz

עוד שבוע לשחרור הרישמי של הבטא של חלונות 8, אבל עבורינו המפתחים יהיה שחרור נוסף חשוב לא פחות – הבטא של Visual Studio 11. יש לא מעט שינויים די מגניבים בגרסה החדשה, ופוסט חדש בבלוג של Visual Studio מתאר את החשיבה מאחוריהם, ומבקש פידבק על פיצ’רים שהם הוסיפו.

יצא לי כבר לשחק עם הגרסה החדשה לא מעט (לסלע יש גישה לשחרורים מוקדמים), ויש הרבה דברים שאני מאוד אוהב בגרסה החדשה לצד כמה דברים שאני אוהב פחות.. אז מה חדש?

 

1. הדבר שהכי זועק לעין בגרסה החדשה זה העיצוב:

 

השפה הגראפית שונתה בלא מעט דברים, כשזה אולי בהשראת מטרו (אם כי כמובן ששפת מטרו לא מתאימה לאפליקציות עבודה כמו Visual Studio).
נעשתה המון עבודה על מנת לפשט את ה UI, ולהוריד כרום מיותר (על פי העקרון content before chrome – תוכן לפני “קישוטים” - שאני מאוד בעדו). VS11 מגיע עם שני Themes, אחד בהיר ואחד כהה, כשהכהה הרבה יותר יפה לדעתי, והרבה פחות משעמם:


אם זה מזכיר למישהו את Blend זה לא במקרה. הרבה מהשפה הגראפית אוחדה בין שני הכלים.
ואם כבר מזכירים את בלנד, אז למפתחי ה WPF נכונה הפתעה מאוד מגניבה – Cider – הdesigner של WPF ב Visual Studio, הוחלף ב designer של בלנד שהרבה יותר חזק וטוב. (וסוף סוף יאפשר להגיע ל blendability גבוהה בשני הכלים באותה צורה). מסוג הדברים שאתה אומר עליהם “איך הם לא חשבו על זה קודם” Smile

2. הממשק עצמו כמעט לחלוטין ללא צבעים.
במבט ראשון העיצוב טיפה מונוכרומאטי, אבל הרעיון כאן הוא לאפשר למפתח להתרכז כמה שיותר בתוכן הגראפי שהוא יוצר, ולא בעיצוב של VS שיכול למשוך לו את העין. בגדול אני אוהב את הרעיון, אבל זה נכון רק כאשר אני מעצב דף גראפי כמו XAML. זה לא נכון כאשר אני כותב קוד, ואז המראה באמת טיפה משעמם.
האייקונים עצמם גם כן חסרי צבעים, ובמבט מקרוב זה נראה כך:

בפוסט המקורי טוען מונטי המונטרי – דיירקטור UX בחטיבת הכלים של מיקרוסופט – שבדיקות שהם עשו גילו שמשתמשים, בין אם ותיקים ובין אם חדשים זיהו את האייקונים באותה מהירות שהם זיהו את האייקונים ב VS10. בנוסף, משתמשים חדשים למדו מהר יותר מה כל אייקון עושה בVS11 מאשר בVS10.
אז זה מה שהוא טוען, אבל אני לא רואה איך זה יכול להיות נכון. להרגשתי בפיצ’ר הזה ספציפית הם פיספסו – אני לא מצליח לזהות ככה אייקונים בקלות, היות וכולם נראים לי במבט מהיר מאוד דומים, ואין לי צבע שיעזור לי להבחין ביניהם. לדוגמא כפתור השמירה – האייקון בצורת הדיסקט “1.44 – אני מוצא הרבה יותר מהר באייקונים הצבעוניים מאשר באייקונים החדשים. בנוסף, היות והאייקונים הם 16*16 בלבד, במחשב עם רזולוציה גבוהה היכלות לסרוק ולזהות על פי צבע נהיית יותר קריטית לפי דעתי  (ואני סה”כ עובד ב 1600*900, יש הרבה שעובדים עם רזולוציה גבוהה יותר).

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

 

3. מספר הכפתורים ב Toolbar צומצם משמעותית

לעומת VS10 -

יש פחות כפתורים == יותר קל למצוא את הכפתור הנכון, ויותר חשוב מזה, יותר קל להבין מה כל כפתור עושה, ויותר קל ללמוד את כל הכפתורים.
במידה ויש פחות אופציות, רוב המפתחים יקחו את הזמן לודא שהם מבינים מה עושה כל כפתור, מה שיאפשר חווית שימוש הרבה יותר טובה. יש המון תכונות מאוד שימושיות של VS שמתחבאות איפשהו, וקשה להדגיש מספיק כמה היכרות טובה עם כלי הפיתוח מאפשרת פרודקטיביות גבוהה יותר. לצד הורדה של הרבה מאוד כפתורים שימושיים פחות (הרי בשורה התחתונה Toolbar הוא בעיקר למפתחים פחות מיומנים), מיקרוסופט הוסיפו כפתורים של פונקציות מאוד חשובות כדוגמת הכפתור הראשון והשני מימין.
הכפתורים הללו מאפשרים לקפוץ בין המקומות האחרונים שהיית בהם בקוד, וזו יכולת סופר חשובה שלא הרבה מכירים אותה.
היכולת הזו היתה זמינה כבר המון זמן דרך קיצור הדרך < – > + ctrl בשביל ללכת אחורה ו < + > + ctrl בשביל ללכת קדימה, רק שהרבה מפתחים עדיין לא מכירים את כל קיצורי הדרך החשובים של VS. בגרסה הזו לעומת זאת יש עבור זה כפתור, ובמידה ועומדים מעליו אפשר לראות tooltip שמראה את הקיצור דרך של המקלדת אליו, ופעם הבאה לעשות את זה כבר מהמקלדת.
היכולת של כלי ללמד את המפתח איך לגלות תכונות חדשות (Discoverability של פיצ’רים או קיצורים) לפי דעתי מאוד מאוד חשובה. אני מעביר לעיתים קרובות קורסים או סדנאות למפתחים בעלי ניסיון של הרבה שנים בVS, ואני תמיד מופתע מחדש לגלות כמה מעט אנשים משתמשים בקיצורי דרך. בדרך ברוב הקורסים שאני מעביר אני אדגים משהו בקוד, ותוך כדי כתיבת הקוד אני משתמש בלא מעט קיצורי דרך של המקלדת, כמו לדוגמא ctrl + k + c בשביל להעביר קוד להערה, רק בשביל לגלות שלרוב הכיתה אין מושג איך עשיתי את מה שעשיתי. אז ctrl + k + c אפשר ללמוד כבר ב VS10 מהכלי עצמו במידה ועומדים מעל הכפתור הרלוונטי ב toolbar:

image

אבל את < – > + ctrl לא היתה דרך טובה ללמוד ללא קריאה של מסמכי קיצורי דרך משמימים (שעשיתי לא פעם). הרבה פעמים אני ממליץ למפתחים לקרוא רשימות של קיצורי דרך, אבל לא עוד.  בכל מה שקשור ל – discoverability של פיצ’רים ב VS11  מיקרוסופט עשו עבודה ממש טובה, ואני מעריך שזה ישפר את הפרודקטיביות בצורה מאוד משמעותית.

הפיצ’ר שיאפשר לחפש פקודות בצורה הכי פשוטה (וללמוד גם את קיצורי הדרך שלהן במקביל) הוא פיצ’ר שחסר לי כבר הרבה זמן;

4. Quick Launch עם חיפוש לפקודות ב Visual Studio:
בצד ימין למעלה ממוקם תיבת טקסט (ctrl + Q) עבור חיפוש של כל פקודה ב Visual Studio:

הפיצ’ר הזה מבחינתי הוא קילר-פיצ’ר שחסר כבר הרבה מאוד זמן.
אפשר לדפדף בתוצאות על ידי המקלדת, ולהפעיל המון בלי לגעת בעכבר (כידוע “עכבר זה למעצבים גראפיים”  Smile). בנוסף, קיצורי הדרך גם נראים שם כך שפקודה שלא ידעתי איך להגיע אליה וחיפשתי משם – פעם הבאה אוכל להפעיל אותה ישירות על ידי קיצור דרך של מקלדת. Discoverability כבר אמרנו?

בשביל לחסוך את כל תגובות ה”ברישרפר זה כבר קיים 60 שנה” – רישרפר אכן מוסיף המון פונקצינאליות, אבל לפי דעתי הוא התוכנה הכי פחות קלה ללמידה שיש. יש שם המון יכולות, אבל זה תוסף שמאוד לא סלחן למישהו שמנסה ללמוד איך לעבוד איתו, וכאן VS, לצד הפיצ’רים שהוא לוקח מרישרפר, עושה לו בית ספר בכל מה שקשור לחווית משתמש.

החיפוש עצמו לא מתבצע תוך כדי הקלדה, אלא VS מחכה שנעצור את ההקלדה לחצי שניה, ורק אז מציג לנו את התוצאות. לא ברור לי למה רשימה מאונדקסת לא יכולה להיות מוצגת הרבה יותר מהר תוך כדי הקלדה, ואני מקוה שעד לשחרור של הגרסה הסופית זה יתוקן.

 

5. אפשרות לחפש בהרבה מאוד חלונות-כלים:
החלונות הבאים תומכים בחיפוש-תוך-כדי הקלדה:

  • SOLUTION EXPLORER
  • REFERENCE MANAGER
  • TEAM EXPLORER
  • INTELLITRACE SUMMARY PAGE
  • TOOLBOX
  • PARALLEL WATCH WINDOW
  • ERROR LIST
  • C++ GRAPHICS EVENT LIST
  • CODE ANALYSIS

כפי שאפשר לראות:

ספציפית ב Solution Explorer, לצד האפשרות לחפש שמות של קבצים, אפשר גם לחפש מחלקות בתוך הקבצים. מגניב ביותר!

צוות הפיתוח של VS מקבל פידבקים דרך הבלוג שלו – אם חסר לכם משהו – זה הזמן להשפיע.

More Posts Next page »