Saturday, September 17, 2011

Applying Pageing and Sorting on ListBox Control

Background :
                    If you have ever used the data grid control then you are familiar with the sorting functionality which is provided by default, when you click any of the data grid header then it will sort the records according to the that column name on every click it will sort it ascending or descending direction. Similarly you are familiar with the data pager control which we often used with the data grid control to show record in the form of page which contain specific page size.It was one of the question which one of my colleague ask me "Can we apply paging to the List Box control ". As I didn't use data pager control other then data grid so in this post I have used the data page control and also the sorting which is provided by the PagedCollectionView.

Solution:
                For this example I have the application which you can see in Image 1, Here you can see that I have used combo box control which is used to sort the list box control item in ascending or descending order, List box control to display the records and the data pager control for the paging purpose.

Image 1

The use of paging is very simple it is just same as you do with the data grid control. Just place the data pager control in the xaml and set the Source property of the data pager to the list or collection which is used to bind the list box control, here you can see if I remove the data pager control then it will display all the record of the collection/list as you can see in the Image 2. So paging is quite easy similar to the data grid paging.

Image 2
Second part which is used to sort the items in the list box control is easy as well but you need to do some coding for it. For this I have done some coding in the view model of the home page which you can see in the List 1 here you can see I have posted whole view model as it is small view model not very large amount of coding in it. In the code you can see I have only two properties one is the PersonList (which is of type PagedCollectionView type which is used too bind the list box control ) and second one is the SortDirection property of type string ( which is used to sort the list box item according to the one of the value either ascending  or descending which are display in the combo box as inline items ).

List 1

In the constructor I have populated the PersonList PagedCollectionView by calling GetPersonRecord function of the person class ( the person class has on one property , the name property which is of type string and hold the person name). The main functionality of the sorting is done in the SortDirection property where I have used the SortDescriptions property of the PagedCollectionView to sort the records in the List.SortDescriptions collection of PagedCollectionView describes how items are sorted in view. SortDescriptions is a collection in which we can add more than one sort field. But here in our example as I have only one field which is the name field so I have first clear the SortDescriptions and then add the new sort direction, if you have more then one direction to sort the record then you don't need to call the clear function of the SortDescriptions you just add the new field in that and it will perform the sorting according to the field name which are provided in the list.We add sort field by adding SortDescriptions that specify which property will be used for sorting and the direction(ascending or descending order). When user select Ascending from the combo box then item are sorted from A to Z direction which you can see in the Image 3.

Image 3
When user select Descending from the combo box then item are sorted out from Z to A direction which you can see in the Image 4.

Image 4
I have tried to used the paging and sorting in the list box control and also tried to explain you how you can used it in your code if you have requirement to sort the list box control, you can event it it without giving the control to you user like I have provided the combo box and provided two option ascending and descending. You can download the source code from here.

All and any comments / bugs / suggestions are welcomed!

Friday, September 16, 2011

SelectedItems Using PagedCollectionView Using PropertyChanged

In my post "Get SelectedItems From DataGrid Using MVVM In Silverlight " I have used command and triggers to get the selected items of the datagrid control. Here we will get the selected item of the data grid control without using the command and trigger. Here we will use the PagedCollectionView to get the selected items. So that we can remove the use of the command and the triggers which we have used in the "Get SelectedItems From DataGrid Using MVVM In Silverlight" post and we will take advantage of INotifyPropertyChanged interface.

Solution :
           The solution is very simple as we don't want to use the command and the triggers but we want to achieve this on the property changed which you can achieve by implementing the INotifyPropertyChanged interface. The main page which we are using for this example is shown in the Image 1, here you can see that I have two similar data grid controls on the left side the source data grid control which is populated when the page is loaded and on the right side is the destination data grid control which will display the selected items from the source data grid control.

Image 1

First I would like to discuss the Persons class which I have used for this example which is listed in the List 1. Here you can see that I have inherit the Persons class from the INotifyPropertyChanged interface and also implemented the INotifyPropertyChanged event handler which you can see at the end of the class.I have First name, Last name, city , country and Is selected properties in the class. You can see that I have only raise the PropertyChanged event from the Is selected property as this proper is of type Boolean and I will perform selected of the record based on the true/ false value of IsSelected property. If IsSelected property has value true then record is selected else record is not selected.
List 1

