Author Archives: ansoesil

Video Tutorials: Understanding Data Access Design Patterns in Crosslight

When building applications, I always encourage the team in-charge, to look up and apply the appropriate design patterns for the application.

In our latest release (Crosslight 2), we introduced several new services such as RestClient, SQLite, EntityContextProvider, EntityContainer, etc, enabling you to easily create data aware apps across multiple mobile platforms.

These are the tools that can get the job done. However using all these services without applying design patterns can make your code redundant and much harder to manage.

I’ve created a video explaining several new design patterns to tackle data access challenges using Crosslight. Check it out below.

These design patterns are also extensively used in our Enterprise App Framework. In the next video, I’ll show you how to create data aware application from scratch using the new tools provided in Crosslight 2 and Enterprise App Framework which leverage the design patterns you just learn in this video.

To learn how to work with data using Crosslight, see our online documentation at
http://developer.intersoftpt.com/display/crosslight/Working+with+Data

With Regards,
Andry

UXGridView Part 5: Using Custom Editing Controls

In the previous post, I’ve covered the basics of data validation and editing using UXGridView the MVVM-way. In this post, I will show you how to extend UXGridView’s editing experiences by customizing the available column types, the editing template and the editing template selector.

Defining Columns Manually

First of all, you need to set the AutoGenerateColumns property of the UXGridView control to false.

<Intersoft:UXGridView AutoGenerateColumns="False">                
</Intersoft:UXGridView>
    

Next, you specify each column manually as follows.

<Intersoft:UXGridView AutoGenerateColumns="True">
    <Intersoft:UXGridView.Columns>
        <Intersoft:UXGridViewCheckBoxColumn
            Header="Discontinued"
            Binding="{Binding Discontinued}"/>
        <Intersoft:UXGridViewComboBoxColumn 
            Header="Category ID" Width="120"
            DisplayMemberPath="CategoryName" ValueMemberPath="CategoryID" 
            Binding="{Binding CategoryID}"                                                         
            ValueListSource="{Binding Categories,
            Source={StaticResource CategoriesViewModel}}"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Product ID" 
            Binding="{Binding ProductID}" 
            IsReadOnly="True"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Product Name"
            Binding="{Binding ProductName}"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Units In Stock" 
            Binding="{Binding UnitsInStock}"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Unit Price" 
            Binding="{Binding UnitPrice}"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Units On Order" 
            Binding="{Binding UnitsOnOrder}"/>
        <Intersoft:UXGridViewTextColumn 
            Header="Quantity Per Unit"
            Binding="{Binding QuantityPerUnit}"/>
    </Intersoft:UXGridView.Columns>                               
</Intersoft:UXGridView>
    

The results look like the following screenshot.

Dropdown column in UXGridView

Notice that I used three different types of column.

  • UXGridViewTextColumn

    The column has a textual representation for data display and a UXTextBox for data editing control.

  • UXGridViewCheckBoxColumn

    The column has a check box representation to display the data and a UXCheckBox for data editing control.

  • UXGridViewComboBoxColumn

    The column has a textual representation to display the data, a dropdown button to display the data list, and a UXComboBox for data editing control.

Using UXGridViewTextColumn and UXGridViewCheckBoxColumn is pretty straightforward, you just need to set the Binding and Header properties, and you’re all set. For UXGridViewComboBoxColumn, you need to specify the ValueListSource which is required for the combo box data source, along with the DisplayMemberPath and ValueMemberPath.

In this sample, I assigned the ValueListSource  for the UXGridViewComboBoxColumn from a static resource.

<Grid.Resources>
    <ViewModels:CategoriesViewModel x:Key="CategoriesViewModel"/>        
</Grid.Resources>
    

The Categories ViewModel looks like the following code.

using System.Collections.Specialized;
using Intersoft.Client.Data.ComponentModel;
using UXGridView.Samples.ModelServices;

namespace UXGridView.Samples.ViewModels
{
    public class CategoriesViewModel : ViewModelBase
    {
        public CategoriesViewModel()
        {
            this.CategoriesSource = CategoriesRepository.Instance;
            this.LoadCategories();
        }

        private INotifyCollectionChanged _Categories;

        public INotifyCollectionChanged Categories
        {
            get { return this._Categories; }
            set
            {
                if (this._Categories != value)
                {
                    this._Categories = value;
                    this.OnPropertyChanged("Categories");
                }
            }
        }

        public IDataRepository CategoriesSource { get; set; }

        public virtual void LoadCategories()
        {
            this.CategoriesSource.GetData
            (
                (Categories) =>
                {
                    this.Categories = new PagedCollectionView(Categories);
                },
                (error) =>
                {

                }
            );
        }
    }
}

    

Notice that the CategoriesViewModel simply retrieves the data from the CategoriesSource which used repository pattern for loosely-coupled data access. To learn more about data repository pattern, see the ProductsRepository example in my previous blog post.

Using Custom Editing Controls

In many business scenarios, you’re often asked to provide more advanced editing controls in the GridView rather than just a plain textbox, such as DateTimePicker, Slider, MaskedInput, etc. Luckily, it’s fairly easy to implement in UXGridView, thanks to the solid editing architecture. All you need to do is simply creating a data template that contains the input control of your choice, and assign it to the CellEditingTemplate property of the UXGridView.

The following examples show how to customize the editing control of each column through the provided CellEditingTemplate property.

<Intersoft:UXGridViewTextColumn 
    Header="Units In Stock" 
    Binding="{Binding UnitsInStock}">
    <Intersoft:UXGridViewTextColumn.CellEditingTemplate>
        <DataTemplate>
            <Intersoft:UXNumericUpDown 
                Maximum="9999" 
                Value="{Binding UnitsInStock, Mode=TwoWay}" 
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Stretch"/>
        </DataTemplate>
    </Intersoft:UXGridViewTextColumn.CellEditingTemplate>
</Intersoft:UXGridViewTextColumn>

<Intersoft:UXGridViewTextColumn Header="Unit Price" Binding="{Binding UnitPrice}"> <Intersoft:UXGridViewTextColumn.CellEditingTemplate> <DataTemplate> <Intersoft:UXCurrencyEditor EditMask="c2" UseEditMaskAsDisplayMask="True" Value="{Binding UnitPrice, Mode=TwoWay}"/> </DataTemplate> </Intersoft:UXGridViewTextColumn.CellEditingTemplate> </Intersoft:UXGridViewTextColumn>
<Intersoft:UXGridViewTextColumn Header="Units On Order" Binding="{Binding UnitsOnOrder}"> <Intersoft:UXGridViewTextColumn.CellEditingTemplate> <DataTemplate> <Intersoft:UXSliderBar Value="{Binding UnitsOnOrder, Mode=TwoWay}" SmallChange="10" LargeChange="20" Maximum="100"/> </DataTemplate> </Intersoft:UXGridViewTextColumn.CellEditingTemplate> </Intersoft:UXGridViewTextColumn>

