Developing Simple Metro Style Applications using Commanding

Posted by: Mahesh Sabnis , on 8/2/2012, in Category WinRT
Views: 25565
Abstract: In this article, we will see how XAML features like commanding can be used in a Metro style application

A Metro style app is a new type of application that runs on Windows 8. An important feature of these applications is they can be developed in a variety of languages using HTML5, CSS, JavaScript or using .NET platform languages like C#, C++ , VB.NET, XAML or using DirectX.

Since XAML support is available for Metro style applications, developers who are familiar with XAML features like Commanding, DataBinding can easily utilize them in Metro applications. In this article, we will see how commanding features can be used in a metro style app.

(In the below article, I have used class name like ViewModel, but this is not the MVVM)

 

Here’s a simple architecture used in the application:

metro-design

 

The general requirements behind commanding in XAML are:

  • No code-behind for the UI.
  • The DataBinding, Validations need to be handled in a separate class.

Prerequisites:

For implementing Metro style application, you will require the following environment:

  • Windows 8 (to be released on August 15th, currently in RC).
  • Visual Studio 2012 RC.

For this article, I am using SQL Server 2012 with a Database ‘Company’ and ‘EmployeeInfo’ as a table:

metroapp-table

EmpNo is an Identity Column and the Primary Key.

Step 1: Open VS2012 RC, and create a blank solution, name it as ‘Metro_Apps_Commanding’. In this solution, add a new WCF 4.5 Service, name it as ‘WCF_DataService’. In this project, add a new ADO.NET Entity Data Model and name it as ‘CompanyEDMX.edmx’. After completing the Wizard, the following EDM gets generated:

edm

Step 2: Rename, IService1.cs to IService.cs and Service1.svc to Service.svc. Change the code in IService.cs:

[ServiceContract]
public interface IService
{
    [OperationContract]
    EmployeeInfo[] GetEmployees();
    [OperationContract]
    int CreateEmployee(EmployeeInfo Emp);
}

Step 3: Implement IService interface in the Service class:

using System.Linq;

namespace WCF_DataService
{
    public class Service : IService
    {
        CompanyEntities objContext;

        public Service()
        {
            objContext = new CompanyEntities();
        }
        /// <summary>
        /// Method to Read all Employees
        /// </summary>
        /// <returns></returns>
        public EmployeeInfo[] GetEmployees()
        {
            var Employees = objContext.EmployeeInfoes.ToArray();
            return Employees;
        }
        /// <summary>
        /// Method to Create New Employee Entry in the Table and
        /// return the EmpNo generated
        /// </summary>
        /// <param name="Emp"></param>
        /// <returns></returns>
        public int CreateEmployee(EmployeeInfo Emp)
        {
            int EmpNo = 0;
            objContext.AddToEmployeeInfoes(Emp);
            objContext.SaveChanges();
            EmployeeInfo EmpNew = (from emp in objContext.EmployeeInfoes.ToList()
                                   orderby emp.EmpNo
                                   select emp).Last();
            EmpNo = EmpNew.EmpNo;
            return EmpNo;
        }
    }
}

Step 4: Host the WCF Service in IIS (we have IIS8 on the Windows 8 Release Preview/RC).

Step 5: In the solution, add a new Metro Application Blank App, name it as ‘Metro_Apps_Commanding’. In this application, add a service reference to the WCF service.

Step 6: In this project, add a new class file and name it as ‘ViewModelBase.cs’. Add the following class in it:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void onPropertyChanged(string pName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(pName)); 
        }
    }
}

The above class is an abstract class. This implements the INotifyPropertyChanged interface. This interface is used to raise the property changed event when any UI control is bound with the public property.

Step 7: In the project, add a new class file and name it as ‘CommandingRepository.cs’. Write the following class in it:

using System;
using System.Windows.Input;