Note: In List 1 I have only shown relevant which I wan to discuss here, function which return sample records has been removed but you can find the function on the sample application which you can download from the link which is provided at the end of the post
In the List 2 you can see the viewModel for the Home page which is used as the data context for the home view.Here you can see that I used two properties one is PersonList which is of type PagedCollectionView and used for the first gird which is place on the left side of the interface and which will be the source datagrid from where you can select record by clicking the check box control which is displayed in the first column of the data grid control.
List 2

Second property which is ObservableCollection type will hold the records for the second datagrid control which is shown on right side and act as destination and will only show records which are selected from the source datagrid and you can only view records in the destination data grid as the check box control property isEnabled is false and IsReady only property of the datagrid is also false. In the constructor I have initialized the PersonList which is of PagedCollectionView type by call the Persons GetPersonRecord function which is the static function and will return List of Persons( which you can find in the Persons class in the downloaded example code). In the next statement I have use foreach loop to attached the PropertyChanged event of the record as I have used the INotifyPropertyChanged interface in the Persons class So  I can now I have PropertyChanged Event for each record.
When you click any of the check box control in the source data grid the PropertyChanged event which is attached with every record and IsSelected property in the Persons class has raise the PropertyChanged event when the value is changed. In Image 2 you can see the sender and the arguments of the events here you can see that in the sender it will show you the whole record and in the arguments it will send the property name which is changed.In the argument 'e' there is name PropertyName which hold the name of the property here you can see that it has "IsSelected" as I have raised the event from IsSelected property.
Image 2

In the PropertyChanged event I have cost the sender to the respective type in this case my class name is "Persons"  so I have created object of type Persons and save the sender in that object. Then in the next statement I have check for the IsSelected value if the value is true then I have added the sender to the SelectedList property which is used to bind to the destination datagrid to show the selected item.

Image 3

When you select records from the source data grid then you can see the selected item in the destination datagrid. For tested purpose I have selected random 3 items from the source data grid and the selected items are shown in the destination data grid as shown in the Image 3 above. If you unchecked the any of the selected record then that record will be removed from the destination datagrid.You can download the source code from here and also test yourself.

Note: In the Persons class you have seen that I have only raised the PropertyChanged event from the IsSelected property because I want to do procession on the basis of the IsSelected property value. If you changed any of the property like First name, Last Name, Country and city then PropertyChanged event will not fires as these properties didn't raise PropertyChanged event in the setter.


All and any comments / bugs / suggestions are welcomed!

Sunday, September 11, 2011

Using PagedCollectionView To Group DataGrid Records Using MVVM

Problem :
          When using data grid control sometime we provide grouping of data so that user can see records in groups but the groups are predefine and If you don't want to see the group which we the developer or the client provide then he/she can't change. So here  we can use approach to provide the grouping of data to the end user if he/she want to see the data grid records in groups then he/she can arrange records in groups but if he/she don't want to see the he/she can remove the grouping of records.

Solution :
             Solution for the above problem again is in the use of the PagedCollectionView type and the sample application which is used for this is shown in the Image 1, here you can see that I have combo box with contains the columns name like I have place country and city which are used to group the data grid records. and one check box control which I will explain when I will discuss the code used to group the datagrid control.


Image 1

Code used to group the data grid record into group is show in the List 1, here you can see that I have function with name ArragePersonListByGroup which is called from the two separate location. In the viewModel I have define two properties one is the GroupBy which is of type string to hold the name of the by which we will group data grid records and in the setter of the GroupBy I have called the ArragePersonListByGroup function so that whenever user change the name from the combo box the grouping is changed.Second property IsNestedGroupAllowed which is of type Boolean type is used for the nested group mean if user want to see first group by country name then in the country he/she want to group by city then user first checked IsNestedGroupAllowed check box then nested grouping is done but if the IsNestedGroupAllowed is unchecked then nested grouping is not done only one level of grouping will done.

List 1