The following examples show how to use UXDateTimePicker to edit a date time column. Notice that you can bind the BirthDate to the column and directly specify the string format in the binding expression.

<Intersoft:UXGridViewTextColumn 
    Header="Birth Date" 
    Binding="{Binding BirthDate, StringFormat=MM/dd/yyyy}">
    <Intersoft:UXGridViewTextColumn.CellEditingTemplate>
        <DataTemplate>
            <Grid Background="White" Margin="1">
                <Intersoft:UXDateTimePicker 
                    Value="{Binding BirthDate, Mode=TwoWay}" 
                    EditMask="MM/dd/yyyy" 
                    BorderThickness="0"                                                              
                    UseEditMaskAsDisplayMask="True" 
                    VerticalAlignment="Center" 
                    HorizontalAlignment="Stretch"/>
            </Grid>
        </DataTemplate>
    </Intersoft:UXGridViewTextColumn.CellEditingTemplate>
</Intersoft:UXGridViewTextColumn>
<Intersoft:UXGridViewTemplateColumn Header="Address" Binding="{Binding Address}" Width="100"> <Intersoft:UXGridViewTemplateColumn.CellTemplate> <DataTemplate> <TextBlock TextWrapping="Wrap" Text="{Binding Address}" Margin="4"/> </DataTemplate> </Intersoft:UXGridViewTemplateColumn.CellTemplate> <Intersoft:UXGridViewTemplateColumn.CellEditingTemplate> <DataTemplate> <Intersoft:UXTextBox AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Address, Mode=TwoWay}"/> </DataTemplate> </Intersoft:UXGridViewTemplateColumn.CellEditingTemplate> </Intersoft:UXGridViewTemplateColumn>

As seen in the example above, UXGridView provides a template column named UXGridViewTemplateColumn. This column allows you to change both display and editing appearance. In the example, the UXGridViewTemplateColumn uses a wrapable TextBlock for the display, and a multi-line TextBox for data editing.

You can define literally any kind of input controls that support MVVM in the CellEditingTemplate, including our latest advanced input controls such as UXCalendar, UXDomainUpDown, UXDateTimeEditor, and so forth.

Using Cell Editing Template Selector

A rather unique scenario is where the users would like to have different editing control for the same column which depends on the data being edited. This can be achieved elegantly in UXGridView by using CellEditingTemplateSelector. It works similar to any other template selectors, so the first thing you need to do is creating a template selector class that derived from DataTemplateSelector.

using System.Windows;
using Intersoft.Client.Framework;
using Intersoft.Client.UI.Data;

namespace UXGridView.Samples.Selectors
{
    public class CellEditingSelector: DataTemplateSelector
    {
        public DataTemplate NumericEditor { get; set; }
        public DataTemplate SliderEditor { get; set; }

        public override DataTemplate SelectTemplate(object item, 
                 DependencyObject container)
        {            
            UXGridViewCell cell = container as UXGridViewCell;
            UXGridViewRowBase rowBase = cell.OwningRowBase;
            UXGridViewRow row = rowBase as UXGridViewRow;

            switch (cell.OwningColumn.PropertyName)
            {
                case "UnitsInStock":
                    if (rowBase.IsNewRow || (row != null && row.Index % 2 != 0))
                        return this.SliderEditor;
                    else
                        return this.NumericEditor;                    
            }

            return null;
        }
    }
}

The above code basically selects the editing control differently depending on whether the row is a normal or an alternating row. Notice that the conditioning is scoped to UnitsInStock column only.

Finally, let’s assign the template selector to the UXGridView, see the code below.

<Grid.Resources>
    <DataTemplate x:Key="NumericEditor">
        <Intersoft:UXNumericUpDown Maximum="100"
                   Value="{Binding UnitsInStock, Mode=TwoWay}"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Stretch"/>
    </DataTemplate>
    <DataTemplate x:Key="SliderEditor">
        <Intersoft:UXSliderBar
                   Value="{Binding UnitsInStock, Mode=TwoWay}"
                   SmallChange="10" LargeChange="20" Maximum="100"/>
    </DataTemplate>
<Selectors:CellEditingSelector x:Key="CellEditingSelector" NumericEditor="{StaticResource NumericEditor}" SliderEditor="{StaticResource SliderEditor}"/> </Grid.Resources> <Intersoft:UXGridView CellEditingTemplateSelector= "{StaticResource CellEditingSelector}">

As the result, editing the cells of UnitsInStock for the normal row will show a UXNumericUpDown control. See the illustration below.

UXNumericUpDown for normal row editing

While editing the cells for the alternating row of the same column will show a UXSliderBar control instead.

UXSliderBar for alternate row editing

That’s all for now, I hope this post gives you some cool ideas on using the available column types and customizing the editing controls in UXGridView. All samples I used in this blog post are available in our latest CTP release. Click here to download it now and get yourself familiar with the MVVM implementation in the UXGridView, starting from the data retrieval, handling the data editing, to customizing editing controls.

Best Regards,

Andry

UXGridView Part 4: Data Editing the MVVM-way with Commands

In the previous post, Jimmy announced the latest CTP for UXGridView with the CUD (Create, Update and Delete) capability. In this post, I will provide the step-by-step instructions to work with these new features.

Enabling the CUD capability

First of all, let’s enable the CUD (Create, Update, Delete) capability in UXGridView. You can enable these features by simply setting the CanUserAdd, CanUserEdit, and CanUserDelete properties to True.

<Intersoft:UXGridView CanUserAdd="True" 
                      CanUserDelete="True" 
                      CanUserEdit="True"/>
        

Notice that there will be a NewRow element at the top of the Grid when you set the CanUserAdd property to True.

Furthermore, you can also delete or edit the selected item(s) when the CanUserEdit or CanUserDelete properties are set to True.

To edit a row you can use either mouse or keyboard. The following list shows you some configuration that you can change to edit a record using mouse / keyboard.

To edit a row using mouse:

  • EditMouseGesture
    • Single Click

      Directly begin edit to the current selected cell.

    • Second Click

      Begin edit to the current selected cell after the row is selected.

    • Double Click

      Directly begin edit to the current selected cell when double click fires.

To edit a row using keyboard:

  • EditKeyGEditKeyGesture
    • F2

      Directly begin edit to the current selected cell using F2 key.

    • Any Keystroke

      Directly begin edit to the current selected cell from any key stroke.

  • EnterKeyAction
    • EnterEdit

      Directly begin edit to the current selected cell using Enter key.

    • MoveToNextRow

      Move to next row (does not edit the cell).

  • EditEnterKeyAction
    • CommitAndMovetoNextRow

      Commit the changes at current edited row and move to the next row.

    • ExitEdit

      Exit the cell edit of the current edited cell.

    • MoveToNextEditableCell

      Move to next editable cell. (Will move to next row’s cell when it reach the end of the row)

