• Commands with Attached Behavior for Silverlight 3 DataForm

    Published by on April 18th, 2009 4:06 am under Composite Application Guidance for WPF & SL, p&p, Patterns & Practices, Prism-v2, Silverlight, Silverlight 3

    6 Comments

    Some weeks ago, due to yet another question from the Composite WPF & Silverlight Codeplex forums, I found myself researching about the Silverlight 3 DataForm control. I turned to this video which allowed me to understand the basics of how it worked really fast.

    The user wanted to execute a command in his view model when an item of the DataForm had been edited.

    This post will not focus on explaining what this control allows (for this I really recommend the video), but focus on the implementation required instead.

    Attached Behavior Saves the Day

    So I checked the documentation and asked Julian Dominguez (the attached behavior guy), and Matias Woloski (an expert in the client development field) if attached behaviors was the way to go. And of course they all pointed my in that direction.

    I took the Click class used in Prism-v2 and turned it into the ItemEditEnded class:

    public static class ItemEditEnded
       {
           private static readonly DependencyProperty ItemEditEndedCommandBehaviorProperty
               = DependencyProperty.RegisterAttached(
               "ItemEditEndedCommandBehavior",
               typeof(DataFormItemEditEndedCommandBehavior),
               typeof(ItemEditEnded),
               null);
    
           public static readonly DependencyProperty CommandProperty
               = DependencyProperty.RegisterAttached(
               "Command",
               typeof(ICommand),
               typeof(ItemEditEnded),
               new PropertyMetadata(OnSetCommandCallback));
    
           public static readonly DependencyProperty CommandParameterProperty
               = DependencyProperty.RegisterAttached(
              "CommandParameter",
              typeof(object),
              typeof(ItemEditEnded),
              new PropertyMetadata(OnSetCommandParameterCallback));
    
           public static ICommand GetCommand(DataForm dataForm)
           {
               return dataForm.GetValue(CommandProperty) as ICommand;
           }
    
           public static void SetCommandParameter(DataForm dataForm, object parameter)
           {
               dataForm.SetValue(CommandParameterProperty, parameter);
           }
    
           public static object GetCommandParameter(DataForm dataForm)
           {
               return dataForm.GetValue(CommandParameterProperty);
           }
    
           private static void OnSetCommandCallback
               (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
           {
               DataForm dataForm = dependencyObject as DataForm;
               if (dataForm != null)
               {
                   DataFormItemEditEndedCommandBehavior behavior = GetOrCreateBehavior(dataForm);
                   behavior.Command = e.NewValue as ICommand;
               }
           }
    
           private static void OnSetCommandParameterCallback
               (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
           {
               DataForm dataForm = dependencyObject as DataForm;
               if (dataForm != null)
               {
                   DataFormItemEditEndedCommandBehavior behavior = GetOrCreateBehavior(dataForm);
                   behavior.CommandParameter = e.NewValue;
               }
           }
    
           private static DataFormItemEditEndedCommandBehavior GetOrCreateBehavior(DataForm dataForm)
           {
               DataFormItemEditEndedCommandBehavior behavior =
                   dataForm.GetValue(ItemEditEndedCommandBehaviorProperty) as DataFormItemEditEndedCommandBehavior;
               if (behavior == null)
               {
                   behavior = new DataFormItemEditEndedCommandBehavior(dataForm);
                   dataForm.SetValue(ItemEditEndedCommandBehaviorProperty, behavior);
               }
               return behavior;
           }
       }

    Then I created the class that would hook the DataForm’s event to the command DataFormItemEditEndedCommandBehavior:

    public class DataFormItemEditEndedCommandBehavior : CommandBehaviorBase<DataForm>
        {
            public DataFormItemEditEndedCommandBehavior(DataForm dataForm)
                : base(dataForm)
            {
                dataForm.ItemEditEnded += OnItemEditEnded;
            }
    
            private void OnItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
            {
                ExecuteCommand();
            }
        }

    As Julian explained: The first class is the one needed to create the attached behavior when the Xaml parser creates the UserControl. His blog post, ICommand for Silverlight with Attached Behaviors, uses a similar approach to the code that end up in the CAL (although it is not exactly the same), and explains how the Attached Behavior pattern works.

    Putting things to work

    Now I had to test that this worked correctly. With that in mind, I took the HelloWorld Quickstart and turned it into a little application with a DataForm inside its only view. To test the scenario, the command would add a new item to a list of customers which was databound to the DataForm, thus giving it an extra “page”.

    You can download the code from here. Since this was originally the HelloWorld Quickstart from Prism-v2, I made some minor changes to it to make the application easier to understand.

    Disclaimer: This code is provided “AS IS” with no warranties, and confers no rights.

    I hope you can find good use to this.

    kick it on DotNetKicks.com

    Tags: , , , , ,

    • Chad

      Very helpful article, however how do I get access to the DataFormItemEditEndedEventArgs property in my ViewModel?

      Eg when this event fires, I only want to do the action if:
      e.EditAction = DataFormEditAction.Commit,

      and do something else if the EditAction is Cancel

    • Damian Schenkelman

      Hi Chad,

      Thanks for your feedback, really appreciate it.

      On to your question, you could change the OnItemEditEnded method from the DataFormItemEditEndedCommandBehavior class to get that required functionality. The outcome would be something like:
      private void OnItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
      {if (e.EditAction == DataFormEditAction.Commit){ExecuteCommand();}}

      Remember that once ExecuteCommand is called, the command will be executed, so any validation on whether to execute the command or not should be performed before.

      I hope that helps.

      Damian

Archives

Categories