In the ArragePersonListByGroup function you see the first statement is to check the GroupBy property name to not equal to the "Select Column Name" mean if user select this option then he/she want to remove the grouping from the data grid records as the else statement you can see that I have clear the GroupDescriptions of the PersonList (which is of PagedCollectionView). For grouping we have used the GroupDescriptions property of the PagedCollectionView which provides a base class for defining how to divide the items in a collection into groups. After checking the GroupBy value to not equal to the "Select Column Name" again If condition is placed to check the IsNestedGroupAllowed ( to check if the user want to multiple level of grouping or not ) property and the null value ( as if no value is selected when application start then GroupBy has null value). First I have not checked the IsNestedGroupAllowed check box so the else part is executed where GroupDescriptions property of the PersonList (which is of PagedCollectionView type) is clear so that if any GroupDescriptions is already added then it should be removed and the new one will be added in the next statement. Now when user select column name from the combo box and check box Nested Group is unchecked then you can see the output in the Image 2.

Image 2

Now if user changed the column name from Country to City then grouping will be on the basis of the City name of the records and if user select "Select Column Name" then grouping will be clear.
Now the next case when user want multiple level of grouping in the records which you can see in the Image 3, here you can see that Nested Group checkbox is checked and first I have select country and then city from the combo box. You can see that first records are grouped in country name and within the country name the records are grouped with city name.

Image 3
The code for the multiple level of the grouping is listed in List 1, here you can see that if the IsNestedGroupAllowed property is true (when user checked the Nested Group checkbox from the UI) and GroupBy property is not null or empty is false. In the if condition is true mean user want multiple level of grouping then first I have check the GroupDescriptions collection if the selected GroupBy is already exist in the GroupDescriptions list or not if GroupBy is not already added then add the GroupBy in the GroupDescriptions collection.

Note : Here you have to check the group name if you didn't check for the already added group then it will add group multiple time as many as you have added it, So it is better to check as I have check in my code so that one group is added once if user selected it multiple times.

I have provided only two level of grouping of the records if you have more columns then you can provide it to more level of grouping.It is better to give as much functionality to end user as if he/she want to see the records in groups , he/she can arranged it into groups and in to multiple level as well.You can download the source code from here.

Note : You have to check for the null value of the group name if you didn't set the default value of the combo box (in my case I have used combo box to show column names) when application run and end user directly check the Nested Group check box (as no column is selected at that time, then a group with PagedCollectionView_Group.Model.Person (Person is the class name of the collection which I have used) is added.
All and any comments / bugs / suggestions are welcomed!

Saturday, September 10, 2011

Using PagedCollectionView Filter Using MVVM

Problem :
          When using data grid control there may be large amount of records in the data grid and to find out record which user is search is very difficult as he/she has to view every record to find out the desired record. So it is nice idea to have filter on the data grid records so the user can filter data grid records and find what he/she is looking in the data grid records

Solution :
          To provide the end user with some sort of filtering of the datagrid record we will use the Filter property of the PagedCollectionView. Example which is used for the filter property of the PagedCollectionView is shown in the Image 1, here you can see that I have used checkbox control to filter the active and in-active records in the datagrid control, combo box control to provide the searching of the First name in the data grid control and it has 3 values StartWith which is used to find the text entered in the text box at the start of the first name in the list, EndWith which is use to find the text entered in the text box at the end of the first name and 3rd one the Contains checks whether the specified text box  character combination occurs within first name. And the text box control used to entered any combination of character to search in the first name of the record.

Image 1

Code used for the filtering of the datagrid record is shown in the List 1. Here I have listed imported code which is only used to filter the record not the properties of the viewModel. In the viewModel I have null-able Boolean property for the Active which hold the value true ( active record), false (for in-active record) and null ( for all records). FirstNameOperator property which is used to hold the string value for the comparison of the first name property like StartWith, EndWith and Contains are the values which are shown in the combo box are selected in the FirstNameOperator. And FirstName property which is used to hold the user input for search the search first name.
List 1
You will notice that, the PagedCollectionView has a property called “Filter” which takes Predicate. The predicate will take callback which will provide the filtering logic. This callback method examines each row and indicates if the row should be included or excluded by returning true or false. Here, our PagedCollectionView is our collection named “Customer”. First of all set the Filter of this collection to null to remove the previous filtering. Then set a new Predicate function call to it.
The callback function FilterCustomerList takes the object first we need to convert the object to corresponding type here in our case as our PagedCollectionView is of type Customer so we need to convert it to Customer type.Next I have check for the active or in-active record if the value of the IsActive property is not null as in case of null we don't need to see the active or in-active records.If value is not null and the currentRecord which is passed to the callback , if Active property is not equal to the value of the IsActive then return false mean I don't want to include this record in the final output. I have used the false value to exclude the current record from the final list as you can see that at the end of the callback I have return true to include the record in the final list.When user checked or unchecked the checkbox the output is similar to the what has be shown in the Image 2, here you can see that only active records are shown as checkbox is checked, if you unchecked the checkbox then in-active records are shown.
Image 2