Beside these options, you can also use several common keystroke as listed below during editing.

  • Tab / Shift + Tab

    To move to next / previous editable cell.

  • Shift + Enter

    Commit the changes at current edited row and stay in the current selection

  • Delete

    Delete the current selected record(s)

  • Escape

    Cancel current changes. If currently you are in an active edit cell, it will cancel the cell changes. If you are in current active edit row, it will cancel the row changes.

Handling the CUD operation

To handle the CUD operation, UXGridView provides several command-related properties that you can specify to execute a method depending on the actions. These command- properties are listed as follows:

  • PrepareNewRowCommand

    Called when you begin edit at the NewRow element. Used to initialized the NewRowItem.

  • ValidateRowCommand

    Validate the row before the row is committed.

  • InsertRowCommand

    Called when a new row is committed. You can directly save the changes and/or refresh the UXGridView if necessary.

  • UpdateCellCommand

    Called when the cell is committed.

  • UpdateRowCommand

    Called when an existing row is committed. You can directly save the changes and/or refresh the UXGridView if necessary.

  • DeleteRowCommand

    Called when a row is deleted. You can directly save the changes and / or refresh the UXGridView if necessary.

  • RejectRowCommand

    Called when the changes in the row is cancelled. This command is used to reject the changes in the data entity if required (such as in DevForce).

  • SaveChangesCommand

    Called when the save changes command is executed. You handle this command to save all the pending changes made in the UXGridView.

  • RejectChangesCommand

    Called when the reject changes command is executed. You handle this command to reject all the pending changes made in the UXGridView.

These command properties can be bound to your ViewModel using delegate command. Next, I will show you how to bind these commands along with some important properties that are necessary for the CUD operation.

Binding the CUD Commands to UXGridView using MVVM Pattern

Let’s create the ViewModel for our example. First, define the delegate commands and the required selection properties, then instantiate them in the constructor.

using System.Collections;
using Intersoft.Client.Framework;
using Intersoft.Client.Framework.Input;
using Intersoft.Client.UI.Data;
using UXGridView.Samples.ModelServices;

namespace UXGridView.Samples.ViewModels
{
    public class ServerEditingViewModel : ServerSideOperationViewModel
    {        
        public ServerEditingViewModel()
            : base()
        {            
            this.DeleteRowCommand = 
                new DelegateCommand(ExecuteDeleteRow);
            this.InsertRowCommand = 
                new DelegateCommand(ExecuteInsertRow);
            this.PrepareNewRowCommand = 
                new DelegateCommand(ExecutePrepareNewRow);
            this.UpdateCellCommand = 
                new DelegateCommand(ExecuteUpdateCell);
            this.UpdateRowCommand = 
                new DelegateCommand(ExecuteUpdateRow);
            this.RejectRowCommand = 
                new DelegateCommand(ExecuteRejectRow);
            this.RejectChangesCommand = 
                new DelegateCommand(ExecuteRejectChanges);
            this.SaveChangesCommand = 
                new DelegateCommand(ExecuteSaveChanges);
            this.ValidateRowCommand = 
                new DelegateCommand(ExecuteValidateRow);
        }

        #region Fields

        private bool _hasChanges;
        private bool _isRefreshed;        
        private object _newProduct;
        private object _selectedProduct;
        private IEnumerable _selectedProducts;

        #endregion

        #region EditableProductsSource

        private IEditableDataRepository EditableProductsSource
        {
            get
            {
                return this.ProductsSource as IEditableDataRepository;
            }
        }

        #endregion

        #region Selection and Editing Properties

        public object NewProduct
        {
            get { return this._newProduct; }
            set
            {
                if (this._newProduct != value)
                {
                    this._newProduct = value;
                    this.OnPropertyChanged("NewProduct");
                }
            }
        }

        public object SelectedProduct
        {
            get { return this._selectedProduct; }
            set
            {
                if (this._selectedProduct != value)
                {
                    this._selectedProduct = value;
                    this.OnPropertyChanged("SelectedProduct");
                }
            }
        }

        public IEnumerable SelectedProducts
        {
            get { return this._selectedProducts; }
            set
            {
                if (this._selectedProducts != value)
                {
                    this._selectedProducts = value;
                    this.OnPropertyChanged("SelectedProducts");
                }
            }
        }

        public bool IsRefreshed
        {
            get { return this._isRefreshed; }
            set
            {
                if (this._isRefreshed != value)
                {
                    this._isRefreshed = value;
                    this.OnPropertyChanged("IsRefreshed");
                }
            }
        }

        public bool HasChanges
        {
            get { return _hasChanges; }
            set
            {
                if (_hasChanges != value)
                {
                    _hasChanges = value;
                    OnPropertyChanged("HasChanges");
                }
            }
        }

        #endregion

        #region Commands

        public DelegateCommand DeleteRowCommand { get; set; }
        public DelegateCommand InsertRowCommand { get; set; }
        public DelegateCommand PrepareNewRowCommand { get; set; }
        public DelegateCommand UpdateCellCommand { get; set; }
        public DelegateCommand UpdateRowCommand { get; set; }
        public DelegateCommand RejectRowCommand { get; set; }
        public DelegateCommand RejectChangesCommand { get; set; }
        public DelegateCommand SaveChangesCommand { get; set; }
        public DelegateCommand ValidateRowCommand { get; set; }

        #endregion
    }
}
    

Next, we will bind these commands to the UXGridView.

<Intersoft:UXPage 
	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:Intersoft="http://intersoft.clientui.com/schemas"
          xmlns:IntersoftModel="clr-namespace:Intersoft.Client.Data.Component