namespace Metro_Apps_Commanding
{
    public class CommandingRepository :ICommand
    {
        Action _handler;
        public CommandingRepository(Action hdl)
        {
            _handler = hdl;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _handler();
        }
    }
}

The above class implements the ‘ICommand’ interface. This class is used to define Command properties which can be bound with UI elements like the ‘Button’ in the UI. The Action delegate declared in the above class is used to execute methods which does not have any input and output parameter.

Step 8: In the ,add a new class and name it as ‘ViewModel.cs’. In this file, add the following class:

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Metro_Apps_Commanding.MyRef;
using Windows.UI.Xaml.Media;

namespace Metro_Apps_Commanding
{
    public class ViewModel : ViewModelBase
    {
        MyRef.ServiceClient Proxy;

        #region Constructor for Command Initializations
        /// <summary>
        /// Constructor used to create instance for WCF Service proxy and
        /// the Command properties
        /// </summary>
        public ViewModel()
        {
            Proxy = new ServiceClient();
           
            EmployeesCommand = new CommandingRepository(GetEmployees);
            EmployeeCommand = new CommandingRepository(CreateEmployee);
        }
        #endregion

        #region Properties to be Bound with UI
        EmployeeInfo _Employee = new EmployeeInfo();

        public EmployeeInfo Employee
        {
            get { return _Employee; }
            set
            {
                if (_Employee != value)
                {
                    _Employee = value;
                    onPropertyChanged("Employee");
                }
            }
        }
        ObservableCollection<EmployeeInfo> _Employees;

        public ObservableCollection<EmployeeInfo> Employees
        {
            get { return _Employees; }
            set
            {
                _Employees = value;
                onPropertyChanged("Employees");
            }
        }
        #endregion

        #region Command Objects
        public CommandingRepository EmployeesCommand { get; set; }
        public CommandingRepository EmployeeCommand { get; set; }
        #endregion

        #region Action Methods
        void GetEmployees()
        {
            Task<ObservableCollection<EmployeeInfo>> Emps = Proxy.GetEmployeesAsync();
            if (Emps.Result != null)
            {
                Employees = Emps.Result;
            }
        }

        /// <summary>
        /// Action Method check for the Validations
        /// </summary>
        void CreateEmployee()
        {
            if (Employee.EmpName == null ||
                Employee.DeptName == null || Employee.Designation == null)
            {
                ColorSet = new SolidColorBrush(Windows.UI.Colors.Red);
                 ErrorMessage = "Please enter values";
            }
            else
            {
                ErrorMessage = "";
                ColorSet = new SolidColorBrush(Windows.UI.Colors.Black);
                Task<int> EmpNo = Proxy.CreateEmployeeAsync(Employee);
                if (EmpNo.Result != 0 && EmpNo.Result > 0)
                {
                    Employee.EmpNo = EmpNo.Result;
                }
            }
        }
        #endregion

        #region Properties for Validations
        SolidColorBrush _ColorSet = new SolidColorBrush();

        public SolidColorBrush ColorSet
        {
            get { return _ColorSet; }
            set
            {
                _ColorSet = value;
                onPropertyChanged("ColorSet");
            }
        }

        string _ErrorMessage;

        public string ErrorMessage
        {
            get { return _ErrorMessage; }
            set
            {
                _ErrorMessage = value;
                onPropertyChanged("ErrorMessage");
            }
        }
        #endregion

    }
}

The above class:

  • Inherits from the ViewModelBase class to make use of PropertyChanged event.
  • Contains Employee and Employees public properties, used to bind with the UI elements.
  • Contains ‘GetEmployees()’ and ‘CreateEmployee()’ methods for making call to WCF service asynchronously and collect results. These results are then bound with UI using Employees and Employee properties.
  • Contains Command objects ‘EmployeesCommand’ and ‘EmployeeCommand’ which are used to execute ‘GetEmployees()’ and ‘CreateEmployee()’ action methods respectively.
  • Contains ‘ColorSet’ and ‘ErrorMessage’ properties for validations effects on UI while performing ‘CreateEmployee()’ action.

