String Formating in VB

0 comments

VB format syntax
Using the Visual Basic format() function
The Format function converts a value to a text string and gives you control over the string's appearance. For example, you can specify the number of decimal places for a numeric value, leading or trailing zeros, currency formats, and portions of the date. The syntax is:
Syntax
Format(expression [,format [,firstdayofweek [,firstweekofyear]]])
expression
Any valid expression
format
A valid named or user-defined format expression
firstdayofweek
A constant that specifies the first day of the week
firstweekofyear
A constant that specifies the first week of the year The firstdayofweek argument has these settings: Constant Value Description
vbUseSystem 0 Use NLS API setting.
vbSunday 1 Sunday (default)
vbMonday 2 Monday
vbTuesday 3 Tuesday
vbWednesday 4 Wednesday
vbThursday 5 Thursday
vbFriday 6 Friday
vbSaturday 7 Saturday

The firstweekofyear argument has these settings: Constant Value Description
vbUseSystem 0 Use NLS API setting.
vbFirstJan1 1 Start with week in which January 1 occurs (default).
vbFirstFourDays 2 Start with the first week that has at least four days in the year.
vbFirstFullWeek 3 Start with the first full week of the year.

Notes
If you try to format a number without specifying format, Format provides functionality similar to the Str function, although it is internationally aware. However, positive numbers formatted as strings using Format don’t include a leading space reserved for the sign of the value; those converted using Str retain the leading space.
If you are formatting a non-localized numeric string, you should use a user-defined numeric format to ensure that you get the look you want.
If the Calendar property setting is Gregorian and format specifies date formatting, the supplied expression must be Gregorian. If the Visual Basic Calendar property setting is Hijri, the supplied expression must be Hijri.
If the calendar is Gregorian, the meaning of format expression symbols is unchanged. If the calendar is Hijri, all date format symbols (for example, dddd, mmmm, yyyy) have the same meaning but apply to the Hijri calendar. Format symbols remain in English; symbols that result in text display (for example, AM and PM) display the string (English or Arabic) associated with that symbol. The range of certain symbols changes when the calendar is Hijri.
Formatting Symbols
Character Description
None
No formatting Display the number with no formatting.
: Time separator. In some locales, other characters may be used to represent the time separator. The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings.
/ Date separator. In some locales, other characters may be used to represent the date separator. The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings.
C Display the date as ddddd and display the time as t t t t t, in that order. Display only date information if there is no fractional part to the date serial number; display only time information if there is no integer portion.
D Display the day as a number without a leading zero (1 - 31).
dd Display the day as a number with a leading zero (01 - 31).
ddd Display the day as an abbreviation (Sun - Sat).
dddd Display the day as a full name (Sunday - Saturday).
ddddd Display the date as a complete date (including day, month, and year), formatted according to your system's short date format setting. The default short date format is m/d/yy.
dddddd Display a date serial number as a complete date (including day, month, and year) formatted according to the long date setting recognized by your system. The default long date format is mmmm dd, yyyy.
w Display the day of the week as a number (1 for Sunday through 7 for Saturday).
ww Display the week of the year as a number (1 - 53).
m Display the month as a number without a leading zero (1 - 12). If m immediately follows h or hh, the minute rather than the month is displayed.
MM Display the month as a number with a leading zero (01 - 12). If m immediately follows h or hh, the minute rather than the month is displayed.
MMM Display the month as an abbreviation (Jan - Dec).
MMMM Display the month as a full month name (January - December).
q Display the quarter of the year as a number (1 - 4).
y Display the day of the year as a number (1 - 366).
yy Display the year as a 2-digit number (00 - 99).
yyyy Display the year as a 4-digit number (100 - 9666).
h Display the hour as a number without leading zeros (0 - 23).
hh Display the hour as a number with leading zeros (00 - 23).
n Display the minute as a number without leading zeros (0 - 59).
nn Display the minute as a number with leading zeros (00 - 59).
s Display the second as a number without leading zeros (0 - 59).
ss Display the second as a number with leading zeros (00 - 59).
t t t t t Display a time as a complete time (including hour, minute, and second), formatted using the time separator defined by the time format recognized by your system. A leading zero is displayed if the leading zero option is selected and the time is before 10:00 A.M. or P.M. The default time format is h:mm:ss.
AM/PM Use the 12-hour clock and display an uppercase AM with any hour before noon; display an uppercase PM with any hour between noon and 11:59 P.M.
am/pm Use the 12-hour clock and display a lowercase AM with any hour before noon; display a lowercase PM with any hour between noon and 11:59 P.M.
A/P Use the 12-hour clock and display an uppercase A with any hour before noon; display an uppercase P with any hour between noon and 11:59 P.M.
a/p Use the 12-hour clock and display a lowercase A with any hour before noon; display a lowercase P with any hour between noon and 11:59 P.M.
AMPM Use the 12-hour clock and display the AM string literal as defined by your system with any hour before noon; display the PM string literal as defined by your system with any hour between noon and 11:59 P.M. AMPM can be either uppercase or lowercase, but the case of the string displayed matches the string as defined by your system settings. The default format is AM/PM.
0
Digit placeholder Display a digit or a zero. If the expression has a digit in the position where the 0 appears in the format string, display it; otherwise, display a zero in that position. If the number has fewer digits than there are zeros (on either side of the decimal) in the format expression, display leading or trailing zeros. If the number has more digits to the right of the decimal separator than there are zeros to the right of the decimal separator in the format expression, round the number to as many decimal places as there are zeros. If the number has more digits to the left of the decimal separator than there are zeros to the left of the decimal separator in the format expression, display the extra digits without modification.
#
Digit placeholder
Display a digit or nothing. If the expression has a digit in the position where the # appears in the format string, display it; otherwise, display nothing in that position. This symbol works like the 0 digit placeholder, except that leading and trailing zeros aren't displayed if the number has the same or fewer digits than there are # characters on either side of the decimal separator in the format expression.
.
Decimal placeholder
In some locales, a comma is used as the decimal separator. The decimal placeholder determines how many digits are displayed to the left and right of the decimal separator. If the format expression contains only number signs to the left of this symbol, numbers smaller than 1 begin with a decimal separator. If you always want a leading zero displayed with fractional numbers, use 0 as the first digit placeholder to the left of the decimal separator instead. The actual character used as a decimal placeholder in the formatted output depends on the Number Format recognized by your system.
%
Percent placeholder The expression is multiplied by 100. The percent character (%) is inserted in the position where it appears in the format string.
,
Thousand separator In some locales, a period is used as a thousand separator. The thousand separator separates thousands from hundreds within a number that has four or more places to the left of the decimal separator. Standard use of the thousand separator is specified if the format contains a thousand separator surrounded by digit placeholders (0 or #). Two adjacent thousand separators or a thousand separator immediately to the left of the decimal separator (whether or not a decimal is specified) means “scale the number by dividing it by 1000, rounding as needed.” You can scale large numbers using this technique. For example, you can use the format string “##0,,” to represent 100 million as 100. Numbers smaller than 1 million are displayed as 0. Two adjacent thousand separators in any position other than immediately to the left of the decimal separator are treated simply as specifying the use of a thousand separator. The actual character used as the thousand separator in the formatted output depends on the Number Format recognized by your system.
:
Time separator In some locales, other characters may be used to represent the time separator. The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings.
/
Date separator In some locales, other characters may be used to represent the date separator. The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings.
E- E+ e- e+
Scientific format If the format expression contains at least one digit placeholder (0 or #) to the right of E-, E+, e-, or e+, the number is displayed in scientific format and E or e is inserted between the number and its exponent. The number of digit placeholders to the right determines the number of digits in the exponent. Use E- or e- to place a minus sign next to negative exponents. Use E+ or e+ to place a minus sign next to negative exponents and a plus sign next to positive exponents.
- + $ ( ) space
Display a literal character To display a character other than one of those listed, precede it with a backslash (\) or enclose it in double quotation marks (” “).
\
Display the next character in the format string Many characters in the format expression have a special meaning and can't be displayed as literal characters unless they are preceded by a backslash. The backslash itself isn't displayed. Using a backslash is the same as enclosing the next character in double quotation marks. To display a backslash, use two backslashes (\). Examples of characters that can't be displayed as literal characters are the date- and time-formatting characters (a, c, d, h, m, n, p, q, s, t, w, y, and /:), the numeric-formatting characters (#, 0, %, E, e, comma, and period), and the string-formatting characters (@, &, <, >, and !).
“ABC”
Display the string inside the double quotation marks To include a string in format from within code, you must use Chr(34) to enclose the text (34 is the character code for a double quotation mark).
@
Character placeholder Display a character or a space. If the string has a character in the position where the @ appears in the format string, display it; otherwise, display a space in that position. Placeholders are filled from right to left unless there is an ! character in the format string. See below.
&
Character placeholder Display a character or nothing. If the string has a character in the position where the & appears, display it; otherwise, display nothing. Placeholders are filled from right to left unless there is an ! character in the format string. See below.
<
Force lowercase Display all characters in lowercase format.
>
Force uppercase Display all characters in uppercase format.
!
Force left to right fill of placeholders The default is to fill from right to left.

Named Formats
Visual Basic provides several standard formats to use with the Format function. Instead of using symbols, you specify these formats by name in the format argument of the Format function. Always enclose the format name in double quotation marks (“”). The following table lists the format names you can use. Named format Description
General Number Shows numbers as entered.
Currency Shows negative numbers inside parentheses.
Fixed Shows at least one digit.
Standard Uses a thousands separator.
Percent Multiplies the value by 100 with a percent sign at the end.
Scientific Uses standard scientific notation.
General Date Shows date and time if expression contains both. If expression is only a date or a time, the missing information is not displayed.
Long Date Uses the Long Date format specified in the Regional Settings dialog box of the Microsoft Windows Control Panel.
Medium Date Uses the dd-mmm-yy format (for example, 03-Apr-93)
Short Date Uses the Short Date format specified in the Regional Settings dialog box of the Windows Control Panel.
Long Time Shows the hour, minute, second, and “AM” or “PM” using the h:mm:ss format.
Medium Time Shows the hour, minute, and “AM” or “PM” using the “hh:mm AM/PM” format.
Short Time Shows the hour and minute using the hh:mm format.
Yes/No Any nonzero numeric value (usually - 1) is Yes. Zero is No.
True/False Any nonzero numeric value (usually - 1) is True. Zero is False.
On/Off Any nonzero numeric value (usually - 1) is On. Zero is Off.

Multiple Formats
A user-defined format expression can have from one to four sections separated by semicolons. (If the format argument contains one of the named formats, only one section is allowed.) If you use The result is
One section The format expression applies to all values.
Two sections The first section applies to positive values and zeros, the second to negative values.
Three sections The first section applies to positive values, the second to negative values, and the third to zeros.
Four sections The first section applies to positive values, the second to negative values, the third to zeros, and the fourth to Null values.

The following example has two sections: the first defines the format for positive values and zeros; the second section defines the format for negative values.
$#,##0;($#,##0)
If you include semicolons with nothing between them, the missing section is printed using the format of the positive value. For example, the following format displays positive and negative values using the format in the first section and displays “Zero” if the value is zero.
$#,##0;;\Z\e\r\o
Note If you try to format a number without specifying format, Format provides the same functionality as the Str function. However, positive numbers formatted as strings using Format lack the leading space reserved for displaying the sign of the value; whereas, those converted using Str retain the leading space.
Examples
The following conversions assume that the country in the Windows Control Panel is set to “English (United States).” Format syntax Result
Format(8315.4, “00000.00”) 08315.40
Format(8315.4, “#####.##”) 8315.4
Format(8315.4, “##,##0.00”) 8,315.40
Format(315.4, “$##0.00”) $315.40
Format(7, “0.00%”) 700.00%
Format(“This Is A Test”, “<”) this is a test
Format(“This Is A Test”, “>”) THIS IS A TEST
Format(Now, “m/d/yy”) 1/27/93
Format(Now, “dddd, mmmm dd, yyyy”) Wednesday, January 27, 1993
Format(Now, “d-mmm”) 27-Jan
Format(Now, “mmmm-yy”) January-93
Format(Now, “hh:mm AM/PM”) 07:18 AM
Format(Now, “h:mm:ss a/p”) 7:18:00 a
Format(Now, “d-mmmm h:mm” 27-January 7:18
Format(Now, “d-mmmm-yy”) 27-January-93
Format(Now, “d mmmm”) 27 January
Format(Now, “mmmm yy”) January 93
Format(Now, “hh:mm AM/PM”) 08:50 PM
Format(Now, “h:mm:ss a/p”) 8:50:35 p
Format(Now, “h:mm”) 20:50
Format(Now, “h:mm:ss”) 20:50:35
Format(Now, “m/d/yy h:mm”) 1/27/93 20:50
Format (format) Positive 5 Negative 5 Decimal .5 Null
Zero-length string 5 -5 0.5
0 5 -5 1
0.00 5.00 -5.00 0.50
#,##0 5 -5 1
#,##0.00;;;Nil 5.00 -5.00 0.50 Nil
$#,##0;($#,##0) $5 ($5) $1
$#,##0.00;($#,##0.00) $5.00 ($5.00) $0.50
0% 500% -500% 50%
0.00% 500.00% -500.00% 50.00%
0.00E+00 5.00E+00 -5.00E+00 5.00E-01
0.00E-00 5.00E00 -5.00E00 5.00E-01

Credit
Most of this is a re-hash of the documentation available from Microsoft. But Microsoft has its Format() documentation spread out in a number of loosely related entries, instead of being in one place.

Calnder using PHP and Ajax

0 comments

Online calendars are often used in many web applications. Though popular, the logic behind creating a calendar can be scary especially for those who are new to programming. There are many web calendars in the market but some of them are quite complicated. If we are not able to understand the code, it becomes harder for us to customise the calendar to fit into our existing application. As such, we need to create a calendar that can plug itself into any system seamlessly without problems. Whether we are using Wordpress, Mambo/Joomla or Drupal, we should only need to insert one line into our code for the calendar to work. ie something like this:

require_once('quick_calendar.php');
?>

If you are already bored at this point or not interested to know how to create a web calendar, feel free to see a live demo of the simple calendar here. The installation procedures is in the source code.

Other than configuring the database access for the calendar, I do not want to change other things. With AJAX, I could even make the page static as I navigate between different months in the calendar. In this tutorial, I would like to share with you a simple calendar I created that fufills the objectives discussed above. The tutorial assumes that you have basic knowledge of PHP and SQL but don't worry, the actual code is minimal and you should be able to customise it easily by reading at the comments. I used PHP 4 so that it is compatible with most servers. You should also be able to re-create the calendar easily in other programming languages using the same logic.

The First Step: Problem Identification
Perhaps the hardest part in creating a calendar is to come up with a good solution to display the days of the month in the correct column, ie Monday, Tuesday..etc.

Apr 2006
Sun Mon Tue Wed Thur Fri Sat
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

Let us take April 2006 for example. There are 30 days and 6 rows in the calendar. If we are given a day in the month, say 15, we have to know that it falls on a saturday and is in the third row (third week). We cannot take for granted that the first day is always the first cell in the table (top left cell). Sometimes, we get 4 or 5 weeks in a month. Only if we know how many days are there in a certain month and which day the first of the month falls in only can we construct the calender as shown above.

Getting Crucial information From The PHP Date function
PHP provides a date() function that gives us alot of useful information about the days and months of the year. To built the calendar for any month, We need 2 important pieces of information from the Date function, ie the "number of days in the month" and a "numeric representation of the first day of the month".

I can get today's date easily from the following code:

// get year, eg 2006$year = date('Y');// get month, eg 04$month = date('n');// get day, eg 3$day = date('j');To get the number of days in this month, I will use the both the date and mktime function like so:

// get number of days in month, eg 28$daysInMonth = date("t",mktime(0,0,0,$month,1,$year));The numeric representation of the day of the week ranges from 0 to 6. 0 is sunday and 6 is saturday. Again, to get the numeric first day of this month, the function mktime comes in handy.

// get first day of the month, eg 4$firstDay = date("w", mktime(0,0,0,$month,1,$year));The Monthly Calendar As A 2-D Array
If we look at the calendar again for April 2006, we will see that it is actually a table(grid) filled with values starting from 1 to x (no of days in the month). The first day of the month is a variable though... It can occur in any day of the week. In the table, imagine each cell as having coordinates (x,y), starting from the top left cell as (0,0) and the bottom right cell as (5,6). In the month of April, the first day of the month is stored in coordinate (0,6). So, the plan now is to store the days of the month in a 2-D Array.

Firstly, we want to know the number of cells needed.

// calculate total spaces needed in array$tempDays = $firstDay + $daysInMonth;Then we want to know the number of rows needed.

// calculate total rows needed$weeksInMonth = ceil($tempDays/7);Populating The 2-D Array
Knowing the number of rows and columns in the 2-D array, we can now fill the arrays with values using 2 for-loops. The first cell will start with a value of 1 and the subsequent cells will have their values increased by 1 till it reaches the end of the array.

for($j=0;$j<$weeksInMonth;$j++) {
for($i=0;$i<7;$i++) {
$counter++;
$week[$j][$i] = $counter;
}
}
?>
For the month of April, the temporary array should be something like this:

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 32 33 34 35
36 37 38 39 40 41 42
The Magic Offset
As you can see, the array above is not correct. The first day of April, ie value 1 should be in (0,6) instead of (0,0). Remeber the variable $firstDay? It is the numeric representation of the first day of month which happens to be 6 in April 2006. If we subtract $firstDay from all the values in the array, we will get the array as follows:

-5 -4 -3 -2 -1 0 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 32 33 34 35 36

You should now be able to guess what we are going to do next. Looking at the array above, you see that we already got the values we want but we also have some unwanted values. Any number less than 1 or more than $daysInMonth (number of days in a month) should be ignored.

function fillArray() {
// create a 2-d array
for($j=0;$j<$this->weeksInMonth;$j++) {
for($i=0;$i<7;$i++) {
$counter++;
$this->week[$j][$i] = $counter;
// offset the days
$this->week[$j][$i] -= $this->firstDay;
if (($this->week[$j][$i] < 1) || ($this->week[$j][$i] > $this->daysInMonth)) {
$this->week[$j][$i] = "";
}
}
}
}
?>
This is the core function in the entire calendar generation algorithm.

Displaying The Calendar
Getting the values right in the 2-D array, we are now ready to display them. Now, we will create a table and start looping again using the foreach function.


$val) { echo ""; for ($i=0;$i<7;$i++) { echo ""; } echo "";}?>
Sun Mon Tue Wed Thur Fri Sat
$date

The final display will be like this:

Apr 2006
Sun Mon Tue Wed Thur Fri Sat
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

We now have a plain calendar.

Adding Special Dates To The Calendar
This calendar only tells you "the days of a week" at the moment and is not very useful. Most online calendar will have reminders as well. Say for example, if my birthday falls on the 4th of April, I want the number 4 in the calendar be displayed differently, possible with a hyperlink in which upon clicking on it, will perform some task like redirecting me to a certain page or displaying more information about myself in a popup windows..etc. To do that, we need to a table in the database with at least 6 columns: id, day, month, year, link, desc.

CREATE TABLE calendar (
id INT NOT NULL AUTO_INCREMENT ,
day VARCHAR( 2 ) NOT NULL ,
month VARCHAR( 2 ) NOT NULL ,
year VARCHAR( 4 ) NOT NULL ,
link VARCHAR( 255 ) NOT NULL ,
desc TEXT NOT NULL ,
PRIMARY KEY ( id )
);
We then need to insert some data into the table for testing:

INSERT INTO calendar ( id , day , month , year , link , desc )
VALUES (
'', '24', '*', '2006', 'http://www.sitecritic.net', 'Check your web cccount on the 24th of every month. 2006 only!'
), (
'', '5', '11', '2006', 'some_javascript_funtion', 'Olympics, remember to buy ticket from alice.'
),(
'', '2', '1', '2007', 'some_javascript_funtion', 'early 2007. Any new plans for the year?'
), (
'', '9', '*', '*', 'http://www.evolt.org', 'Remember to check updates from evolt.org every month.'
);
The * under the month or year column means every month or year.

Next, we do a query and extract the important dates for a certain month and store it in an array.

$sql = "SELECT * FROM calendar WHERE (month='$month' AND year='$year') || (month='*' AND year='$year') || (month='$month' AND year='*') || (month='*' AND year='*')";
$rs = $db->query($sql);
while ($rw = $rs->fetchRow()) {
extract($rw);
$links[] = array('day'=>$day', 'month'=>$month, 'year'=>$year, 'link'=>$link, 'desc'=>$desc);
}
?>
If we create a class to generate the calendar, we need to pass the $links array into the class like so:

$cal = &new Calendar($cArray, $today, $links, $css);
$cal->render();
?>
The $cArray is a class containing the array for the plain calendar as shown in step 6. The $today variable is today's date. The $links variable contains the important dates in this month. With the $css variable, we can decorate the calendar table and make it look nicer.

Adding AJAX Capability
To make the calendar more user friendly, we want to be able to navigate easily between the months or years without refreshing the page. Thanks to AJAX, we can now do that easily. If the user clicks on "next month" for example, we need to call the AJAX function to refresh the calendar without refreshing the page. We do that using XMLHttpRequest. This is the main code that does the trick.

http.open('get', 'quick_calendar.php?m='+m+&y='+y+'&ran='+ran_no);After I get a response from the AJAX function, i need to update the calender. The calendar is wrapped around with the div tag called 'quickCalender'. I just need to rewrite the contents of the tag on the fly.

document.getElementById("quickCalender").innerHTML = http.responseText; Conclusion and Future Improvements
In this tutorial, we went through the concept of how to create a web calendar using AJAX and PHP. I left the details in the code to prevent the tutorial becoming too long and indigestable. If you look at the code hard enough, you will notice that I packed alot of codes in one file. As a good programming practice, I should have broken them down into smaller parts/files. Because the objective of this project is to create a quick "plug and forget" calendar system, I did that on purpose.

Also, the object oriented approach I used in the code may not be flexible enough if I want to have different layouts for the calendar. The problem can be easily fixed by using inheritance, ie creating a superclass for the QuickCalendar class. We can then have BrownCalendar, SpecialCalendar ...etc.

Hope you enjoy reading this tutorial as much as I wrote it. The full demo can be seen here and source code is here.

Bernard Peh specialises mainly in SEO and PHP programming. He is also the web developer behind Sitecritic.net Web Design. During his free time, he maintains his melbourne web developer blog.
52 comments on this article. Log in to add your comment | Rate this article:

dcal
Submitted by Douglas Clifton on November 14, 2006 - 21:59.

dcal is a PHP calendar I wrote a few years ago. It will display any year or month from 1970 to 2037 and includes holidays, moon phases, the vernal and autumnal equinox, summer and winter solstices, and some other goodies including a slick JavaScript slider interface for jumping directly to a particular month.
login or register to post comments

Hi Bernard, just so you know
Submitted by dmackinn on November 23, 2006 - 16:19.

Hi Bernard, just so you know the source code and the demo links appear to be broken.
login or register to post comments

Great Article
Submitted by cianuro on November 23, 2006 - 20:24.

Great article Bernard. Although there are many perfectly decent calendar apps already out there made by the big guys, it's nice to follow along the creative process. And it seems the URLs are fied now. Regards Dave
login or register to post comments

There's a problem...
Submitted by daiBach on November 30, 2006 - 17:22.

If you are in December and try to navigate back one month then the year gets incremented as well e.g. moving from Dec 06 to Nov 06 actually brings up Nov 07.
login or register to post comments

changes to the script
Submitted by bpeh on December 2, 2006 - 10:02.

thanks all esp daibach for pointing out the error, I've updated the script: php calendar. I emailed the admin with the changes but it wasnt updated. I did a typo with the link in the first paragraph. The link in last paragraph is fine.
there is also an extended version http://web-developer.sitecritic.net/quick_calendar_demo2.php

the source code is http://web-developer.sitecritic.net/quick_calendar.txt

login or register to post comments

Good script. On your
Submitted by bichenoubi on December 4, 2006 - 06:39.

Good script. On your website, on the simple version, there is still errors if you go back and froth a couple of time between dec 2006 and jan 2007. On the extended version, there is no problem. One advice, even thought this is not the point of the article, for accessibility reasons, you should remove the hre f = "java script:; "(errors only for evolt filter) and replace it with the ajax function, eg: hre f="quick_calendar2.php ? m=11&y=2006"(errors only for evolt filter). You will only have to change a bit the php code to find a new way to send only calendar html code for your ajax functions (i.e. your already there random var?).
login or register to post comments

thx for your script.but your
Submitted by jabky on December 4, 2006 - 08:30.

thx for your script.but your source code up there are not like your extended version. and i try to change like gichenoubi said but it still doesn't work for me. i'm the newbie in ajax
login or register to post comments

Some quickCalendar fixes
Submitted by kirk837 on January 5, 2007 - 07:13.

1. To fix the Jan->Dec problem in the "simple" version: In createHeader(), in elseif ($prevMonth==0){, need to add the forgotten statement $prevMonth=12;

2. The "jumpiness" of dates in calendar body when changing month to month is fixed by css changes:

td {height: 22px; border: 0px;
.today and .link {padding: 1px;
Jumpiness of the entire page is improved by setting weeksInMonth (in CreateQCalendarArray) to 6 (this sometimes leaves a blank bottom week, but it keeps the rest of the page from jumping).
3. I found it necessary to initialize the links[] array to '' prior to filling it in the "while mysql_fetch_array" loop, to avoid empty array errors at the foreach loop in createBody. Don't know why this isn't a problem for the posted demo.

4. Each next/previous month or year causes the entire css to again be written to the page, making the page increasingly large (to see this, save the page and examine in an editor). Here's a fix: Place css in quick_calendar.css; in function createHeader, read in the css within the "class=calendar" table with fread:

$this->html = "
";
$this->html .= fread(fopen($this->cssfile,'r'),filesize($this->cssfile));
$this->html .= " ... "
(where $cssfile='quick_calendar.css', passed to function QCalendar through added parameter cssfile; in QCalendar, $this->cssfile=cssfile).

5. Along with quick_calendar.txt (the "simple" version), it would be nice if Bernard would post quick_calendar2.txt for the "advanced" version. (I have done my own; I'd like to compare notes.) It would also be nice if Bernard would fix the erroneous "we-developer" links on this page.

login or register to post comments

Some quickCalendar fixes - continued
Submitted by kirk837 on January 5, 2007 - 23:23.

6. Every change of month or year creates another
around the calendar. (To actually see this, use FireFox with Firebug and set Outline Blocks.) To avoid this, remove from $this->html = " in createHeader(), and remove at the very end of createFooter(). To place a calendar on a (.php) page, surround the php with a div:




(This is only slightly less "quick" than the php-only statement.)
7. Also, throughout css, "a:link" should be "a.link" (Now I can get rid of those underlines!)

login or register to post comments

Starting with Mondays
Submitted by a1phanumeric on January 12, 2007 - 15:05.

You can easily update this script to start with Mondays (therefore having Sat and Sun together at the end of the table) by changing:







to...







and adding:
// Start with Monday
if($firstDay == 1){
$counter = 1;
}
?>
before the
for($j=0;$j<$this->weeksInMonth;$j++) {
for($i=0;$i<7;$i++) {
$counter++;
...
?>
loop Simple! Ed.
login or register to post

Flat Button in VB

0 comments

How do we implement a Flat Button?

Well, let's describe step by step how we can create a usercontrol that behaves as a flat button. From VB open a new ActiveX project and save it as MyFlatButton.ctl.


Open a new ActiveX control project

Events "MouseDown" and "MouseUp" will help us to draw the edge sunken or raised when the user mouse-clicks the usercontrol. When the MouseDown event is fired we will draw a sunken edge. When the MouseUp event is fired we will draw a raised edge. This is when we should take the first decision: do we draw the edge directly from the mouse event? or should we call the Paint event instead? Is there a difference? The answer is yes, there's a big difference! Always try to have as little code as you can in procedures that are message consumers. The more code we add to the MouseDown message the slower our application will process mouse events. It is a better approach to put all drawing code in the Paint event and let the MouseDown and MouseUp events flow faster.

Let's see this with more detail. If we were drawing the button edge directly from the MouseDown event we would code something like:

Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
Call pDrawEdgeSunken
End If
End Sub

Where pDrawEdgeRaised would be a procedure that draws the usercontrol edge with a sunken style. On the other hand, if we just call the Paint event our code would be:

Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
m_MouseDown = True
Call UserControl_Paint
End If
End Sub

Where m_MouseDown is a boolean flag we've created to let the rest of the code know when the mouse is down. The UserControl_Paint event will have all necessary drawing code. What's the difference? well, the first version is calling procedure pDrawEdgeSunken and therefore the MouseDown event will not finish until pDrawEdgeSunken does. On the other hand, the second version calls UserControl_Paint and doesn't wait for this procedure to finish (Paint is an event procedure). It just posts a message WM_PAINT to itself and quits. This second version is more efficient and would not convert the MouseDown procedure in a potential "bottle-neck".

Using this second approach we are now going to create the first version of our flat button. You can paste the following code into your usercontrol:

Option Explicit

Public Event Click()

Private m_MouseDown As Boolean
Private m_MouseOver As Boolean
Private m_ClientRec As RECT

'API declarations:
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type

Private Const BDR_RAISEDINNER = &H4
Private Const BDR_SUNKENOUTER = &H2
Private Const BF_RECT = &HF

Private Declare Function DrawEdge Lib "user32" ( _
ByVal hdc As Long, _
qrc As RECT, _
ByVal edge As Long, _
ByVal grfFlags As Long) As Long

Private Declare Function GetWindowRect Lib "user32" ( _
ByVal hwnd As Long, _
lpRect As RECT) As Long

Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
m_MouseDown = True
Call UserControl_Paint
End If
End Sub

Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Not m_MouseOver Then
m_MouseOver = True
UserControl_Paint
End If
End Sub

Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbLeftButton Then
m_MouseDown = False
UserControl_Paint
RaiseEvent Click
End If
End Sub

Private Sub UserControl_Resize()

GetWindowRect UserControl.hwnd, m_ClientRec

'Transform from screen to client coordinates
With m_ClientRec
.Right = .Right - .Left
.Bottom = .Bottom - .Top
.Left = 0
.Top = 0
End With

UserControl_Paint

End Sub

Private Sub UserControl_Paint()

Dim lEdge As Long

If Not UserControl.Ambient.UserMode Then
m_MouseOver = True
End If

UserControl.Cls

lEdge = 0

If m_MouseOver Then
If m_MouseDown Then
lEdge = BDR_SUNKENOUTER
Else
lEdge = BDR_RAISEDINNER
End If
DrawEdge UserControl.hdc, m_ClientRec, lEdge, BF_RECT
End If

'
'Add your code here to draw the picture and caption...
'

End Sub

Let's review this code before we test it. As you can see, MouseUp and MouseDown only check whether the right mouse button has been pressed and they both set the flag m_MouseDown to either True or False depending on the event. MouseMove only takes care of flag m_MouseOver and all three events call Paint to force a re-painting of the object. Resize is responsible for getting the size of the client area - using the API GetWindowRect - and also forces a re-paint. Finally the Paint event clears the usercontrol and draws the appropriate edge based on the values of m_MouseOver and m_MouseDown. Easy, isn't it?

We can now test the usercontrol. If we do so, we can see that the control first shows flat. Then, as soon as the mouse enters its client area, the control shows raised. We can now start clicking the button and the edge turns sunken when we down-click and it turns back to raised when the mouse is released.

Good, it seems to work! There are only two remaining problems:
1. The edge doesn't show flat when the mouse leaves the button.
2. The push-pop effect seems to be wrong if we mouse-click quickly.

In the next section I'm going to discuss how these two problems can be solved.


Trapping messages to solve flat button "issues"

The first question that arises is: how can we know when the mouse leaves the button? In March 1997 Microsoft published the source code of a flat button implemented with Visual Basic. The project was called Visual Basic Soft Button Sample. Since then, many soft buttons have appeared on the net and I would say all share the same technique: they use APIS SetCapture and ReleaseCapture in order to detect when the mouse leaves the button.

How does this solution work and what sort of problems does it have? Basically the solution presented by Microsoft is based on capturing the mouse when the mouse gets into the button client area. Using SetCapture, it holds the mouse until detects that it has moved outside of the button to then release it. Sounds logical? I think it is a very good approach. So what's the problem then? Well, I would say the implementation of this solution has mainly two problems:

1. SetCapture only allows one window to be holding the mouse.
2. MouseMove event will have to release and capture the mouse every time this event is fired.

We'll better understand this if we have a code sample. If we were using SetCapture and ReleaseCapture we would have to modify the MouseMove event with the following code:

Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

ReleaseCapture

If X >= 0 And _
Y >= 0 And _
X <= UserControl.ScaleWidth And _
Y <= UserControl.ScaleHeight Then

SetCapture UserControl.hwnd

m_MouseOver = True

UserControl_Paint
End If

End Sub

After calling the SetCapture API, all mouse messages, no matter where the mouse is, are posted to our window. It would be more logical if we just call SetCapture the first time we detect the mouse has entered the button area and call ReleaseCapture only once too, as soon as we detect that the mouse has left the button. We can't! Why? because only one window can hold the mouse by calling SetCapture. If an external application/code calls SetCapture while the mouse is over the button, we would lose its messages and we would never know when to call ReleaseCapture and draw the edge flat again. Therefore, the solution is based on releasing and capturing the mouse every time the user moves the mouse over the button. Not very efficient and still containing potential problems...

There's a better way to detect when the mouse leaves the button: using API TrackMouseEvent and WM_MOUSELEAVE message. TrackMouseEvent can be used to ask the system to track the mouse for us and post a WM_MOUSELEAVE message as soon as the mouse leaves our window area. It is a cleaner and more efficient approach but, how can we detect the WM_MOUSELEAVE message? We're going to use the SmartSubclass library to trap all messages posted to the usercontrol. You will have to add a reference to SmartSubclass.dll, as described in the article, and you'll be able to create a SmartSubclass variable. You'll find another example on how to use this class in Trap the Mouse!

Add the following code to the usercontrol:

Private WithEvents m_Sniff As SmartSubClass

Private Type TrackMouseEvent
cbSize As Long
dwFlags As Long
hwnd As Long
dwHoverTime As Long
End Type

Private Const WM_MOUSELEAVE = &H2A3
Private Const TME_LEAVE = &H2

Private Declare Function TrackMouseEvent Lib "comctl32.dll" Alias "_TrackMouseEvent" ( _
ByRef lpEventTrack As TrackMouseEvent) As Long

Private Sub UserControl_Initialize()
Set m_Sniff = New SmartSubClass
m_Sniff.SubClassHwnd UserControl.hwnd, True
End Sub

Private Sub UserControl_Terminate()
m_Sniff.SubClassHwnd UserControl.hwnd, False
End Sub

Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

Dim tTrackMouseEvent As TrackMouseEvent

If Not m_MouseOver Then

With tTrackMouseEvent
.cbSize = Len(tTrackMouseEvent)
.dwFlags = TME_LEAVE
.hwnd = UserControl.hwnd
End With

TrackMouseEvent tTrackMouseEvent

m_MouseOver = True
UserControl_Paint
End If

End Sub

Private Sub m_Sniff_NewMessage( _
ByVal hwnd As Long, _
uMsg As Long, _
wParam As Long, _
lParam As Long, _
Cancel As Boolean)

Select Case uMsg
Case WM_MOUSELEAVE
m_MouseOver = False
UserControl_Paint
End Select

End Sub

If you add the code to your usercontrol and you test it again, you will see that the button turns flat again as soon as the mouse leaves its client area. We've had to call TrackMouseEvent just once and when our 'sniffer' gets the WM_MOUSELEAVE message, it sets the m_MouseOver flag to False and forces a control re-painting.

I honestly prefer TrackMouseEvent to SetCapture, basically because we don't get conflicts with other windows, we rely on the system to track the mouse position and we don't need to over-charge the MouseMove event.

Now let's go back to our usercontrol. If you remember there's still a remaining problem: The "push-pop" effect seems to be wrong if we click the button fast. I describe this problem with more details in Trap the mouse!. I will only tell you that this problem is due to receiving DblClick rather than MouseDown. To fix the problem we need to get rid of the DblClick event and get MouseDown instead.

Add the following code to the usercontrol:

Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_LBUTTONDOWN = &H201

Private Sub m_Sniff_NewMessage( _
ByVal hwnd As Long, _
uMsg As Long, _
wParam As Long, _
lParam As Long, _
Cancel As Boolean)

Select Case uMsg
Case WM_MOUSELEAVE
m_MouseOver = False
UserControl_Paint

Case WM_LBUTTONDBLCLK
uMsg = WM_LBUTTONDOWN
End Select

End Sub

You can now click the flat button as fast as you want. You will always get the right "push-pop" effect. We've used our "sniffer" to get the WM_LBUTTONDBLCLK message and replaced it with a WM_LBUTTONDOWN. It couldn't be easier!

The flat button has now a very solid structure. We can only add enhancements to it. In the next section I will describe how we can make the flat button post click events when we hold the mouse down-clicked.


How can we make the flat button simulate repeated clicks?

When you hold the mouse down-clicked on the button you will get only one MouseDown message. You need to release the mouse and down-click again in order to receive the next MouseDown message. This could be a problem if we would like to use our flat button on the implementation of let's say a viewport control. The user may want to scroll the viewport area by holding the mouse down on the flat button. Makes sense. So how can we do that if Windows sends only one message? We will need to do it ourselves!

We need a timer. Actually we need two timers! We could use the Timer control that comes with Visual Basic, but we're going to use API calls. Why? because it is a good way of learning Windows core functions!

Once the user down-clicks the button we need a timer to control when to start posting Click events and we need another timer to control the frequency of this posting. Windows provides two very good timer functions, SetTimer and KillTimer, and a timer message WM_TIMER.

In order to implement the repeated click you should add the following code:

Private Const m_TimerDelay = 1
Private Const m_TimerLapse = 2
Private Const m_RepeatDelay = 250
Private Const m_RepeatLapse = 125

Private Const WM_TIMER = &H113

Private Declare Function SetTimer Lib "user32" ( _
ByVal hwnd As Long, _
ByVal nIDEvent As Long, _
ByVal uElapse As Long, _
ByVal lpTimerFunc As Long) As Long

Private Declare Function KillTimer Lib "user32" ( _
ByVal hwnd As Long, _
ByVal nIDEvent As Long) As Long

Private Sub m_Sniff_NewMessage(ByVal hwnd As Long, uMsg As Long, wParam As Long, lParam As Long, Cancel As Boolean)

Select Case uMsg

Case WM_MOUSELEAVE
m_MouseOver = False
UserControl_Paint

Case WM_LBUTTONDBLCLK
uMsg = WM_LBUTTONDOWN

Case WM_TIMER
Select Case wParam
Case m_TimerDelay
KillTimer UserControl.hwnd, m_TimerDelay

If m_MouseDown Then
SetTimer UserControl.hwnd, m_TimerLapse, m_RepeatLapse, 0
End If
Case m_TimerLapse
If m_MouseDown Then
RaiseEvent Click
End If
End Select

End Select

End Sub

Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

If Button = vbLeftButton Then
m_MouseDown = True
UserControl_Paint

If m_RepeatDelay > 0 Then
SetTimer UserControl.hwnd, m_TimerDelay, m_RepeatDelay, 0
End If
End If

End Sub

Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

If Button = vbLeftButton Then
m_MouseDown = False

KillTimer UserControl.hwnd, m_TimerDelay
KillTimer UserControl.hwnd, m_TimerLapse

UserControl_Paint

RaiseEvent Click
End If

End Sub

Now the flat button starts waiting m_RepeatDelay milliseconds once the user down-clicks. After this time it will start posting Click events every m_RepeatLapse milliseconds.

Let's review the solution: we've modified MouseDown to program the first timer (m_TimerDelay). When the "sniffer" gets a WM_TIMER message it checks for its timer ID. If it is the first timer, the delay lapse has expired and it programs the second timer (m_TimerLapse). It also kills the first timer. Otherwise, if the WM_TIMER message belongs to the second timer, it posts a Click event (the user is holding the mouse down). Finally, the MouseUp event only has to kill both timers.

Well, that's it! Now you have a flat button working that only needs extra properties like Caption, Font, Picture, ForeColor... I will leave it up to you to implement the enhancements.

Format numbers in VB

0 comments

This is a FAQ for VB Beginners that deals with the problem of numbers that have more digits after the decimal point than you want to use or display.

Topics covered are:


a. Use the "Fixed" Format and Format$
b. Use the "Standard" Format and Format$
c. Use Format$ and your own settings
d. Use FormatNumber
e. Use the Round Function

Sometimes calculations in your project will result in numbers with a lot of decimal places - users input numbers with multiple digits after the decimal point or the calculation throws up a long list of decimal digits that you don't want or need you want to restrict these numbers to, say, 2 decimal places, here are a few ways you can do this:


a. Use the "Fixed" Format and Format$

This example takes a user input from a textbox and performs a calculation on it. The result, correct to 2 decimal places, is shown in a label. If your project needs are different, you can adapt the code as necessary.

Add a textbox (Text1), a commandbutton(Command1) and a Label (Label1) to a form, then copy and paste this code:-

Code:Option Explicit
Dim lngDemoNo As Double

Private Sub Form_Load()
Text1 = 123764.76464646 ' You can change this at run time if you want
lngDemoNo = Text1 * 3.142 ' Create some demo data
End Sub

Private Sub Command1_Click()
' For demo purposes, display the result
Label1 = "Your Input Number * Pi = " & Format$(lngDemoNo, "Fixed")
End Sub


b. Use the "Standard" Format and Format$

The only difference to the above is that this will insert commas if the value of the number is greater than 999 (ie. 1,000.00 instead of 1000.00).

You can recyle the code example above and just change the code line in the Command1_Click event to:

Code:Label1 = "Your Input Number * Pi = " & Format$(lngDemoNo, "Standard")


c. Use Format$ and your own settings

You can force the result to appear in whatever format you like by using Format$ and creating the settings yourself. Re-use the first example and change the code line in the Command1_Click Event to:

Code:Label1 = "Your Input Number * Pi = " & Format$(lngDemoNo, "0.000")

Notice that I have put three zeros after the decimal point, so the resulting label display is shown correct to three decimal places. You can, of course, change this to whatever suits your purposes.


d. Use FormatNumber

You can also use the FormatNumber Function provided by VB. This function will accept several arguments that can be used to present a number in different ways, but we will just use one in this FAQ.

Once again, replace the Command Button code line with this one:

Code:Label1 = "Your Input Number * Pi = " & FormatNumber(lngDemoNo, 3)

The "3" digit is the number of decimal places to use. Again, you can change this to whatever you want.


e. Use the Round Function

Similar to most of the above options, the Round Function will return a value set to the number of places you decide and will round the last digit up or down.

The replacement code line in this case is:

Code:Label1 = "Your Input Number * Pi = " & Round(lngDemoNo, 2)

So there you have various ways of achieving the same thing – dictating how many digits you want to have after the decimal point.

Sun Mon Tue Wed Thur Fri Sat Mon Tue Wed Thur Fri Sat Sun