Model.CollectionViews;assembly=Intersoft.Client.Data.ComponentModel" xmlns:ViewModels="clr-namespace:UXGridView.Samples.ViewModels" x:Class="UXGridView.Samples.Views.ServerSideOperation.Editing" Title="Editing Page" d:DesignWidth="1024" d:DesignHeight="800"> <Grid x:Name="LayoutRoot"> <Grid.DataContext> <ViewModels:ServerEditingViewModel/> </Grid.DataContext>
<Intersoft:DockPanel> <Intersoft:UXGridView AutoGenerateColumns="False" QueryOperation="Server" CanUserPage="True" PageSize="20" RowHeaderVisibility="Visible" IsBusy="{Binding IsBusy, Mode=TwoWay}" IsRefreshed="{Binding IsRefreshed, Mode=TwoWay}" ItemsSource="{Binding Products}" SortDescriptors="{Binding QueryDescriptor.SortDescriptors, Mode=TwoWay}" PageDescriptor="{Binding QueryDescriptor.PageDescriptor}" GroupFootersVisibility="Visible" GroupByBoxVisibility="Visible" CanUserAdd="true" CanUserDelete="true" CanUserEdit="true" NewItem="{Binding NewProduct, Mode=TwoWay}" SelectedItem="{Binding SelectedProduct, Mode=TwoWay}" ValidateRowCommand="{Binding ValidateRowCommand}" InsertRowCommand="{Binding InsertRowCommand}" DeleteRowCommand="{Binding DeleteRowCommand}" PrepareNewRowCommand="{Binding PrepareNewRowCommand}" UpdateCellCommand="{Binding UpdateCellCommand}" UpdateRowCommand="{Binding UpdateRowCommand}" SaveChangesCommand="{Binding SaveChangesCommand}" RejectRowCommand="{Binding RejectRowCommand}" RejectChangesCommand="{Binding RejectChangesCommand}" HasChanges="{Binding HasChanges}"> <Intersoft:UXGridView.GroupDescriptors> <Intersoft:UXGridViewGroupDescriptor PropertyName="CategoryID"/> </Intersoft:UXGridView.GroupDescriptors> <Intersoft:UXGridView.Columns> <Intersoft:UXGridViewTextColumn Header="Category ID" Binding="{Binding CategoryID}"/> <Intersoft:UXGridViewTextColumn Header="Product ID" Binding="{Binding ProductID}" IsReadOnly="True" Aggregate="Count" FooterFormatString="Count = {0}"/> <Intersoft:UXGridViewTextColumn Header="Product Name" Binding="{Binding ProductName}"/> <Intersoft:UXGridViewTextColumn Header="Units In Stock" Binding="{Binding UnitsInStock}" Aggregate="Max" FooterFormatString="Max = {0}"/> <Intersoft:UXGridViewTextColumn Header="Unit Price" Binding="{Binding UnitPrice}" Aggregate="Avg" FooterFormatString="Avg = {0:n2}"/> <Intersoft:UXGridViewTextColumn Header="Units On Order" Binding="{Binding UnitsOnOrder}" Aggregate="Min" FooterFormatString="Min = {0}"/> <Intersoft:UXGridViewTextColumn Header="Quantity Per Unit" Binding="{Binding QuantityPerUnit}"/> </Intersoft:UXGridView.Columns> </Intersoft:UXGridView> </Intersoft:DockPanel> </Grid> </Intersoft:UXPage>

Handling the CUD Operation in ViewModel Using DevForce

After the commands are bound to the ViewModel, it is up to you how you want to handle the CUD operation.

If you prefer to automatically update the records after each CUD operation, you can do that in the InsertRowCommand, UpdateRowCommand and DeleteRowCommand respectively, and probably followed up by RefreshCommand to refresh the data. However, if you prefer a batch update, you can notify the UXGridView by setting the HasChanges property to True, and later call the SaveChanges method to perform the batch update.

This batch update capability might not be available in all data providers such as WCF RIA. When you enable server query in WCF RIA such as paging, sorting, and filtering; you always get a new fresh data from the database regardless of the changes in the client. This behavior is due to the WCF RIA not supporting client-side caching. In this case, you might want to do automatic update and/or refresh after each CUD operation. There are samples that show how to do this in our CTP package.

Next, I will show you how to handle the CUD Operation in the ViewModel. To save time, I will only cover the one using DevForce which allows you to enable batch update.

Since the sample used ProductsRepository to encapsulate all data operation, I’ll show you first what I’m doing in the ProductsRepository.

using System;
using System.Collections;
using System.ComponentModel;
using IdeaBlade.EntityModel;
using IdeaBlade.Validation;
using Intersoft.Client.Data.ComponentModel;
using Intersoft.Client.Data.Provider.DevForce;
using UXGridView.Samples.Data.DevForce;

namespace UXGridView.Samples.ModelServices
{
    public class ProductsRepository : IEditableDataRepository
    {
        public ProductsRepository(NorthwindEntities entityManager)
        {
            this.Manager = entityManager;
        }

        private static IDataRepository _repository;

        public static IDataRepository Instance
        {
            get
            {
                return _repository ?? (_repository = CreateRepository());
            }
            set
            {
                _repository = value;
            }
        }

        private NorthwindEntities Manager { get; set; }

        public static IDataRepository CreateRepository()
        {
            return new ProductsRepository(EntityManager.Create());
        }