Second filter criteria which is the to filter by first name of the customer.The first name of the customer is the string type so we need to perform string operation on the first name field of the customer record.To check the first name of the customer I have created separate function to check the first name of the customer record which take the customer record and then perform the string operation like start with, end with and contain. Here you can see that I have used blnIsValid of type Boolean and set it to false mean record is not matched and will be excluded from the list. Then I have check the FirstNameOperator property to perform what user want to check in the first name first name field, mean user want to say e.g If use enter 'e' character in the text box and want to see the records which are start with the character 'e' then he/she will select StartWith operation, or user want to see the record which are end with character 'e' then he/she will select EndWith from the operation or if he/she want to see customer records which contain the character 'a' then he/she will select Contains operator. Output of the filter when user filter by first name is shown in the Image 3, here you can see that I have not used the active filter criteria.

Image 3
The final output when user use both active and first name criteria is shown in the Image 4, Here you can see that I have use the active records and then I have used the Contains operation for searching in the first name of the customer record and I have entered character 'e' to search in the first name. In the Image 3 you can see that I have search the records by entering the character 'e' in the text box control and used the Contains operator and we have total 5 records displayed in the Image 3 one is in-active and remain 4 are active records. And in Image 4 you can see I have only select the active record by checking the checkbox and the first name criteria is same as I have used in the Image 3.

Image 4
Hope you get idea of how to filter the data grid records when you have large amount of records are showing in the data grid control, by providing the filter criteria to filter the records end user can find the desired record quickly and hence he/she can save his/her time which he/she has to spend in order to search the specific record in the data grid control.You can download the source code from here.

All and any comments / bugs / suggestions are welcomed!

Sunday, September 4, 2011

Make a Silverlight TextBox update its binding on every character

Problem :
              One of the problem which I have faced during my learning and working in MVVM pattern and one of requirement when you have provided the search box as user type in some character in text box control you want to show results in the data grid control. But in silverlight there is only two option when you bind a property to update the source Default and Explicit. There is no PropertyChanged option which is provided in the WPF.

Solution : 
              For the problem I have created sample application to discuss how to get this working if you have similar situation mean there may be requirement from your client to provide such functionality mean as client type in some character the result should displayed in the some control link datagrid which I have used. In the example I have used one text box control ( for user to type contact name ) and a datagrid control to show the matching result of the user search. You can see the layout of the example in Image 1, I have home page which contain the problem and the working example which is working fine as user type in character in the text box matching results are filtered in the data grid control.

Image 1
Example 1:
                For example 1 which is shown in the Home page link you can see I have used the normal binding of the text box control. The code for the first example is shown in the List 1. Here you can see that I have not set UpdateSourceTrigger which is default mean when control lost focus then latest value is send back to the view model as binding mode is TwoWay.  I have created two view models for home and the Working Example view but the code is nearly same in both view models. The change will be in the xaml of the both the views.

List 1
Example 2: 
             For Example 2 which is shown in working example page link here you can see  as user is typing in the contact name text box records in the datagrid are filtered.The xaml code for the Example 2 is shown in the List 2. Here you can see that I have used the Behavior for updating the source whenever user type in some character in the text box.


List 2

Note: I have also tried to use the event trigger to solve this problem but in event trigger the problem is it didn't send last character entered by the user. For exam if user enter character 'AN' then it will send 'A' back to the source not 'N' which is the last character entered by the user. That is why I didn't use that solution as end user will not get latest result because of last character exclusion. 

By using the above technique mean using the Behavior my problem of filtering the record on every key press was solved hope if you have requirement like this you can use same technique to solve it.You can download the source code from here.

All and any comments / bugs / suggestions are welcomed!