DCSIMG
February 2011 - Posts - אלעד כץ | Elad Katz
Sign in | Join | Help

אלעד כץ | Elad Katz

לגו של גדולים

February 2011 - Posts

Faking Forms Authentication in Silverlight

פורסם בתאריך Feb 15 2011, 12:14 AM על ידי eladkatz

In a website that implements Forms Authentication Silverlight fits in very easily – Forms Authentication leaves a cookie on the client – and Silverlight just picks it off if you’re using the Browser Stack (which you normally would). In other words – It simply works.

However, many times you would like to do the authentication yourself – that is – to fake an HTTP Post so that you can authenticate to a server without having the user actually fill the form and submit it – you’d like to do it programmatically in Silverlight.

My first thought was doing the following:

   1: WebClient wc = new WebClient();
   2:  
   3: this.completed = Completed;
   4:  
   5: wc.UploadStringCompleted += new UploadStringCompletedEventHandler(wc_UploadStringCompleted);
   6:  
   7: wc.UploadStringAsync(new Uri("http://MyServer/login", UriKind.Absolute),
   8:     String.Format("username={0}&password={1}", user, pass));

 

Using WebClient, we can call UploadStringAsync to upload a list of parameters (those usually would be the form fields – in this case it’s username and password), and thus fake the HTTP Post of the form.

It worked like a charm for a few hours, and then one of the programmers in the team started getting Access Denied from the server. It took me quite some time to realize why… (and a lot of fiddling with fiddler)

I took snapshots of the http activity on my computer and on the computer of the other programmer, only to find out the my computer was creating a slightly different web-request. It was adding content-type header:

content-type: application/x-www-form-urlencoded

 

Turns out that Chrome by default adds this header, whereas IE8 does not. Weird..

I’m pretty sure that IE is the one that’s correct. Chrome just added this header all by itself – It was supposed to be rejected because we have to add this ourselves.

All you need to do is to add the following line:

   1: wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";

and it works.

 

I’m actually not sure why the server is checking this, but in our case (Spring over Java over Apache) it was.

The full code looks like this:

   1: public class FormsAuthenticationHelper
   2: {
   3:     Action completed = null;
   4:  
   5:     public void Authenticate(string user, string pass, Action Completed)
   6:     {
   7:         WebClient wc = new WebClient();
   8:  
   9:         this.completed = Completed;
  10:  
  11:         wc.UploadStringCompleted += new UploadStringCompletedEventHandler(wc_UploadStringCompleted);
  12:  
  13:         // hack for IE8. Chrome does this automatically
  14:         wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
  15:  
  16:         wc.UploadStringAsync(new Uri("http://localhost:58289/login", UriKind.Absolute),
  17:             String.Format("j_username={0}&j_password={1}", user, pass));
  18:     }
  19:  
  20:     void wc_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
  21:     {
  22:         if (this.completed != null)
  23:             this.completed();
  24:  
  25:     }
  26:  
  27: }

SDP 2011 - ההרצאה שלי על MVVM וארכיטקטורת תוכנה

פורסם בתאריך Feb 14 2011, 11:13 PM על ידי eladkatz

 

ב13-16 למרץ יתקיים כנס מקצועני התוכנה השנתי של סלע עם המון הרצאות מרתקות מהמומחים של סלע, במגוון רחב של תחומים.

ההרצאה שלי – MVVM – The Holy Grail of UI Architecture תתקיים ב16.3 ותתמקד בDesign Pattern הכי מדובר בשנה האחרונה לאפליקציות חכמות ב WPF וב Silverlight.

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

תצביעו להרצאה ואולי תקבלו כרטיסים חינם!

 

לצד ההרצאות (כ-30 הרצאות!) יהיו 8 מסלולים של ימי פיתוח מרוכזים שירדו לעומק מקסימאלי בכל נושא:

בתחום WPF / Silverlight מתוכנן יום שלם של ירידה עמוקה לקוד, עם תומר שמם, אלעד שחם, נעמה רז-ליבנה, ואיתי.

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

MVVM: How to show a dialog box from the ViewModel using Behaviors

פורסם בתאריך Feb 06 2011, 07:19 PM על ידי eladkatz

Behaviors seems like the killer feature for every possible problem we used to have with MVVM. Thank god for WPF4 Smile

Showing a Message Box is one of those navigational problems that comes with MVVM. It’s something that we want the ViewModel to control, but yet we don’t want the ViewModel to contain any hard reference to the View.

My solution uses the Messenger class from MVVMLight toolkit, but can easily be adjusted to use Prism’s EventAggregator.