        public void GetData(Action<IEnumerable> onSuccess, 
Action<Exception> onFail) { this.Manager.Products .ExecuteAsync( op => { if (op.CompletedSuccessfully) { if (onSuccess != null) onSuccess(op.Results); } else { if (onFail != null) { op.MarkErrorAsHandled(); onFail(op.Error); } } } ); } public void GetData(QueryDescriptor queryDescriptor, Action<IEnumerable> onSuccess, Action<int> onItemCountRetrieved, Action<Exception> onFail) { this.Manager.Products.OrderBy(p => p.ProductID).Parse(queryDescriptor) .ExecuteAsync( op => { if (op.CompletedSuccessfully) { if (onSuccess != null) onSuccess(op.Results); if (onItemCountRetrieved != null) onItemCountRetrieved(-1); // not applicable; } else { if (onFail != null) { op.MarkErrorAsHandled(); onFail(op.Error); } } } ); } public void GetTotalItemCount (QueryDescriptor queryDescriptor, Action<int> onSuccess) { var op = this.Manager.Products .Parse(queryDescriptor, false).AsScalarAsync().Count(); op.Completed += (o, e) => { if (onSuccess != null) onSuccess(e.Result); }; } public void Insert(object entity) { this.Manager.AddEntity(entity); } public void Delete(IList entities) { foreach (object o in entities) { this.Delete(o); } } public void Delete(object entity) { Product product = entity as Product; product.EntityAspect.Delete(); } public void RejectChanges() { this.Manager.RejectChanges(); } public void SaveChanges( Action onSuccess, Action<Exception> onError) { this.Manager.SaveChangesAsync ( op => { if (op.IsCompleted) { if (op.HasError) { // handle error op.MarkErrorAsHandled(); onError(op.Error); } else { onSuccess(); } } }, null ); } public object Create() { return this.Manager.CreateEntity<Product>(); } public void Validate(object entity) { Product product = (Product)entity; product.EntityAspect.ValidationErrors.Clear(); product.EntityAspect.VerifierEngine.Execute(product); if (product.CategoryID < 1 || product.CategoryID > 8) product.EntityAspect.ValidationErrors .Add(new VerifierResult( VerifierResultCode.Error, "Specified CategoryID does not exist", new string[] { "CategoryID" })); if (product.UnitPrice < 0) product.EntityAspect.ValidationErrors .Add(new VerifierResult (VerifierResultCode.Error, "Unit Price can not be less than 0", new string[] { "UnitPrice" })); if (product.UnitsInStock < 0) product.EntityAspect.ValidationErrors .Add(new VerifierResult( VerifierResultCode.Error, "Units in Stock can not be less than 0", new string[] { "UnitsInStock" })); if (product.UnitsOnOrder < 0) product.EntityAspect.ValidationErrors .Add(new VerifierResult( VerifierResultCode.Error, "Units on Order can not be less than 0", new string[] { "UnitsOnOrder" })); } public void RejectChanges(object entity) { IRevertibleChangeTracking revertible =
(IRevertibleChangeTracking)entity;
revertible.RejectChanges(); } } }

Finally, let’s handle the CUD commands in our ViewModel.

For Create Operation

public void ExecuteInsertRow(object parameter)
{
    this.NewProduct = null;

    if (!this.IsBatchUpdate)
        this.SaveChanges();
    else
        this.HasChanges = true;
}

public void ExecutePrepareNewRow(object parameter)
{

    // It's possible to initialize the new row with default values
    // Example:
    // product.ProductName = "New Product";

    this.NewProduct = this.EditableProductsSource.Create();
    this.EditableProductsSource.Insert(this.NewProduct);
}
    

For Update Operation

public void ExecuteUpdateCell(object parameter)
{
    object[] updateCellParameters = (object[])parameter;
    object product = updateCellParameters.GetValue(0);
    string property = updateCellParameters.GetValue(1).ToString();

    // perform cell-level validation if required
}

public void ExecuteUpdateRow(object parameter)
{
    if (!this.IsBatchUpdate)
        this.SaveChanges();
    else
        this.HasChanges = true;
}

For Delete Operation

public void ExecuteDeleteRow(object parameter)
{
    this.EditableProductsSource.Delete(parameter as IList);

    if (!this.IsBatchUpdate)
        this.SaveChanges();
    else
        this.HasChanges = true;
}

For Validation, Reject Row and Refresh Operation

public void SaveChanges()
{
    this.IsBusy = true;

    this.EditableProductsSource.SaveChanges
    (
        () =>
        {
            this.IsRefreshed = true;
            this.LoadProducts(); // refresh
            this.HasChanges = false;
            this.IsBusy = false;
        },
        exception =>
        {
            this.IsBusy = false;

            MessagePresenter presenter = new MessagePresenter();
            presenter.ShowErrorMessage("An unexpected error has occurred: " 
+ exception.Message); } ); } public void ExecuteValidateRow(object parameter) { this.EditableProductsSource.Validate(parameter); } public void ExecuteRejectRow(object parameter) { if (parameter != null) this.EditableProductsSource.RejectChanges(parameter); this.NewProduct = null; }

For Batch Operation

public void ExecuteRejectChanges(object parameter)
{
    this.EditableProductsSource.RejectChanges();
    this.HasChanges = false;
}

public void ExecuteSaveChanges(object parameter)
{
    // if users click on the save changes while the new row is being edited,
    // presume the new row isn't intended
    if (this.NewProduct != null)
        this.EditableProductsSource.RejectChanges(this.NewProduct);

    this.SaveChanges();
}

I hope this post gives you a comprehensive understanding on handling the CUD (Create, Update, Delete) operation in UXGridView using MVVM design pattern. In the next post, I will blog about customizing editing controls in UXGridView, for instance, customizing the editing control of certain columns to more advanced input controls such as UXDateTimePicker, UXNumericUpDown, UXSliderBar, and so forth.

If you have any questions regarding the CUD operation, please post some questions in our community website.

Regards,

Andry

UXGridView Part 3: Fundamental Grid Features

The core feature-sets that we wanted to focus in the first data controls CTP are the QueryDescriptor component model, and the fundamental grid architecture/features such as high-performance virtualization, adaptable layout, drag and drop, grouping and much more. In my previous post, I’ve explained how to use QueryDescriptor which greatly simplify server data access using MVVM design pattern. Now, I will share some of the fundamental grid features that already available in the first CTP, and what you can expect in the next release.

Virtualization

One of the most fundamental features in UXGridView is its high-performance virtualization (virtual rendering) architecture. This means that the UXGridView doesn’t actually render all the data passed to it. Instead, it implements a smart logic which virtually render the data based on the available view port and the current scroll position. This virtualization feature is applied to all types of cells and rows rendering including the RowGroupHeader and RowGroupFooter, so that you can expect a blazing fast and scalable grid control for your applications.

You can try to bind the UXGridView with an unbound collection that holds million of rows as follows.

public class Data
{
    public string Column1 { get; set; }
    public string Column2 { get; set; }
    public string Column3 { get; set; }
    public string Column4 { get; set; }
    public string Column5 { get; set; }
    public string Column6 { get; set; }
    public string Column7 { get; set; }
    public string Column8 { get; set; }
    public string Column9 { get; set; }
    public string Column10 { get; set; }
    public string Column11 { get; set; }
    public string Column12 { get; set; }
    public string Column13 { get; set; }
    public string Column14 { get; set; }
    public string Column15 { get; set; }
    public string Column16 { get; set; }
    public string Column17 { get; set; }
    public string Column18 { get; set; }
    public string Column19 { get; set; }
    public string Column20 { get; set; }
    public string Column21 { get; set; }
    public string Column22 { get; set; }
    public string Column23 { get; set; }
    public string Column24 { get; set; }
    public string Column25 { get; set; }
    public string Column26 { get; set; }
    public string Column27 { get; set; }
    public string Column28 { get; set; }
    public string Column29 { get; set; }
    public string Column30 { get; set; }
}

public partial class SilverlightControl1 : UserControl
{
    public SilverlightControl1()
    {
        InitializeComponent();

        ObservableCollection
 data = new ObservableCollection();
        for (int i = 0; i < 1000000; i++)
        {
            Data d = new Data()
            {
                Column1 = "Data1",
                Column2 = "Data2",
                Column3 = "Data3",
                Column4 = "Data4",
                Column5 = "Data5",
                Column6 = "Data6",
                Column7 = "Data7",
                Column8 = "Data8",
                Column9 = "Data9",
                Column10 = "Data10",
                Column11 = "Data11",
                Column12 = "Data12",
                Column13 = "Data13",
                Column14 = "Data14",
                Column15 = "Data15",
                Column16 = "Data16",
                Column17 = "Data17",
                Column18 = "Data18",
                Column19 = "Data19",
                Column20 = "Data20",
                Column21 = "Data21",
                Column22 = "Data22",
                Column23 = "Data24",
                Column24 = "Data25",
                Column25 = "Data25",
                Column26 = "Data26",
                Column27 = "Data27",
                Column28 = "Data28",
                Column29 = "Data29",
                Column30 = "Data30"
            };

            data.Add(d);
        }

        this.uXGridView1.ItemsSource = data;
    }
}
        

Notice that the overall UXGridView performance is based on the view port, the smaller the view port the less objects need to be rendered, thus it will perform faster.

Adaptable Layout (Dynamic Row Height)

Since rows and cells can be represented in many ways, we included a nice feature to support dynamic row height in the virtualization logic. This feature enables you to use a wrapped textblock for the cell, or using a fluid template for the Row and RowDetail without compromising the virtualization feature. Several examples of this feature were already available in the downloadable CTP that you can play around.

To enable text wrapping in the cell, right now you need to customize the CellStyle and add a TextBlock with TextWrapping property enabled. In the next release, we will add a built-in property directly in the UXGridViewColumn to easily enable text wrapping.

Drag and Drop (Column and Group Reordering)

Drag-drop interaction is commonly found in a number of Grid operations such as column reordering, and group manipulation like adding group, removing group or reordering group. Thanks to ClientUI’s robust DragDrop Framework, we can quickly provide fluid drag-drop capabilities in the UXGridView. You can find more exciting UX features related to the drag-drop in the next and third milestone of the CTP release.

Data Grouping

If you are wondering why GroupDescriptors is not included in the QueryDescriptor, it’s because data grouping is considered to be a client operation rather than a server operation. To group a data in runtime, you drag a column header and drop it onto the GroupByBox element which you can enable through the GroupByBoxVisibility property.

You can also add some predefined group declaratively as follows.

<Intersoft:UXGridView Name="uXGridView1" GroupByBoxVisibility="Visible">                        
    <Intersoft:UXGridView.GroupDescriptors>
        <Intersoft:UXGridViewGroupDescriptor PropertyName="CategoryID"/>
        <Intersoft:UXGridViewGroupDescriptor PropertyName="SupplierID"/>
    </Intersoft:UXGridView.GroupDescriptors>                                    
</Intersoft:UXGridView>

Group Total and Aggregates

In addition to the data grouping, we also introduced GroupFooter which represent the aggregate values of a specific column for a specific group. There are five built-in aggregates supported in UXGridView which are: Average, Sum, Count, Min and Max. A more advanced, customized aggregate function is also planned in the next CTP release, which leverage the use of MVVM and multi-binding.

The following example shows how to enable the group footers, and specify the aggregate function for certain columns.

<Intersoft:UXGridView Name="uXGridView1" GroupByBoxVisibility="Visible"
                        GroupFootersVisibility="Visible">                        
    <Intersoft:UXGridView.GroupDescriptors>
        <Intersoft:UXGridViewGroupDescriptor PropertyName="CategoryID"/>
        <Intersoft:UXGridViewGroupDescriptor PropertyName="SupplierID"/>
    </Intersoft:UXGridView.GroupDescriptors>                    
    <Intersoft:UXGridView.Columns>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=ProductID}" 
            Header="Product ID" />
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=CategoryID}" 
            Header="Category ID"/>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=SupplierID}"
            Header="Supplier ID"/>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=ProductName}" 
            Header="Product Name"/>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=UnitPrice}" 
            Header="Unit Price" Aggregate="Avg"/>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=UnitsInStock}" 
            Header="Units In Stock" Aggregate="Sum"/>
        <Intersoft:UXGridViewTextColumn 
            Binding="{Binding Path=UnitsOnOrder}" 
            Header="Units On Order" Aggregate="Count"/>
    </Intersoft:UXGridView.Columns>