Step 9: Since we will be binding ‘Salary’ i.e. a decimal property to TextBox in the UI, we have to implement Converter for the create employee operation. To do this, in the project add a new class and name it as ‘DataConverter’. Tthis class implements ‘IValueConverter’. The code is as shown below:

public class DataConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
                        object parameter, string language)
    {
        value = decimal.Parse(value.ToString());
        return value;
    }

    public object ConvertBack(object value, Type targetType,
                        object parameter, string language)
    {
        value = decimal.Parse(value.ToString());
        return value;
    }
}

Step 10: Open ’MainPage.xaml’ and design it in the following manner (pardon my design skills):

metro-ui-design

The XAML with DataBinding will be as shown below:

<Page
    x:Class="Metro_Apps_Commanding.MainPage"
    IsTabStop="false"
    xmlns="hxxp://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x=hxxp://schemas.microsoft.com/winfx/2006/xaml
    xmlns:local="using:Metro_Apps_Commanding"
    xmlns:d=hxxp://schemas.microsoft.com/expression/blend/2008
    xmlns:mc=hxxp://schemas.openxmlformats.org/markup-compatibility/2006
    xmlns:src="using:Metro_Apps_Commanding"
    mc:Ignorable="d">
    <Page.Resources>
      
<src:ViewModel x:Key="EmpVM"></src:ViewModel>
        <DataTemplate x:Key="EmpTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding EmpNo}" Width="50" Height="50" />
                <TextBlock Text="{Binding EmpName}" Width="120" Height="50" />
                <TextBlock Text="{Binding Salary}" Width="80" Height="50" />
                <TextBlock Text="{Binding DeptName}" Width="120" Height="50" />
                <TextBlock Text="{Binding Designation}" Width="120" Height="50" />
            </StackPanel>
        </DataTemplate>
        <src:DataConverter x:Key="valConverter"></src:DataConverter>
    </Page.Resources>

    <Grid x:Name="grdParent" DataContext="{Binding Source={StaticResource EmpVM}}"
          Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="309*"/>
            <ColumnDefinition Width="374*"/>
        </Grid.ColumnDefinitions>
        <Grid HorizontalAlignment="Left" Height="451" Margin="10,10,0,0"
              VerticalAlignment="Top" Width="598"
               DataContext="{Binding Path=Employee,Mode=TwoWay}">
            <Grid.RowDefinitions>
                <RowDefinition Height="58*"/>
                <RowDefinition Height="68*"/>
                <RowDefinition Height="64*"/>
                <RowDefinition Height="56*"/>
                <RowDefinition Height="59*"/>
                <RowDefinition Height="146*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="285*"/>
                <ColumnDefinition Width="313*"/>
            </Grid.ColumnDefinitions>
           
            <TextBlock TextWrapping="Wrap" Text="EmpNo:"/>
            <TextBlock Grid.Row="1" TextWrapping="Wrap" Text="EmpName:"/>
            <TextBlock Grid.Row="2" TextWrapping="Wrap" Text="Salary:"/>
            <TextBlock Grid.Row="3" TextWrapping="Wrap" Text="DeptName:"/>
            <TextBlock Grid.Row="4" TextWrapping="Wrap" Text="Designation:"/>
          
            <TextBox x:Name="txteno" Grid.Column="1" TextWrapping="Wrap"
                     Margin="57,0,85,10"
                      Text="{Binding Path=EmpNo}" IsEnabled="False" />
            <TextBox x:Name="txtename" Grid.Column="1" Grid.Row="1"
                     TextWrapping="Wrap" Margin="57,10,85,0"
                   
  Text="{Binding Path=EmpName,Mode=TwoWay}"
                      BorderBrush="{Binding ElementName=grdParent,
                      Path=DataContext.ColorSet}"/>
            <TextBox x:Name="txtsal" Grid.Column="1" Grid.Row="2"
                     TextWrapping="Wrap" Margin="57,7,85,3"
                     