Lauren Bugnion suggested a solution a while ago which is to send a message to the view’s CodeBehind, and from there to open a message box like so:

   1: public MainPage() //Yuck code behind.
   2: {
   3:     InitializeComponent();
   4:  
   5:     Messenger.Default.Register<DialogMessage>(
   6:         this,
   7:         msg =>
   8:         {
   9:             var result = MessageBox.Show(
  10:                 msg.Content,
  11:                 msg.Caption,
  12:                 msg.Button);
  13:  
  14:             // Send callback
  15:             msg.ProcessCallback(result);
  16:         });
  17: }

 

It’s important to say that the solution is basically good, and that the separation of layers is kept.

 

It is, however, far from elegant. It uses code behind, and it is not very reusable. That’s where behaviors come into play – The perfect solutions to such problems. What we have to do is create a Custom MessageBox Behavior that could address that.

 

The Custom Behavior

Behaviors are really easy to implement, all you have to do is to implement Behavior<T>, and override the OnAttached and OnDetached:

   1: class DialogBehavior : Behavior<FrameworkElement>
   2: {
   3:     Messenger messenger = Messenger.Default;
   4:  
   5:     protected override void OnAttached()
   6:     {
   7:         base.OnAttached();
   8:  
   9:         messenger.Register<GalaSoft.MvvmLight.Messaging.DialogMessage>(this, Identifier, ShowDialog);
  10:     }
  11:  
  12:     public string Identifier { get; set; }
  13:     public string Caption { get; set; }
  14:     public string Text { get; set; }
  15:     public MessageBoxButton Buttons { get; set; }
  16:  
  17:     private void ShowDialog(GalaSoft.MvvmLight.Messaging.DialogMessage dm)
  18:     {
  19:         var result = MessageBox.Show(Text, Caption, Buttons);
  20:  
  21:         dm.Callback(result);
  22:     }
  23:  
  24: }

In order to use it we have to put it anywhere in the view, but putting it in the top makes more sense:

   1: <i:Interaction.Behaviors>
   2:     <local:DialogBehavior Caption="MyCap" Text="MyText" Buttons="YesNoCancel" Identifier="mb1"  />
   3:     <local:DialogBehavior Caption="MyCap2" Text="MyText2" Buttons="YesNo" Identifier="mb2"  />
   4: </i:Interaction.Behaviors>

Now comes the cool part. The usage from the ViewModel is as simple as it gets:

   1: Messenger mes = Messenger.Default;
   2:  
   3: mes.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage("Hey", res =>
   4: {
   5:     Debug.WriteLine(res.ToString());
   6: }), "mb1");
   7: mes.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage("Hey", res =>
   8: {
   9:     Debug.WriteLine(res.ToString());
  10: }), "mb2");

 

Using the messenger we send a weak message. The DialogBehavior shows a Dialog based on the how we set it in the view, and the response comes back to the ViewModel. Elegant, isn’t it?

The full code can be found here

איך מממשים MVVM ? זה הרבה יותר פשוט ממה שחושבים.

פורסם בתאריך Feb 04 2011, 05:55 PM על ידי eladkatz

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

בפוסט הקודם דיברתי על הסיבות לממש MVVM, ועל הפואנטה שעומדת מאחורי הDesignPattern הזה. העליתי גם שאלה – מה גורם להפרדה של XAML ו Code Behind לא להוות הפרדה מספיק טובה.

אז האמת שההפרדה של XAML ו CodeBehind כבר לא ממש רעה, רק שהבעיה היא שהם לא ממש מופרדים – הם למעשה מהווים ביחד מחלקה אחת, ולכן שינויים באחד מהם לעיתים מאוד קרובות "ישברו" את השני. (ה Coupling כאן גבוה, ואת זה נרצה לשנות).

כל הרעיון מאחורי MVVM זה פשוט לקחת את הCodeBehind ולהוציא אותו למחלקה נוספת, שנקרא לה ViewModel, וכן תהייה לנו הפרדה חזקה.ה View יהיה הXAML, והלוגיקה תשב במחלקה נפרדת לחלוטין.

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

הבעיה היחידה היא כמובן איך מתבצעת שליטה של שכבת הלוגיקה (ה ViewModel) על שכבת התצוגה (ה View) אם הן שתי מחלקות נפרדות, ש"לא מכירות" אחת את השניה? כאן נכנסים שני פיצ'רים מאוד חזקים של WPF ו Silverlight – Binding – שעביר מידע בין הViewModel לבין הView, ו Commands, שיעבירו פקודות מהView לViewModel. זה הרבה יותר פשוט ממה שזה נשמע.

בשביל שהמחלקות יכירו אחת את השניה בצורה "חלשה", נקבע את מחלקת הViewModel להיות הDataContext של מחלקת הView. התקשורת בין שתי המחלקות תעבור לפיכך, ע"י שימוש בBinding וCommands.

אז תכלס, איך מממשים את זה?

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

 

screens

 

לממש את זה בדרך הרגילה של XAML ו CodeBehind די פשוט.ניצור UserControl חדש שיכיל את כל הUI, ונקרא לו ListOfProducts.

ה XAML שלנו יראה בערך כך:

 

   1: <UserControl x:Class="WpfApplication21.ListOfProducts"
   2:              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   4:     <Grid>
   5:         <Button Content="Show Products" VerticalAlignment="Top" Click="Button_Click" />
   6:         <ListBox Margin="0,23,0,0" DisplayMemberPath="Name" x:Name="MyListBox">
   7:         </ListBox>
   8:     </Grid>
   9: </UserControl>

 

והCode Behind היה נראה כך:

 

   1: public partial class ListOfProducts : UserControl
   2: {
   3:     public ListOfProducts()
   4:     {
   5:         InitializeComponent();
   6:     }
   7:  
   8:     private void Button_Click(object sender, RoutedEventArgs e)
   9:     {
  10:         DummyService service = new DummyService();
  11:         var products = service.GetAllProducts();
  12:  
  13:         MyListBox.ItemsSource = products;
  14:     }
  15: }

 

ניקח את המסך הזה כדוגמא, ונהפוך אותו מXAML ו CodeBehind, ל View ו ViewModel:

כרגע ישנה תקשורת בין הXAML ל CodeBehind ובין הCodeBehind לבין הXAML.

בשביל להעביר את האפליקציה הזו לMVVM, נבצע שלושה דברים: ניצור מחלקת ViewModel, נרשום את הViewModel כ DataContext של ה View, ונקשר את הכל ע"י Binding.

1. יצירת ה ViewModel

ניצור מחלקה חדשה, ונקרא לה ListOfProductsViewModel. (כסוג של סטנדרט, מחלקות ViewModel מסתיימות בViewModel, בעוד מחלקות של View מסתיימות בView.)

המחלקה הזו צריכה למעשה להחליף את הCodeBehind. לכן היא צריכה להכיל שני חלקים חשובים-

1. לחשוף Property מסוג List<Product> - רשימת המוצרים - בשביל שהView יוכל להציג.

2. לחשוף Command שיאפשר לView להרים כאשר הכפתור נלחץ:

 

   1: // The ViewModel class. Implements INotifyPropertyChanged to help DataBinding.
   2: public class ListOfProductsViewModel : INotifyPropertyChanged
   3: {
   4:     // The Products property.
   5:     private ObservableCollection<Product> products;
   6:     public ObservableCollection<Product> Products
   7:     {
   8:         get { return products; }
   9:         set 
  10:         { 
  11:             products = value;
  12:             if (PropertyChanged != null)
  13:                 PropertyChanged(this, new PropertyChangedEventArgs("Products"));
  14:         }
  15:     }
  16:  
  17:     // Property for the Command. (RelayCommand is a helper class that wraps ICommand).
  18:     public RelayCommand ShowProductsCommand { get; set; }
  19:  
  20:     // Reference to the dummy service.
  21:     DummyService service;
  22:  
  23:     // ctor
  24:     public ListOfProductsViewModel()
  25:     {
  26:         // Create a new product service.
  27:         service = new DummyService();
  28:  
  29:         // initialize the command
  30:         ShowProductsCommand = new RelayCommand(ShowProducts);
  31:     }
  32:  
  33:     void ShowProducts()
  34:     {
  35:         var productList = service.GetAllProducts();
  36:  
  37:         this.Products = new ObservableCollection<Product>(productList);
  38:     }
  39:  
  40:  
  41:  
  42:     public event PropertyChangedEventHandler PropertyChanged;
  43: }

 

הסבר:

1. מחלקת הViewModel מכינה את המידע (הProperties) ואת הפעולות (Commands) עבור מחלקת הView (הUserControl שיציג את ה UI ).

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

3. אפשר להסתכל על הViewModel כעל CodeBehind שלא מכיל שום קישור ישיר לXAML. הViewModel בדר"כ יהווה את כל ה"תוכן" של האפליקציה, רק בלי להציג אותה. כל המידע, הלוגיקה, והמצב (State) של האפליקציה ימצא ב ViewModel. הView רק יציג, ולא שום דבר מעבר לזה.

4. הCommand שאנו חושפים הוא מסוג RelayCommand שזו פשוט מחלקה שמממשת את ICommand. המחלקה הזו מגיע מספריית עזר בשם MVVMLight, אבל היינו יכולים ליצור את זה גם בעצמינו. עוד על כך בהמשך.

2. קישור הViewModel לView