</Intersoft:UXGridView>

Upcoming UXGridView Features

In this post, you have learned some of the fundamental features available in UXGridView, such as virtualization, grouping, adaptable layout and drag-drop. We are currently topping off the rest of the features expected to ship in the CTP2 release due next week. One of the most exciting features is the unique data editing capability leveraging sophisticated custom editor concept and commanding. This will allow you to easily updating your data to the repository using MVVM design pattern. Stay tuned for our next announcement!

Best Regards,

Andry

UXGridView Part II: Data Access the MVVM-way with QueryDescriptor

As Jimmy wrote in his blog post, our upcoming Grid control for Silverlight and WPF, UXGridView, allows you to perform data operation with MVVM pattern elegantly using QueryDescriptor. In this post, I will explain some of fundamental concepts of the QueryDescriptor in more practical ways.

Understanding QueryDescriptor

First let’s take a look the QueryDescriptor class below.

QueryDescriptor Class

As you can see, the QueryDescriptor has three properties that hold the information about the query. It also has a QueryChanged event that will be raised when any of the QueryDescriptor’s properties are changed. The following illustration shows some examples on how query information is stored in the QueryDescriptor.

QueryDescriptor queryDescriptor = new QueryDescriptor();

// filtering
// get records that have 
// (UnitPrice >= 0 AND UnitPrice < 50) OR (UnitPrice == 0)
CompositeFilterDescriptorCollection groupFilter1 = 
    new CompositeFilterDescriptorCollection();

groupFilter1.LogicalOperator = FilterCompositionLogicalOperator.And;
groupFilter1.Add(
    new FilterDescriptor() 
    { 
        PropertyName = "UnitPrice", 
        Operator = FilterOperator.IsGreaterThanOrEqualTo, Value = 0 
    }
);
groupFilter1.Add(
    new FilterDescriptor() 
    { 
        PropertyName = "UnitPrice", 
        Operator = FilterOperator.IsLessThan, Value = 50 
    }
);

CompositeFilterDescriptorCollection groupFilter2 = 
    new CompositeFilterDescriptorCollection();

groupFilter2.LogicalOperator = FilterCompositionLogicalOperator.And;
groupFilter2.Add(
    new FilterDescriptor() 
    { 
        PropertyName = "UnitsInStock", 
        Operator = FilterOperator.IsEqualTo, Value = 0 
    }
);

queryDescriptor.FilterDescriptors.LogicalOperator = 
    FilterCompositionLogicalOperator.Or;

queryDescriptor.FilterDescriptors.Add(groupFilter1);
queryDescriptor.FilterDescriptors.Add(groupFilter2);

// paging
// get the record 6 - 10
queryDescriptor.PageDescriptor.PageSize = 5;
queryDescriptor.PageDescriptor.PageIndex = 1;

// sorting
// sort by category ascending then by product id descending
queryDescriptor.SortDescriptors.Add(
    new SortDescriptor() 
    { 
        PropertyName = "CategoryID", 
        Direction = System.ComponentModel.ListSortDirection.Ascending 
    }
);
queryDescriptor.SortDescriptors.Add(
    new SortDescriptor() 
    { 
        PropertyName = "ProductID", 
        Direction = System.ComponentModel.ListSortDirection.Descending 
    }
);    

With our data controls, the QueryDescriptor will be updated automatically whenever users perform data operations through our data controls such as paging, filtering and sorting. So all you need to do here is simply wiring up the QueryDescriptor to the data controls and listen to its QueryChanged event.