Text="{Binding Path=Salary,Mode=TwoWay,
                      Converter={StaticResource valConverter}}"/>
            <TextBox x:Name="txtdname" Grid.Column="1" Grid.Row="3"
                     TextWrapping="Wrap" Margin="57,6,85,4"
                     
Text="{Binding Path=DeptName,Mode=TwoWay}"
                     BorderBrush="{Binding ElementName=grdParent,
                     Path=DataContext.ColorSet}"/>
            <TextBox x:Name="txtdesig" Grid.Column="1" Grid.Row="4"
                     TextWrapping="Wrap" Margin="57,9,85,6"
                    
Text="{Binding Path=Designation,Mode=TwoWay}"
                     BorderBrush="{Binding ElementName=grdParent,
                     Path=DataContext.ColorSet}"/>
           
            <Button x:Name="btnCreateEmp" Content="Create Employee"
                    Grid.Column="1" HorizontalAlignment="Left" Margin="57,30,0,0"
                    Grid.Row="5" VerticalAlignment="Top" Width="171" Height="50"
                   
Command="{Binding ElementName=grdParent,
                     Path=DataContext.EmployeeCommand}"/>
            <TextBlock x:Name="txtvalidmessages"
                       Grid.Row="5" TextWrapping="Wrap"
                       
Foreground="{Binding ElementName=grdParent,
                        Path=DataContext.ColorSet}"
                        Text="{Binding ElementName=grdParent,
                        Path=DataContext.ErrorMessage}"/>
        </Grid>
        <ListBox x:Name="lstEmps"
                 Grid.Column="1" HorizontalAlignment="Left"
                 Height="482" Margin="24,26,0,0"
                 VerticalAlignment="Top" Width="697"
               
   ItemsSource="{Binding Path=Employees}"
                  ItemTemplate="{StaticResource EmpTemplate}"/>

        <Button x:Name="btnLoadEmployees"
                Content="Load Employees" Grid.Column="1"
                HorizontalAlignment="Left" Margin="24,526,0,0"
                VerticalAlignment="Top" Width="697"
                 Command="{Binding Path=EmployeesCommand}"/>

    </Grid>
</Page>

In the above XAML, the text in bold shows the DataBinding and the commanding expressions.

Step 11: Run the application and you will get the following result

metro-ui1

Click on the ‘Load Employees’ button

metro-ui2

Click on ‘Create Employee’ button. Without entering any data in textboxes, the validation fires

metro-ui3

The Textbox will have a RED border and an error message appears.

Now enter data in the textboxes except in EmpNo and click ‘Create Employee’. The employee record is created and the EmpNo will display as shown below:

metro-ui4

Conclusion:

This sample application indicates that if a developer has created a basic commanding application in WPF or Silverlight 4.0, then it can be easily migrated to Metro-Style apps.

The entire source code of this article can be downloaded over here

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Anup Hosur on Sunday, August 5, 2012 7:42 AM
Nice article.
I know, you are just showing an example of using command in metro style. But i don't like your approach, in this example you are using view first approach and is very difficult to do unit test, in some scenario you may need to add multiple views and is not possible to the great extend.

As a extension to this article, please write an article on MVVM pattern using ViewModel first approach. It will solve all the problem what i mentioned earlier and is very useful for the community people.
Comment posted by Jairam on Wednesday, August 8, 2012 2:46 AM
Nice article and nice suggestion by Anup. Anup you should also spread your knowledge to this .NET community as Mahesh sir does as this will help us beginners to learn more and more about .NET, especially in wcf areas
Comment posted by Anil on Wednesday, July 3, 2013 12:28 AM
Hi,
Good Presentation. I have a doubt
when I m using remote wcf service do I need to change any thing in the code.
I m using remote wcf service for which I m able retrieve methods but at runtime I m not able get the result of it.

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel