Thursday 27 January 2011

How to filter data correctly on a repeating table in an InfoPath form using a date range

If you have used InfoPath in your developments, you might have come across with the issue of filtering data using a date range, after the data is received to the form. To illustrate the issue I will use a sample InfoPath form.

Add two date pickers and name them ‘fldFrom’ and ‘fldTo’

screen_01

Add your data source to the form. To this sample I will use a SharePoint list. which contains ID, Created Date and the Version. Drag the data source to the InfoPath form and you will be prompt with three options

screen_02

Select the ‘Repeating Table’ option. And if you preview the form you can see a similar view shown below.

screen_03

Now we will add a rule to the repeating table so that the data will be filtered according to a given date range.

Select the data source and click on the ‘Manage Rules’ on the home tab.

screen_06

Add a formatting rule.

screen_07

And enter following to the condition.

screen_08

Click ok and select the ‘Hide this control’

screen_10

And if you preview your form and give the desired date range for the From and To dates. You can easily notice that the filtering of data is not what you have expected. In this example I want to list data, which the created date is between ‘01-Jan-2011’ and ‘14-Jan-2011’. But even I assign those values to the date pickers, I will not get data which was created on ‘14-Jan-2011’.

**Please note : In the InfoPath form that I have used, the date format is ‘M/D/YYYY’

screen_11

So prevent this issue, in the Rule we created earlier, we will use an expression. The logic of the expression is to convert the dates into a number, and compare them.

E.g.: We convert the date to a number format similar to ‘YYYYMMDD’ if the date is ‘01/01/2011’ after the conversion it will be ‘20110101

First change the dates formats to the following format [‘English (South Africa) and ‘YYYY/MM/DD’]

screen_12

And change the Condition we added earlier to the following expressions:

screen_13

The expressions should be :

number(concat(substring(d:Created, 1, 4), substring(d:Created, 6, 2), substring(d:Created, 9, 2))) < number(concat(substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 1, 4), substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 6, 2), substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 9, 2)))


number(concat(substring(d:Created, 1, 4), substring(d:Created, 6, 2), substring(d:Created, 9, 2))) > number(concat(substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 1, 4), substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 6, 2), substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 9, 2)))


And when you preview the form, initially all the records will be displayed, since there are no default values set on two date pickers. But when I change the dates the range I set earlier, I will get the desired  result shown below.


screen_14


Hope this might be useful to you.. Smile

Tuesday 18 January 2011

Repeating a SQL row based on a value in a different column

There are times that we get requirements such as populating and duplicate SQL rows, based on a value, on another column. E.g.: In an inventory system when items are received those details will be saved in the following format (ItemDetails) :

screen_01

And we are asked to create a GUI for end user to enter ‘Serial Numbers’ for each item. And we have to repeat the above mentioned item codes number of times which equals to the ‘ItemQty’. Of course we can achieve that using a SQL cursor or iterate using C# coding. But following example I will show how to do it using SQL.

The task would have been very simple if we would have a another table with a structure similar to this: (TempTable)

screen_02

So when the two table are joined ‘ItemDetails’ will repeat according to the row count of the ‘TempTable’. But it is not very practical, and it will result in duplicating data, which will grow your database un-necessary when time goes.

But instead we can use on single table which contains a series of numbers. These numbers will start from ‘1’. And the end should be the maximum quantity which an Item can have. For this example I will take ‘10’ as the maximum value. And that table should have the following structure.

screen_03

Use the following T-SQL statement to create the table:

CREATE TABLE [IntermediateTable](
[MaxQty] [int] NULL
) ON [PRIMARY]


For this example I have inserted up to 20. But in a real world scenario it may be required to enter values (More than 1000). In a similar situation you can use the following T-SQL statement to insert values.


insert into IntermediateTable
select
thousand.number*1000 +
hundred.number*100 +
ten.number*10 +
one.number
from(
select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) one
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) ten
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) hundred
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) thousand
where (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number) > 0 and (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number) <= 2000
order by (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number)



** Please Note : Above statement will insert values from ‘1’ to ‘2000’. But removing the where condition will insert values from ‘0’ to ‘10000’.


And using the following T-SQL statement we can join the table and produce the required result.


select A.*
from ItemDetails as A
join IntermediateTable as B on B.MaxQty <= A.ItemQty
where A.BatchNo = 'B1'


screen_04

Designing a windows form where size exceeds your screen’s resolution

Some times we need to design forms in our  applications, beyond the size of our development machines screen resolution. But the worse case is, our development environment does not allow us to resize our forms beyond our screen resolution. (Screen resolution – Borders to be exact).

One method is to do the development on the client resolution. But it’s not going to work all the time, if we have to do the development and send it across to a different physical location.

The other method is to insert a ‘Panel’ to the form, change it size to the one at the clients end. The one I am working at has the resolution of ‘1440 X 900’. But I want to make a form which fits for ‘1920 X 1200’. But I will change the Panel’s size to a bit less than ‘1920 X 1200’. Because I have to leave space for the borders and the scroll bars. So I would make it somewhere around ‘1870 X 1150’

And change the following form properties also :

  • AutoScaleMode = None
  • AutoScroll = True
  • AutoSize = True
  • WindowState = Maximized

 

And you can note that there are two scroll bars available at the design time, which you can scroll and place controls beyond your screens resolution. And when run on clients environment, the form will  maximize and since the panel is bit smaller than the actual screen size, the scroll bars will not be visible.

screen_01