MVVM Part 6 – more about caliburn micro FrameWork

30 בJanuary 2013

3 תגובות

בפוסט הקודם הצגנו בצורה בסיסית
את ה
FrameWork של Caliburn Micro ואת הדרך ליצור אפליקציה בסיסית.

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

בכל מקרה אחרי שעשינו
hello world בשיטתMicro  Caliburn ונפגשנו עם המושג BootStrapprer בואו נתקדם למשהו רציני יותר.

נראה מה זה IOC בשיטת Caliburn, נראה איך עושים actions, Events

*הערה: נדרשת הבנה
מוקדמת על
data
Binding
, על WPF-command, ועל Events לפני שנוכל להבין מה קורה כאן.

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

בקלאסים של NET. או למשל בהשוואה ל MVVM-LIGHT הבסיסי שהצגתי בפוסטים
הקודמים.

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


PropertyChanged(this, new PropertyChangedEventArgs(“MembersCounter”));

אלא פשוט,

NotifyOfPropertyChange(() => MembersCounter);

Actions

חשוב לזכור  שMVVM  מתאפיין בין היתר בשימוש בCommands בין השאר כדי לשלוח פקודות כמו מה view ל ViewModel. הארכיטקטים של Caliburn החליטו לקחת את זה צעד קדימה, הם ויתרו על
השימוש ב
CommandBinding שאנו מכירים מתוך הxaml ולעומתו בחרו להטמיע את השימוש בספריית interactivity.   

זאת הספרייה המיוחדת שמגיעה בextension לWPF
ונותנת לנו את כל היכולות החסרות בסטנדרט.
Behaviors,Triggers,Interactions . וכל הדברים שאפשר לעשות
בשימוש ב
Expression
Blend
.
כדי לבדוק שאנו מסוגלים להשתמש ב
DLL
ננסה את הדבר הבא: נפתח פרוייקט
WPF   נוסיף לו מהרפרנס של דוט נט את הDLL  הבא:

System.Windows.Interactivity ובקוד הxaml  שלנו
נוסיף
using לDLL או מה שנקרא XMLNS, בקיצור נוסיף שורה בזאת:

 xmlns:i="clr-namespace:System.Windows.Interactivity; 
                                      assembly=System.Windows.Interactivity"

בחזרה לעניין, אנו צריכים גם את השורה הבאה בתוך ה xaml כדי להשתמש ביכולות של caliburn micro

              xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"

עכשיו בוא נניח שיש
לנו פונקציה פשוטה ב
ViewModel ונרצה להפעיל אותה מתוך הView שלנו, פשוט נכתוב את הקוד הבא:

        <Button>
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="func1">
                    </cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>

אנו רואים שימוש ב Interaction – trigger כדי להפעיל את הפונקציה, אני אישית לא אוהב
את הקוד הזה כי הוא ארוך מדי עבור פעולה בסיסית כזאת , אבל בכל זאת למה הם בחרו
בצורה הזאת כדי להפעיל פונקציה במקום באמצעות
Command שאנו רגילים לראות בעבודה עם xaml?

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

שימו לב בקוד xaml שמעל לשימוש בactionMessage שמתחבר
לפונקציה המתאימה ב
ViewModel באמצעות MethodName, ישנו כמובן רווח בשיטה הזאת שכן בMVVM  “רגיל” שנרצה
לקשור
Command לכפתור, זה נחמד אבל אכן command אין לכל פקד חוץ מבודדים ואז אנחנו עוברים לשימוש בInteractions דומים לשיטה הבסיסית של caliburn, מה שאצלם נחסך מאתנו וישנה אחידות בין הפעולות.

להזכירכם ברגע שנשמור
על
NamingConvention ייווצר עבורינו אוטומטית הקשר בין הView  לviewModel ושם תחפש עבורינו המערכת את הפונקציה המתאימה שעונה לשם func1 במקרה הספציפי הזה.

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

public void func2(string
msg)

       {

          MessageBox.Show(“Test”
+ msg);

       }

אז כך ייראה הxaml שלי:

         <Button>

            <i:Interaction.Triggers>

                <i:EventTrigger EventName=”Click”>

                   
<cal:ActionMessage MethodName=”func2″>

                      
<cal:Parameter Value=”hello”></cal:Parameter>

                   
</cal:ActionMessage>

                </i:EventTrigger>

            </i:Interaction.Triggers>

        </Button>

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

        <Button>

            <i:Interaction.Triggers>

                <i:EventTrigger EventName=”Click”>

                   
<cal:ActionMessage MethodName=”func2″>

                      
<cal:Parameter Value=”{Binding ElementName=txt, Path=Text}”></cal:Parameter>

                   
</cal:ActionMessage>

                </i:EventTrigger>

            </i:Interaction.Triggers>

        </Button>

        <TextBox x:Name=”txt” />

   מה לגבי שליחת הAction ל ViewModel שונה מהViewModel לנו? זאת אומרת הרי כברירת מחדל כל הBindings והActions מופנים לdataContext הנוכחיים.. הדבר רלוונטי הרבה פעמים בMVVM   כשיש לנו נניח Grid שנמצא בתוך stackPanel ולGrid ישנו אובייקט ViewModel משלו כי הוא מייצג הרבה
לוגיקה נפרדת. במקרה כזה אנחנו נשתמש ביכולות המובנות של
WPF , בוא נניח שיש לי קוד כזה:

<StackPanel>

        <Button Content=”press
me”>

            <i:Interaction.Triggers>

                <i:EventTrigger EventName=”Click”>

                   
<cal:ActionMessage MethodName=”func2″>

                       
<cal:Parameter Value=”{Binding ElementName=txt}”></cal:Parameter>

                       

                   
</cal:ActionMessage>

                </i:EventTrigger>

            </i:Interaction.Triggers>

        </Button>

        <TextBox x:Name=”txt”>

        </TextBox>

        <Grid Width=”100″ Height=”100″ DataContext=”{StaticResource vm}”>

            <Button  Content=”press
me 2″
Height=”20″ cal:Action.TargetWithoutContext=”{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}” >

                <i:Interaction.Triggers>

                    <i:EventTrigger EventName=”Click”>

                       
<cal:ActionMessage MethodName=”func3″>

                       
</cal:ActionMessage>

                   
</i:EventTrigger>

                </i:Interaction.Triggers>

            </Button>

        </Grid>

    </StackPanel>

נשים לב שיש לי אלמנט
מסוג
stackPanel ובתוכו כפתור ובנוסף אלמנט מסוג grid רק מה שחשוב זה שיש לו datacontext משלו.

עכשיו נרצה לפנות בכל
זאת מתוך הכפתור הפנימי שנמצא ב
grid לפונקציה חיצונית שהיא לא
חלק מה
datacontext של הgrid, זה יכול להיות כל קלאס
אחר במערכת, ספציפית במקרה הזה אני פונה לפונקציה של ה
datacontext החיצוני (stackPanel) ומשתמש בטכניקה cal:Action.TargetWithoutContext
,
זה אומר שאני מחפש פונקציה מחוץ לdatacontext הנוכחי.

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

 

EventAggregator  הדבר הבא הוא העברת מסרים בין ViewModels כזכור אין לנו קשר ישיר בין ViewModel, כי זה בניגוד לחוקי הארכיטקטורה. אז caliburn מגיע עם תשתית events רחבה,

ואני אדגים את השימוש הבסיסי ביותר.

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

אני פשוט מגדיר אובייקט נניח כך:

public class Message

    {

        public string content=“”;

 

        public Message(string msg)

        {

            content = msg;

        }

    }

ואז נגדיר אינטרפייס handler
משלנו שיורש מהאינטרפייס הכללי
IHandel
(כן.. אינטרפייס יכול לרשת מאינטרפייס) וייראה כך:

public interface IHandle<Messge>
: IHandle

    {

        void Handle(Message message);

    } 

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

  public
class HelloWorldViewModel:
PropertyChangedBase,IHandle<Message>

   {

     public void
Handle(Message message)

       {

          MessageBox.Show(message.content);

       }

   }

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

אז את ה
EventAggregatorנקבל באמצעות שירות IOC  של caliburn Micro בצורה הבאה:

 public class HelloWorldViewModel    

   {

     private EventAggregator eventAggregator;                                                                                  

     public HelloWorldViewModel()

       {

           eventAggregator = IoC.Get<EventAggregator>();

       }

   }

 

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

שליחת מסר זה באמצעות
אותו
eventaggregator בדרך הפשוטה הבאה:

  public
void func3()

       {

          
eventAggregator.Publish(new Message(“hello
caliburn”
));

       }

ורישום מתבצע באותו
אובייקט שנרשם כמימוש של
 Ihandle

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

eventAggregator.Subscribe(this);

זאת אומרת אני רושם את עצמי לאירועים שיבואו מה eventaggregator ובגלל ששינינו את המימוש הבסיסי ל IHandle<Messge> אני אקבל רק הודעות מסוג זה.

בנוסף המימוש של חוזה
מסוג
IHandle<Messge> מביא איתו את פונקציית המימוש  האוטומטית   (
Public void Handle( Message message

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

Leave a Reply to יואב Cancel reply

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

3 תגובות

  1. יואב31 בJanuary 2013 ב 6:11

    מעולה! גדול , בדיוק חיפשנו מידע בעברית

    Reply
  2. jho kim31 בJanuary 2013 ב 6:12

    ?? you again

    Reply
  3. jocker31 בJanuary 2013 ב 6:13

    אחלה . עזר מאוד

    Reply