Silverlight to Angular – 3 (Commands)

In Silverlight, we use Commands to do clean separation of View and ViewModel. If we were not using commanding, we will end up creating code behind code to handle the events fired by the controls and then publish the message from view to viewmodel or call viewmodel method from view. Either way, there is unnecessary code in code behind. With command, we can bind the command to a control event using magic black box (in my case Jounce or your ICommand implementation) which will take care of the wiring control event directly to viewmodel method, leaving our code very clean.

We will reuse the code we did in the last blog since it already has commanding implementation. If you want to know more about how to do commanding, please take a look at the Jounce Command documentation or one of my old blog on commanding.

Problem: Display number of times a button is clicked.

I am not going to explain the Silverlight solution here since it is already explained here. Let’s look at the AngularJS solution here since I conveniently skipped it in the previous blog.

View:

1 <!DOCTYPE html> 2 <html ng-app> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div ng-controller="Controller"> 8 Number of Clicks:{{NumberOfClicks}} 9 <button ng-click="onClick()">Click Me</button> 10 </div> 11 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> 12 <script src="Scripts/Controller.js"></script> 13 </body> 14 </html>

In this example we are interested in line (9). Angular provides a cool directive ‘ng-click’ which will listen to the click event and calls a function in the controller or if it is an expression then it evaluate the expression. It is as simple as that. So in Angular, if you want to use commanding to do clean separation of view and controller, all you have to do is use ng-click directive on a button. From view model perspective, the function view is calling nothing more than just another function and nothing special about it. There are bunch of APIs you can use to create this commanding behavior on different controls and you can find them all here in Angular API directive.

Controller:

1 function Controller($scope) { 2 $scope.NumberOfClicks = 5; 3 $scope.onClick = function() { 4 $scope.NumberOfClicks = $scope.NumberOfClicks + 1; 5 }; 6 }

In Silverlight, not only we can execute the action through command, we have another option enable the command or not. This can be achieved using ng-disabled directive. Will look at how to use that in the following example. One benefit in Angular is that you can pass as many parameters as you want in the function call instead of just one in Silverlight. Let’s take a look at a sample where we can pass multiple parameters in function call.

Following example allow user to enter two numbers and perform an addition and display result. When the user enters non numeric number we want to disable the ‘Sum’ button.

View:

1 <!DOCTYPE html> 2 <html ng-app> 3 <head> 4 <title></title> 5 </head> 6 <body ng-controller="Controller"> 7 First : <input ng-model="numberOne"/><br/> 8 Second: <input ng-model="numberTwo"/><button ng-disabled="IsValid(numberOne, numberTwo)" 9 ng-click="onClick(numberOne, numberTwo)">Sum</button><br/> 10 Sum: {{sum}} 11 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> 12 <script src="Scripts/Controller.js"></script> 13 </body> 14 </html>

Both first and second number are two-way binding since the user change need to be propagated to model. On button click, we are using ng-click directive to call ‘onClick’ function in the controller and we are passing two parameters numberOne and numberTwo from the UI. If you notice, we also have another directive called ng-disabled, this will make the element disable if it return associated function from control returns false. If the input data collection is a form then you can enable and disable form submit button using Form.$validate.

Controller:

This is nothing but simple java script code which has variable assignments and a function call.

1 function Controller($scope) { 2 $scope.numberOne = 0; 3 $scope.numberTwo = 0; 4 $scope.sum = 0; 5 $scope.onClick = function(first, second) { 6 $scope.sum = Number(first) +Number(second); 7 }; 8 $scope.IsValid = function(first, second) { 9 return !(isNumber(first) && isNumber(second)); 10 }; 11 function isNumber(n) { 12 return !isNaN(parseFloat(n)) && isFinite(n); 13 } 14 }

One another point of interest along the line of ng-click is about other events from other elements. Take a look at this stack overflow question and answer. At the end, there is a comment by Tim Steward which is very interesting. He pointed out that if you do not see any ng- function for the element you are working on, his suggestion is to use Angular-UI

Using ChildsItemPath in C1FlexGrid to create clean MVVM code.

In the latest Component One drop, they have added a new property in C1FlexGrid called ‘ChildItemsPath’. It is a cool feature. In this blog, I am going to go through a simple example of using ChildItemsPath.

First lets see what made me to get to this property. In the current project I am working on, I had to create a grid with the following requirements

1. When there are no children in a group row, grid has to show only the group row and no child rows.

2. Group rows should be editable.

3. When user selects a row in the grid, we need to do something in the view model so I need to know which record user selected.

Just to give you an idea of the data model that I am working with.

ParentID – Parent Description – Child ID – Child Description

We are going to group on Parent ID and show Parent ID and Parent Description in the group row. So like any good old programmer, I approached this problem head on by creating a model like the one above and then created a Paged Collection View with group on Parent description, bind the collection to my C1FlexGrid. With this couple of things happened.

First one, when I have a record like the following

1 – Parent1 – null – null

where Parent1 have no children, it created a group row with 1 – Parent1 and created an empty child row. That was not expected behavior. So I wrote a small hack when the grid loading, I check and see if the rows have null value then change the height of row to 0. Pretty hack but, the group row did how expand/collapse button, giving the user the impression there is data. Next comes editing group row. I was trying to figure out how to edit the group row, while think about it, I looked at the model again and then it occurred to me, I could change my collection like the following

ID – Description

|- ID – Description

|- ID – Description

Something like

   1:  public class TestType1 : INotifyPropertyChanged
   2:  {
   3:          public int ID { get; set; }
   4:          public string Description { get; set; }
   5:          public ObservableCollection<TestType1> Children { get; set; }
   6:   
   7:          public TestType1()
   8:          {
   9:              Children = new ObservableCollection<TestType1>();
  10:          }
  11:   
  12:          public event PropertyChangedEventHandler PropertyChanged;
  13:          private void NotifyPropertyChanged(string info)
  14:          {
  15:              if (PropertyChanged != null)
  16:              {
  17:                  PropertyChanged(this, new PropertyChangedEventArgs(info));
  18:              }
  19:          }
  20:  }

Anytime when you have a collection like this, you should consider using ChildItemsPath, it is the new property added to C1FlexGrid. So how do we go about using it. Lets see the XAML

   1:      <Grid x:Name="LayoutRoot" Background="White"
   2:            d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
   3:          <c1:C1FlexGrid ItemsSource="{Binding MyCollection1}" AutoGenerateColumns="False" Name="flex" SelectionMode="Row" ChildItemsPath="Children">
   4:              <i:Interaction.Triggers>
   5:                  <i:EventTrigger EventName="SelectionChanged">
   6:                      <i:InvokeCommandAction Command="{Binding SelectionChanged}" CommandParameter="{Binding ElementName=flex, Path=SelectedItem}"/>
   7:                  </i:EventTrigger>
   8:              </i:Interaction.Triggers>
   9:              <c1:C1FlexGrid.Columns>
  10:                  <c1:Column Binding="{Binding ID, Mode=TwoWay}"/>
  11:                  <c1:Column Binding="{Binding Description, Mode=TwoWay}"/>
  12:              </c1:C1FlexGrid.Columns>
  13:          </c1:C1FlexGrid>
  14:      </Grid>

The last parameter in line (3) is the new property I was talking about. All you have to specify, which is the child collection property in the model that it has to group on.

As you can see from the line 4-8, after learning about triggers and behaviors, I no longer write code behind code to pass data to view model. If you can get the data from the gird, then use event trigger. So in our case, the third requirement was to pass the selected item to view model when the user select a row. that is what implemented in 4-8.

Line 5 – We are interested in listening SelectionChanged event.

Line 6 – When SelectionChanged event fires, Sl intercenpts and fires View Model command implementation for Selection Changed with Selected Item from the gird as the argument.

Lets look at the View Model for command implementation

   1:          public IActionCommand<object> SelectionChanged { get; set; }
   2:   
   3:          public List<TestType1> MyCollection1
   4:          {
   5:              get
   6:              {
   7:                  List<TestType1> list = CreateData();
   8:                  return list;
   9:              }
  10:          }
  11:   
  12:          private List<TestType1> CreateData()
  13:          {
  14:              List<TestType1> list = new List<TestType1>();
  15:              for (int i = 0; i < 5; i++)
  16:              {
  17:                  TestType1 parentData = new TestType1 { ID = i, Description = i.ToString() };
  18:                  for (int j = 0; j < i; j++)
  19:                  {
  20:                      TestType1 childData = new TestType1 { ID = j, Description = j.ToString() };
  21:                      parentData.Children.Add(childData);
  22:                  }
  23:                  list.Add(parentData);
  24:              }
  25:              return list;
  26:          }
  27:   
  28:          public MainViewModel()
  29:          {
  30:              SelectionChanged = new ActionCommand<object>(
  31:                  p => {
  32:                      TestType1 t = p as TestType1;
  33:                      int k = 0;
  34:                  });
  35:          }

Line 1- Command declaration

Line 3-25 – Collection generation for XAML data binding.

Line 30-34 – Action<T> implementation for Selection Changed Command.

With this code if I would run I get the following screen

image

Where the first row does not have children so it does not show expand or collapse icon. Since mode is set to two way, I am able to edit group row as well as child rows. With Event Trigger, there is no code in the view’s code behind.

That’s it.

True MVVM by implementing Event Triggers for non button base controls in Silverlight

First of all, I had a great time in Dallas Tech Fest. It was great. Hopefully, I get to present again next time as well.