In the QueryChanged function delegate, you will need to parse the information in QueryDescriptor to a data operation command for your specific data source. Fortunately, our data provider libraries come with some methods allowing you to easily parse the QueryDescriptor into WCF RIA or DevForce data service.

Next, I will show you how to bind the QueryDescriptor to UXGridView, listen to its QueryChanged and perform the data operation.

Binding QueryDescriptor to UXGridView using MVVM Pattern

First, let’s create the ViewModel for our example.

using System.Collections.Specialized;
using Intersoft.Client.Data.ComponentModel;

namespace UXGridView.Samples.ViewModels
{
    public class ListProductsViewModel : ViewModelBase
    {
        public ListProductsViewModel()
        {
            this._queryDescriptor = new QueryDescriptor();
        }

        private INotifyCollectionChanged _products;
        private QueryDescriptor _queryDescriptor;

        public INotifyCollectionChanged Products
        {
            get { return this._products; }
            set
            {
                if (this._products != value)
                {
                    this._products = value;
                    this.OnPropertyChanged("Products");
                }
            }
        }

        public QueryDescriptor QueryDescriptor
        {
            get
            {
                return this._queryDescriptor;
            }
            set
            {
                if (this._queryDescriptor != value)
                {
                    if (this._queryDescriptor != null)
                        this._queryDescriptor.QueryChanged 
                            -= new System.EventHandler(OnQueryChanged);

                    this._queryDescriptor = value;
                    this._queryDescriptor.QueryChanged 
                        += new System.EventHandler(OnQueryChanged);

                    this.OnPropertyChanged("QueryDescriptor");
                }
            }
        }

        public virtual void LoadProducts()
        {

        }

        private void OnQueryChanged(object sender, System.EventArgs e)
        {
            this.LoadProducts();
        }
    }
}        

Notice that the LoadProducts() is still empty now, we’ll get to that later. Next we will bind this to our UXGridView in our View.

<Intersoft:UXPage
	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:Intersoft="http://intersoft.clientui.com/schemas"
        xmlns:ViewModels="clr-namespace:UXGridView.Samples.ViewModels"
	x:Class="UXGridView.Samples.Views.UXGridView.ListProducts"
	Title="ListProducts Page"
	d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <Grid.DataContext>
            <ViewModels:ListProductsViewModel/>
        </Grid.DataContext>

        <Grid MaxWidth="700" Margin="12">
            <Intersoft:UXGridView 
                Margin="8" AutoGenerateColumns="False" 
                ItemsSource="{Binding Products}"
                QueryOperation="Server"
                SortDescriptors="{Binding QueryDescriptor.SortDescriptors}"
                PageDescriptor="{Binding QueryDescriptor.PageDescriptor}" 
                PageSize="20" CanUserPage="True">
                <Intersoft:UXGridView.Columns>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Category ID" 
                        Binding="{Binding CategoryID}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Product ID" 
                        Binding="{Binding ProductID}" 
                        Aggregate="Count" FooterFormatString="Count = {0}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Product Name" 
                        Binding="{Binding ProductName}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Unit Price" 
                        Binding="{Binding UnitPrice}" 
                        Aggregate="Avg" FooterFormatString="Avg = {0}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Units In Stock" 
                        Binding="{Binding UnitsInStock}" 
                        Aggregate="Max" FooterFormatString="Max = {0}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Units On Order" 
                        Binding="{Binding UnitsOnOrder}" 
                        Aggregate="Min" FooterFormatString="Min = {0}"/>
                    <Intersoft:UXGridViewTextColumn 
                        Header="Quantity Per Unit" 
                        Binding="{Binding QuantityPerUnit}"/>
                </Intersoft:UXGridView.Columns>
            </Intersoft:UXGridView>
        </Grid>
    </Grid>
</Intersoft:UXPage>

Note that you will need to set the QueryOperation to Server to enable server-side data operation. That’s required because UXGridView also has the capability to manipulate the data at client side, which is the default setting. For now, let’s focus on the server-side query mode.

The QueryChanged event of the QueryDescriptor will be raised when it is bind to any of our data controls such as UXGridView, UXDataFilter or UXDataPager. The event will also be raised whenever there are changes in the QueryDescriptor, so it is the only place where you want to handle all data operations.

Now, let’s start processing this QueryDescriptor and retrieve a piece of data from our repository.

Parsing QueryDescriptor and Retrieving Data from WCF RIA

To parse the QueryDescriptor to WCF RIA, you need to include the Intersoft.Client.Data.Provider.Ria assembly in your project. This data provider assembly provides several methods that will allow you to easily parse the QueryDescriptor to the WCF RIA data service.

Now, let’s parse our QueryDescriptor and write some code to load the data in the LoadProducts() method that we’ve prepared in the previous section.

private NorthwindDomainContext _manager;
private NorthwindDomainContext Manager
{
    get
    {
        if (this._manager == null)
            this._manager = new NorthwindDomainContext();

        return this._manager;
    }
}

public virtual void LoadProducts()
{
    if (Intersoft.Client.Framework.ISControl.IsInDesignModeStatic)
        return;


    var query = this.Manager.GetProductsQuery()
                         .OrderBy(p => p.ProductID)
                         .Parse(this.QueryDescriptor);
    query.IncludeTotalCount = true;


    this.Manager.Load(
        query,
        op =>
        {
            if (op.IsComplete)
            {
                this.Products = new PagedCollectionView(op.Entities);
                if (op.TotalEntityCount != -1)
                    this.QueryDescriptor.PageDescriptor.TotalItemCount 
                        = op.TotalEntityCount;
            }
            else
            {
                // error handling
            }
        },

        true);
}

private void OnQueryChanged(object sender, System.EventArgs e)
{
    this.LoadProducts();
}
        

As you can see, the implementation is very straightforward, you just need to call .Parse(this.QueryDescriptor) to produce a query that WCF RIA can process. Note that IncludeTotalCount is set to True, which is important in paging scenarios as we will need to set the QueryDescriptor.PageDescriptor.TotalItemCount property to the total entity count of the particular query. This enables the data pager UI to determine the total number of pages available.

Parsing QueryDescriptor and Retrieving Data from Dev Force

In addition to the WCF RIA, you can also parse the QueryDescriptor to a DevForce service using similar approaches, with minor adjustments. First, you need to include the Intersoft.Client.Data.Provider.DevForce assembly in your project. This data provider provides similar methods as available in the RIA counterpart. Next, you need to adjust some of the code such as declaring the relavant DevForce type, see below.

private NorthwindEntities _manager;
private NorthwindEntities Manager
{
    get
    {
        if (this._manager == null)
            this._manager = new NorthwindEntities();

        return this._manager;
    }
}

