Prism 4 – Silverlight and Custom Commands

Silverlight 4 includes commanding support for ButtonBase classes, but for anything else we would need to write a little bit of code.

There is a nice section here on how to extend this to other situations. Here is a quick sample on how to create command support for MouseLeftButtonDown on an UIElement. You can download the code from here.

Here is how we can use the Command behavior we create for invoking a command on the mouseleftbutton up event

<UserControl x:Class=”SLPrismCustomCommands.MainPage”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:d=”http://schemas.microsoft.com/expression/blend/2008
    xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006
    mc:Ignorable=”d”
             xmlns:local=”clr-namespace:SLPrismCustomCommands”
    d:DesignHeight=”300″ d:DesignWidth=”400″>

    <Grid x:Name=”LayoutRoot” Background=”White”>
        <Ellipse Fill=”Green” Width=”20″ Height=”20″
                 local:Click.CommandParameter=”{Binding RelativeSource={RelativeSource Self}}”
                 local:Click.Command=”{Binding MyCommand}”/>
    </Grid>
</UserControl>

The following would create a behavior

  public class MouseLeftButtonDownBehavior : CommandBehaviorBase<UIElement>
    {
        public MouseLeftButtonDownBehavior(UIElement obj)
            : base(obj)
        {
            if (obj == null) throw new System.ArgumentNullException(“object cannot be null”);
            obj.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseButton);
        }

        private void OnMouseButton(object sender, System.Windows.RoutedEventArgs e)
        {
            ExecuteCommand();
        }
    }

Here is the class for the Attached properties

public static class Click
    {
        private static readonly DependencyProperty ClickCommandBehaviorProperty = DependencyProperty.RegisterAttached(
            “ClickCommandBehavior”,
            typeof(MouseLeftButtonDownBehavior),
            typeof(Click),
            null);
        /// <summary>
        /// Command to execute on click event.
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            “Command”,
            typeof(ICommand),
            typeof(Click),
            new PropertyMetadata(OnSetCommandCallback));

        /// <summary>
        /// Command parameter to supply on command execution.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
            “CommandParameter”,
            typeof(object),
            typeof(Click),
            new PropertyMetadata(OnSetCommandParameterCallback));
        /// <summary>
        /// Sets the <see cref=”ICommand”/> to execute on the click event.
        /// </summary>
        /// <param name=”buttonBase”>UIElement dependency object to attach command</param>
        /// <param name=”command”>Command to attach</param>       
        public static void SetCommand(UIElement element, ICommand command)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            element.SetValue(CommandProperty, command);
        }

        /// <summary>
        /// Retrieves the <see cref=”ICommand”/> attached to the <see cref=”ButtonBase”/>.
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase containing the Command dependency property</param>
        /// <returns>The value of the command attached</returns>       
        public static ICommand GetCommand(UIElement element)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            return element.GetValue(CommandProperty) as ICommand;
        }

        /// <summary>
        /// Sets the value for the CommandParameter attached property on the provided <see cref=”ButtonBase”/>.
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase to attach CommandParameter</param>
        /// <param name=”parameter”>Parameter value to attach</param>       
        public static void SetCommandParameter(UIElement element, object parameter)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            element.SetValue(CommandParameterProperty, parameter);
        }

        /// <summary>
        /// Gets the value in CommandParameter attached property on the provided <see cref=”ButtonBase”/>
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase that has the CommandParameter</param>
        /// <returns>The value of the property</returns>       
        public static object GetCommandParameter(UIElement element)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            return element.GetValue(CommandParameterProperty);
        }

        private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                MouseLeftButtonDownBehavior behavior = GetOrCreateBehavior(element);
                behavior.Command = e.NewValue as ICommand;
            }
        }

        private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                MouseLeftButtonDownBehavior behavior = GetOrCreateBehavior(element);
                behavior.CommandParameter = e.NewValue;
            }
        }

        private static MouseLeftButtonDownBehavior GetOrCreateBehavior(UIElement element)
        {
            MouseLeftButtonDownBehavior behavior = element.GetValue(ClickCommandBehaviorProperty) as MouseLeftButtonDownBehavior;
            if (behavior == null)
            {
                behavior = new MouseLeftButtonDownBehavior(element);
                element.SetValue(ClickCommandBehaviorProperty, behavior);
            }

            return behavior;
        }
    }
and here is the code for the CommandBehaviorBase