While preparing for the presentation, I realized something, so far all the coding I have done, I did not truly develop in MVVM pattern. When I develop application which has buttons, I was able to move the code for button click event to Command thus removed code from code behind. But I did not do the same for other controls. Currently, Command only support Button Base classes. As you can guess, ListBox, ComboBox and others do not derive from Button Base, thus making use Command on these controls not possible. So I was going back to my old codes and found out that, I will adding ‘SelectionChanged’ event in the code behind and from there, I fire an event to View Model to perform an action. That was really bad, it never occurred to me to investigate this at that time. While preparing for Jounce Talk at Dallas Tech Fest I spend some more time looking at the problem and the answer was out there all along and I never noticed, as always.

So in this blog, lets look at how to remove the code from code behind for ListBox. This will be same for other controls. For simplicity, we will create a small listbox which will have three values and once the selection is made, we will display a text block at the bottom of the list box with “You selected {0}” of the value. Simple enough. First lets look at the XAML.

<UserControl x:Class="EventTrigger.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"
    xmlns:sampleData="clr-namespace:EventTrigger.SampleData"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"         
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White"
          d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
        <StackPanel>
        <ListBox Name="MyListBox" ItemsSource="{Binding ListItems}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=MyListBox, Path=SelectedIndex}"/>
                    </i:EventTrigger>    
                </i:Interaction.Triggers>
        </ListBox>
            <TextBlock Text="{Binding SelectedValue}" />
        </StackPanel>
    </Grid>
</UserControl>

 

In the XAML the point of interests are

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

 

We add the interactivity to the name space. This is very powerful dll to add behaviors, event triggers and more. The second part of interest is the triggers implementation right below ListBox

<ListBox Name="MyListBox" ItemsSource="{Binding ListItems}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=MyListBox, Path=SelectedIndex}"/>
                    </i:EventTrigger>    
                </i:Interaction.Triggers>
        </ListBox>

 

What we are saying here is that, when SelectionChanged event fires, invoke MyCommand, with Selected Index of MyListBox. As simple as that. InvokeCommandAction is straight forward, it specifies what command in view model to execute and what are its parameter. One thing to note here, I did not send either, SelectedItem or SelectedValue. Only reason I did not send it because, those two are of type ListBoxItem. I do not want any UI related information in my view model. Since I know the collection that is bound to the List box, with selected index, I can easily find out what item was selected.

Now that we know the command, if we look at the EventTrigger, here we define which which event that we want the command to be fired. In the list box, we are interested in selection changed. If the selection changes, myCommand will be fired. You need to make sure the event name spelled out correctly. Now that we know how triggers are hooked up, lets look at the view model code.

The ListItems which are bound to the ListBox was generated as follows

private List<string> listItems = new List<string>();

        public List<string> ListItems
        {
            get
            {
                if (listItems == null)
                    listItems = new List<string>();
                listItems.Add("1");
                listItems.Add("2");
                listItems.Add("3");
                return listItems;
            }
        }

The TextBlock which displays the message is nothing but a string property.

public string SelectedValue {get; set;}

This is typical command implementation, there is nothing special about handling anything event trigger.

public IActionCommand<int> MyCommand { get; private set; }

        public MainViewModel()
        {
            MyCommand = new ActionCommand<int>(TestMethod);
        }

        private void TestMethod(int parameter)
        {
            SelectedValue = string.Format("You selected : {0}", listItems[parameter]);
            RaisePropertyChanged(() => SelectedValue);
        }

First we declare the MyCommand and then in the ViewModel constructor we instantiate it and then we implement the method which executes the command.

Why my Commands did not work in Jounce

If you haven’t used commands in Silverlight, you have to try it. It is another beautiful way to move code from code behind to view model. If want to know how to use Commands, have a look at Jounce Codeplex or have a look at my blog.

Now lets go the problem I faced.  In XAML the Command and CommandParameter was defined properly. In the ViewModel both the commands are declared public and are initialized in the ViewModel constructor. Also both the execute methods are are declared and defined properly.  Now I was told, no compilation error and when you run, the command method never hits. After 30 minutes of debugging (I know it took some time, when you are not looking for the obvious), I found out that the command declarations were missing the getter and setter. Right after adding the getter and setter, it worked like a charm. So it is important to note that, if you are exposing something from ViewModel to view, make sure Jounce can get to it by declaring it as property.

Just in case you run into any problems, here are the things you need to watch out for using Commands

1. Make sure Command and CommandParameters are defined in the XAML and they bound to appropriate fields in ViewModel.

2. Make sure the command variables are declared ‘public’ and have getters and private setter.  ‘{ get; private set; }’

3. In the ViewModel constructor, you instantiate the command with execute method.

ButtonCommand = new ActionCommand<string>(CommandMethodToExecute)

4. Make sure the method signature correct. (In this case, if it is wrong, it will give compilation error).