עכשיו שיש לנו מחלקה שמחליפה את הCodeBehind, אפשר למחוק משם את כל הקוד. תחת MVVM אנחנו תמיד נשאף למצב בו אין לנו בCodeBehind שום קוד בכלל. כרגע, כל מה שנרצה לשים ב Code Behind זה אך ורק את הקישור לViewModel:

   1: public partial class ListOfProductsView : UserControl
   2: {
   3:     public ListOfProductsView()
   4:     {
   5:         InitializeComponent();
   6:  
   7:         this.DataContext = new ListOfProductsViewModel();
   8:     }
   9: }

 

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

3. יצירת הBinding בתוך הView

כל מה שנותר לנו לעשות זה לחבר את הBinding בתוך הView. לפיכך נערוך את הXAML כך שיראה כך:

 

   1: <UserControl ...>
   2:     <Grid>
   3:         <Button Content="Show Products" VerticalAlignment="Top" Command="{Binding ShowProductsCommand}" />
   4:         <ListBox Margin="0,23,0,0" DisplayMemberPath="Name" x:Name="MyListBox" ItemsSource="{Binding Products}">
   5:             
   6:         </ListBox>
   7:     </Grid>
   8: </UserControl>

 

 

וזהו. יש לנו את אפליקציית הMVVM הפשוטה ביותר בעולם. זה כל ה Design Pattern, וכאמור הוא די פשוט.

אז למה יצא לMVVM שם של ארכיטקטורה מורכבת? מה הקושי ב MVVM ?

האפליקציה שבנינו כאן היא די פשוטה, ויש לא מעט סיטואציות בהן יהיה לנו קשה לבצע הכל ע"י סה"כ Binding ו Commands. הסיטואציות הבעייתיות הקלאסיות הן בדר"כ:

1. מימוש UX מתוחכם, בו יהיה לנו קשה לבצע הכל מהViewModel . כדוגמא, מימוש של Drag and Drop, וכו'.

2. בעייתיות בCommands – Commands בWPF וSilverlight זה פיצ'ר טיפה בעייתי. יש מעט מאוד קונטרולים שיודעים להרים Command, וגם הם רק לClick . איך אני תופס אירוע כמו MouseMove או TextChanged ב MVVM ?

3. בעיות ניווט בין Views שונים – בעיית הניווט (Navigation) היא בעייה תמיד כשכותבים אפליקציות, וב MVVM היא מוחרפת. מעבר בין מסך למסך זה משהו שהיינו רוצים לשלוט עליו בViewModel , אבל לViewModel אסור לגעת ישירות בView ואי אפשר לעשות את זה עם Binding בלבד... אז איך מבצעים Navigation?

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

על כך, בפוסטים הבאים.

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

fix: SatisfyImports not working correctly in Prism over MEF

פורסם בתאריך Feb 04 2011, 04:18 PM על ידי eladkatz

I’m working on a Silverlight project under Prism/MEF, and that’s the first time I’m using MEF and not Unity in Prism. Resolving dependencies works a bit differently with MEF, and so when I tried to resolve IRegionManager in a ViewModel class my first instinct was to do the following:

   1: [Import]
   2: public IRegionManager regionManager;
   3:  
   4: public ViewModelConstructor()
   5: {
   6:     CompositionInitializer.SatisfyImports(this);
   7: }

I’m creating my view models using the ViewModelLocator pattern (and not directly using MEF) since IMO it’s a much better approach. (More on that on a later post)

At first it seemed like it was working, and that MEF was actually supplying me with the region manager, only… the Navigation didn’t work. It took me a few minutes to realize that the problem was not of the RegionManager (As there are so many things that can get wrong there…), but it was actually MEF’s doing.

Turns out that the region manager I was getting was a brand new instance, and not the instance that the MefBootstrapper created.. and that was because the underlying IOC Container in MEF was actually not the same.

MefBootstrapper creates a MEF Container, but, for some reason doesn’t register it as the default one. When I later do SatisfyImports, MEF doesn’t know of any container, and thus creates a new container => and a new RegionManager if I ask it of one.

The solution is rather simple, all you have to do is to add the following to the to the method InitializeShell in the bootstrapper:

   1: protected override void InitializeShell()
   2: {
   3:     base.InitializeShell();
   4:  
   5:     // The added line. Why this is not part of Prism's documentation is really beyond me :-/
   6:     CompositionHost.Initialize(this.Container);
   7:  
   8:     Application.Current.RootVisual = (UIElement)this.Shell;
   9: }

and voila, it’s working as expected.

CompositionHost.Initialize(..) registers the IOC container to use if you later do CompositionInitilizer.SatisfyImports().

I can’t really understand why MEF is using two different classes here. CompositionHost != CompositionInitializer. It seems like really bad design, but that’s just the way it is.

I’d expect this line to already be in Prisms documentation and in the Quick Starts, but it’s not.

 

and of course, kudos to the MEF master, Bnaya Eshet for helping me in tracking the issue Smile