public class CommandBehaviorBase<T> where T : UIElement
    {
        private ICommand command;
        private object commandParameter;
        private readonly WeakReference targetObject;
        private readonly EventHandler commandCanExecuteChangedHandler;
        /// <summary>
        /// Constructor specifying the target object.
        /// </summary>
        /// <param name=”targetObject”>The target object the behavior is attached to.</param>
        public CommandBehaviorBase(T targetObject)
        {
            this.targetObject = new WeakReference(targetObject);

            // In Silverlight, unlike WPF, this is strictly not necessary since the Command properties
            // in Silverlight do not expect their CanExecuteChanged handlers to be weakly held,
            // but holding on to them in this manner should do no harm.
            this.commandCanExecuteChangedHandler = new EventHandler(this.CommandCanExecuteChanged);
        }

        /// <summary>
        /// Corresponding command to be execute and monitored for <see cref=”ICommand.CanExecuteChanged”/>
        /// </summary>
        public ICommand Command
        {
            get { return command; }
            set
            {
                if (this.command != null)
                {
                    this.command.CanExecuteChanged -= this.commandCanExecuteChangedHandler;
                }

                this.command = value;
                if (this.command != null)
                {
                    this.command.CanExecuteChanged += this.commandCanExecuteChangedHandler;
                    UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// The parameter to supply the command during execution
        /// </summary>
        public object CommandParameter
        {
            get { return this.commandParameter; }
            set
            {
                if (this.commandParameter != value)
                {
                    this.commandParameter = value;
                    this.UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// Object to which this behavior is attached.
        /// </summary>
        protected T TargetObject
        {
            get
            {
                return targetObject.Target as T;
            }
        }
        /// <summary>
        /// Updates the target object’s IsEnabled property based on the commands ability to execute.
        /// </summary>
        protected virtual void UpdateEnabledState()
        {
            if (TargetObject == null)
            {
                this.Command = null;
                this.CommandParameter = null;
            }
            else if (this.Command != null)
            {
                TargetObject.IsHitTestVisible = this.Command.CanExecute(this.CommandParameter);
            }
        }

        private void CommandCanExecuteChanged(object sender, EventArgs e)
        {
            this.UpdateEnabledState();
        }

        /// <summary>
        /// Executes the command, if it’s set, providing the <see cref=”CommandParameter”/>
        /// </summary>
        protected virtual void ExecuteCommand()
        {
            if (this.Command != null)
            {
                this.Command.Execute(this.CommandParameter);
            }
        }
    }

 

 

RIA services and combobox lookups with Async CTP

If we are editing data in a datagrid and have a few combobox columns, it will be difficult to know when the data is ready for all the comboboxes. The Async CTP that was released will help.

I got the extension method Kyle McClellan wrote in this post to consume LoadOperations as Tasks.
I had the following setup to display data from Products table in Northwind database in a Datagrid with Suppliers and categories as Comboboxes for editing.
          <sdk:DataGrid AutoGenerateColumns=”False” Name=”dataGrid1″ ItemsSource=”{Binding Products}”>
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn Header=”Name”  Binding=”{Binding ProductName}”/>
                <sdk:DataGridTemplateColumn Header=”Supplier”>
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text=”{Binding Supplier.CompanyName}”/>
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                    <sdk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                                <ComboBox ItemsSource=”{Binding Source={StaticResource root}, Path=DataContext.Suppliers}” 
                                          SelectedValuePath=”SupplierID”
                                          DisplayMemberPath=”CompanyName”
                                          SelectedValue=”{Binding SupplierID, Mode=TwoWay}” />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellEditingTemplate>
                </sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header=”Category”>
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text=”{Binding Category.CategoryName}”/>
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                    <sdk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                                <ComboBox ItemsSource=”{Binding Source={StaticResource root}, Path=DataContext.Categories}” 
                                          SelectedValuePath=”CategoryID”
                                          DisplayMemberPath=”CategoryName”
                                          SelectedValue=”{Binding CategoryID, Mode=TwoWay}” />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellEditingTemplate>
                </sdk:DataGridTemplateColumn>
                <sdk:DataGridTextColumn Header=”QuantityPerUnit”  Binding=”{Binding QuantityPerUnit}”/>
                <sdk:DataGridTextColumn Header=”UnitPrice”  Binding=”{Binding UnitPrice}”/>
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
       
The Domain service methods for getting categories and suppliers are going to return data after 5 and 3 sec.<
 public IQueryable<Category> GetCategories()
        {
            Thread.Sleep(5000);
            return this.ObjectContext.Categories;
        }

 public IQueryable<Supplier> GetSuppliers()
        {
            Thread.Sleep(3000);
            return this.ObjectContext.Suppliers;
        }

The Page is bound to instance of a ViewModel. If we use the GetData method to load the data, we can see that data is not loaded yet for the Categories and Suppliers columns. If we use GetDataAsync method to load the data, we are going to see the datagrid populated when all the related data is there.

 public class MainViewModel : INotifyPropertyChanged
    {
        public IEnumerable<Supplier> Suppliers { get; set; }
        public IEnumerable<Category> Categories { get; set; }
        public IEnumerable<Product> Products { get; set; }
        NorthwindDomainContext ctx = new NorthwindDomainContext();

        public MainViewModel()
        {
            //GetData();
            GetDataAsync();
        }

        async void GetDataAsync()
        {
            Task<LoadOperation<Product>> productTask = ctx.Load(ctx.GetProductsQuery()).AsTask();
            Task<LoadOperation<Category>> categoryTask = ctx.Load(ctx.GetCategoriesQuery()).AsTask();
            Task<LoadOperation<Supplier>> supplierTask = ctx.Load(ctx.GetSuppliersQuery()).AsTask();
            await TaskEx.WhenAll(productTask, categoryTask, supplierTask);
            Suppliers = supplierTask.Result.Entities;
            Products = productTask.Result.Entities;
            Categories = categoryTask.Result.Entities;

            NotifyPropertyChanged("Suppliers");
            NotifyPropertyChanged("Categories");
            NotifyPropertyChanged("Products");
        }

        void GetData()
        {
            ctx.Load<Product>(ctx.GetProductsQuery(), (lo) => { Products = lo.Entities; NotifyPropertyChanged("Products"); }, false);
            ctx.Load<Category>(ctx.GetCategoriesQuery(), (lo) => { Categories = lo.Entities; NotifyPropertyChanged("Categories"); }, false);
            ctx.Load<Supplier>(ctx.GetSuppliersQuery(), (lo) => { Suppliers = lo.Entities; NotifyPropertyChanged("Suppliers"); }, false);          
        }

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

    }

you can download the code from here