public virtual void LoadProducts()
{
    if (Intersoft.Client.Framework.ISControl.IsInDesignModeStatic)
        return;

    this.Manager.Products
        .OrderBy(p => p.ProductID).Parse(this.QueryDescriptor)
        .ExecuteAsync(
        op =>
        {
            if (op.CompletedSuccessfully)
            {
                this.Products = new PagedCollectionView(op.Results);
            }
            else
            {
                // error handling
            }
        }
        );                 
}

public virtual void GetTotalItemCount()
{
    var queryCount = this.Manager.Products
        .Parse(this.QueryDescriptor, false).AsScalarAsync().Count();

    queryCount.Completed += (o, e) =>
    {
        if (e.Result != -1)
            this.QueryDescriptor.PageDescriptor.TotalItemCount = 
                e.Result;
    };    
}

private void OnQueryChanged(object sender, System.EventArgs e)
{
    this.GetTotalItemCount();
    this.LoadProducts();
}

Notice that you need to retrieve the total item count in two separate calls. This is required since DevForce handles the total item count retrieval differently.

I hope you agree that the QueryDescriptor makes MVVM and data service significantly easier and straightforward to implement. Also, remember that the QueryDescriptor can be shared across multiple controls such as UXDataFilter and UXDataPager, in addition to the UXGridView.

In my next post, I will cover about some nice Grid features that we shipped in the first CTP release. For now, download the CTP1 bits here, and enjoy building your data-centric application the MVVM way.

Best Regards,

Andry

Pivot Charting

Powerful data analysis and visualization with simple implementation

Hi all, I’m going to brief about the latest feature in WebGrid.NET Enterprise® 6.0 which called Pivot Charting. By definition Pivot Charting is a powerful data analysis and visualization tool that enables you to present your dataset in multiple viewpoints.

Don’t confuse it with more complex business intelligent application such as Microsoft SQL Server 2005 analysis. The featured offered in WebGrid.NET Enterprise® 6.0 is a more simplified approach more like Microsoft Access 2007®.

Pivot Charting is not enabled by default. You’ll need to perform several steps to enable it.

  1. Bind the WebGrid with a datasource. This datasource can be from a raw data retrieved using simple SELECT command, or a report data retrieved using complex query.
    pc11.jpgpc2.jpg
  2. After you bind the WebGrid with a datasource, the next thing to do is to decide how you would like your view mode. There are three types of mode available:

    pc3.jpg
    Allow Pivot Charting (with initial view = Grid View / Pivot Chart View)
    Allow Pivot Charting means that you will have an option to switch between GridView and PivotChartView

    WebGrid1.LayoutSettings.InitialView = GridInitialView.GridView;
    WebGrid1.AllowPivotCharting = true;

    pc4.jpg
    Pivot Chart View Exclusive
    Pivot Chart View exclusive means that the WebGrid will show only Pivot Chart View, you won’t be able to switch between views with this view mode.

    WebGrid1.LayoutSettings.InitialView = ISNet.WebUI.WebGrid.GridInitialView.PivotChartViewExclusive;

    Both View
    Both view means that you can show both Grid View and Pivot Chart View at the same time.

    WebGrid1.LayoutSettings.InitialView = ISNet.WebUI.WebGrid.GridInitialView.Both;
    WebGrid1.LayoutSettings.ChartDisplayPosition = ISNet.WebUI.WebGrid.ChartPosition.Top;

With just these two steps now you already have a Pivot Charting feature in your application.

Let say you use the following settings:

WebGrid’s data source : Northwind [Order Details]
WebGrid’s Initial View : PivotChartViewExclusive

You’ll get something like this:

pc5.jpg

Done! You’ve created your first basic Pivot Charting application. This view is still meaningless, since no data / settings are applied. Let’s start dragging some Field to drop zones.

  1. Drag and drop OrderID to Drop Data zone.
  2. Drag and drop EmployeeID to Drop Category zone.
  3. Drag and drop OrderDate (Years) to Drop Series zone.
  4. Try filtering the data based on EmployeeID by clicking the “down arrow” at EmployeeID’s Pivot Object.

pc6.jpg

Note that you can also specify this settings programmatically, so at start up the user will be shown this chart automatically.

That’s all for now, in the next post I’ll discuss more about built-in features in Pivot Charting.

VS2005 ToolBox Installation failed. Package ‘ToolBox Control Installer Package’ has failed to load property (GUID = {36839529-3AF3-47FA-8194-F4A3FA9F0ED7})

Some of our customers recently complaining about this error message during ToolBox Installation using 2007 R1 Installer which released on last 31 January 2007.

We’ve investigated what could cause this error intensively and found out that the VS2005 Tools.InstallCommunityControls package have high dependency with PC environment’s registry keys:

1. ToolBox out of sync.

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\UserToolBoxControls\

To manage the user controls in the Toolbox, Visual Studio maintains a list in the Registry. This list refers to directories under the <My Documents>\Visual Studio 2005\Controls\ folder. This list must contain existing directories. If this list contains entries for directories that do not exist, the Toolbox package fails.

 The following screenshots show out-of-sync configuration:

11.jpg

21.jpg

As you can see in figure 1 and 2, the registry under key:

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\UserToolBoxControls\

contains a value called NotExistingDirectoryName which does not exist in: C:\documents and settings\Username\My Documents\Visual Studio 2005\Controls\

To resolve this issue: Delete the registry values containing not existing directories from the registry. (In this case the NotExistingDirectoryName value)

2. Corrupted registration entries that manage the packages.

The second resolution comes from Chetan Chudasama’s Weblog:

http://blogs.msdn.com/chetanc/archive/2005/11/16/493495.aspx

If you have the SQL Server Management Studio Express CTP or SQL Server 2005, then it is overwriting some registry key settings. The workaround for this is:

  • Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\{36839529-3AF3-47fa-8194-F4A3FA9F0ED7}, change the CodeBase value to use the Visual Studio 8.0 location, for e.g. file:///C:\Program Files\Microsoft Visual Studio8\Common7\IDE\Microsoft.VisualStudio.ToolBoxControlInstaller.dll
  • Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\{36839529-3AF3-47fa-8194-F4A3FA9F0ED7}\SatelliteDll, change the Path value to point to the Common7\IDE folder for e.g. C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\
  • Open the Visual Studio Command Prompt and run devenv /setup

If you encounter this Package ‘ToolBox Control Installer Package’ issue, try to re-configure the registry as mentioned above. Furthermore you’ll need to add the controls manually to VS2005 toolbox using the following step :

  1. Open the Visual Studio Command Prompt and run devenv /setup
  2. run devenv /Command Tools.InstallCommunityControls

Currently we have implemented a new way to install the controls to toolbox, so you can have faster and smoother installation process. This new technique no longer depends on “devenv /setup” or “InstallCommunityControls”, so rest assured that these existing problems have been completely eliminated.  We will post this new installer in near future.