We help IT Professionals succeed at work.

Easy Calendar Question

aj85
aj85 asked
on
Medium Priority
332 Views
Last Modified: 2010-04-04

I am using the MonthView component in D4, for an application I am developing.  I have a database with future dates that I need to have shown in bold on the calendar, i.e. 10/01/02.  My question is first can this be done?  And secondly, in short if it can, how?  Please provide example.

Thanks in advance!
aj85
Comment
Watch Question

Mohammed NasmanSoftware Developer
CERTIFIED EXPERT

Commented:
Look at
http://homepages.borland.com/torry/calendar.htm
there's a free calendar components can connec to database

Commented:
Hi,
You need to create an array (prefferably dynamic) that holds the daynumbers to be bold. Than you need to pass this array to the bolddays function at the Get MonthInfo event.

procedure TVForm1.MonthCalendar1GetMonthInfo(Sender: TObject;
  Month: Cardinal; var MonthBoldInfo: Cardinal);
var
  b: array of LongWord;
begin
  setlength(b, 2); // two days will be bold
  b[0] := 1; // 1st day of the month will be bold
  b[1]:=20; // 20th day of the month will be bold
  MonthCalendar1.BoldDays(b, MonthBoldInfo);
end;

It may be a little hard task to generate the array from database but it is possible. You may need an another function that generates the array and returns it inside of the event. I prefer to return a string value that represents the bold days. For example :

DayStr := chr(12) + chr(14) + chr(20) //12,14 and 20 will bold;

the event will look like


procedure TVForm1.MonthCalendar1GetMonthInfo(Sender: TObject;
  Month: Cardinal; var MonthBoldInfo: Cardinal);
var
  b: array of LongWord;
  DayStr:ShortString;
  i:integer;
begin
  DayStr:=GetBoldDayStr(Month);
  setlength(b, Length(DayStr));
  for i := 0 to length(DayStr) - 1 do
    b[i] := longword(DayStr[i + 1]);
  MonthCalendar1.BoldDays(b, MonthBoldInfo);
end;

GetBoldStr procedure will filter your table with the month parameter passed and contruct the string that has to be passed. While constructing the string you need to handle duplicate days. You may create a array that has 31 Boolean items to represent the bold days. Then make the items true that corresponds to the day that has to be bold. For Example:
------------------------------------------------
var
 myArr: array[0..30] of boolean;

for i := 0 to Table1.RecordCount - 1 do
begin
  Myarr[extractday(Table1MyDate.AsDateTime)] := true;
  Table1.next;
end;
------------------------------------------------

finally construct the string

for i := 0 to 30 do
  if Myarr[i] then
   begin
     result := result + chr(i);
   end;

This is my point of view. You may find better solutions in your project.

Good Luck

Author

Commented:

mnasman,

Yes, I know there are several compononets that are DB-Aware, however none of the ones that I found, allow you to have a single calendar component and have the entire year viewable.  They only allow single month views.  So that is why I am using the MonthView component because it allows this function.  Now, Emeeren, I am testing your suggestion and will get back with you soon.  If you or anyone else has any other ways to approach this please let me know.

Thanks,
aj85

Mohammed NasmanSoftware Developer
CERTIFIED EXPERT

Commented:
Hello

  you can add monthcalendar to your form, and get/set the date using it, like the following

// when u click on the month view to choose date, add this date to the field in the table

procedure TForm1.MonthCalendar1Click(Sender: TObject);
begin
  DBEdit3.Text :=DateToStr(MonthCalendar1.Date);
end;

// and when u move to next or previews record, you can get the date from the talbe and put it in the month calender
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
  MonthCalendar1.Date := StrToDate(DBEdit3.text);
end;

if you have problem with that, give me your email, and i will send you sample,

Author

Commented:

Mnasman,

You have the idea, but what I am saying is that I want all dates that are in the database to be bold on the calendar.  Not just when you select the date in the table. I have written code to connect to the table with the month view, and have the dates appear on the component when you select the date.  I am just unclear on how to make all the dates bold that are in the table.  Which is the example, I need.  Thanks for the response though.  

aj85

Author

Commented:

emeeren,

I have tried your suggestion, but it bolds the same dates on every month.  And not the dates in the table.  I may be doing something wrong.  Can you clarify the "GetBoldDayStr" function.  I pass in the day, but as stated before that day in bold for all months.  How do I handle the month?  So that the given month and day are reflected?

Thanks,
aj85

Commented:
hi;

procedure TVForm1.MonthCalendar1GetMonthInfo(Sender: TObject;
  Month: Cardinal; var MonthBoldInfo: Cardinal);
begin

end;

as you can see the event executes while the component paints the month panel and it passes the Month varible. if you have 12 visible months, this event executes 12 times and the event passes the month which it is going to paint. So we need passing the month variable to the GetBoldDayStr function. Than the function will able to filter the table with the given month parameter. I'll try to give an simplified example.

function GetBoldDayStr(Month: word): shortstring;
var AStartDate, AEndDate: TDate;
  i: integer;
  myArr: array[0..30] of boolean;
begin
  result := '';
  AStartDate := EncodeDate(2001, Month, 1); // 2001 will be the visible year.
  AEndDate := AstartDate + 1 Month;  
  Table1.filter := ... //(datefield > AStartdate) and(Datefield < AEndDate)
  table1.filtered := true;
  table1.open;
  for i := 0 to 30 do myarr[i] := false;
  for i := 0 to table1.RecordCount - 1 do
  begin
    Myarr[extractday(Table1STARTDATE.AsDateTime)] := true;
    table1.next;
  end;
  for i := 0 to 30 do
    if Myarr[i] then result := result + chr(i);
  table1.filtered := false;
  end;
end;

Don't forget. This function will be called (VisibleMonthPanelCount) times. Have a nice day.

Commented:
I forget to clarify. Try this. You'll see 10th day is bold at November and 20th day is bold at December.

procedure TVForm1.MonthCalendar1GetMonthInfo(Sender: TObject;
  Month: Cardinal; var MonthBoldInfo: Cardinal);
begin
  if month = 11 then
    MonthCalendar1.BoldDays([10],MonthBoldInfo)
  else if month = 12 then
    MonthCalendar1.BoldDays([20],MonthBoldInfo);
end;

Author

Commented:

emeeren,

Thanks for the additional information, I will try this and get back to you.

aj85

Author

Commented:

Emreeren,

Your last example works fine, however when trying the function example, I get the following errors.


[Error] Unit1.pas(60): Undeclared identifier: 'extractday'

Also, when I try to pass the parameter to the "GetBoldDayStr" function, i.e. DayStr:=GetBoldDayStr, I get an incompatible type error.

What should I do next to correct this?

Thanks,
aj85

Commented:
hi;
extractday returns the day part of the given date and this function is located at dateUtil(s).pas which is inside of the rx pack. But we need the day part of the given date so you may use decodeDate function or any other function you have.

I really don't know why you get an error. Please check the type of DayStr variable and the result type of the GetBoldDayStr function. Both of them must be shortstring. Or you may change the GetBoldDayStr to a procedure and pass the DayStr variable as a var parameter. For example

function GetBoldDayStr(Month: word): shortstring;
to
procedure GetBoldDayStr(Month: word; var DayStr:ShortString);

and use it like

procedure TVForm1.MonthCalendar1GetMonthInfo(Sender: TObject;
 Month: Cardinal; var MonthBoldInfo: Cardinal);
var
 b: array of LongWord;
 DayStr:ShortString;
 i:integer;
begin
 GetBoldDayStr(Month,DayStr); // change here
 setlength(b, Length(DayStr));
 for i := 0 to length(DayStr) - 1 do
   b[i] := longword(DayStr[i + 1]);
 MonthCalendar1.BoldDays(b, MonthBoldInfo);
end;

and inside of the procedure use DayStr instead of result. This may solve your problem.

Author

Commented:

Thanks for the reply again.  I have tried your other example, and I must be doing something wrong.  Please see the procedure below:

procedure TForm1.GetBoldDayStr(Month: word; var DayStr:ShortString);
var AStartDate, AEndDate: TDate;
 i: integer;
 myArr: array[0..30] of boolean;


begin
 DayStr := '';
 AStartDate := EncodeDate(2001, Month, 1); // 2001 will be the visible year.
 AEndDate := AstartDate +  Month;
 //Table1.filter :=  //(datefield > AStartdate) and(Datefield < AEndDate)
 table1.filtered := true;
 table1.open;
 for i := 0 to 30 do myarr[i] := false;
 for i := 0 to table1.RecordCount - 1 do
 begin
   Myarr[decodeDate(Table1Event_Date.AsDateTime, 'dd', 'mm', 'yy')] := true;
   table1.next;
 end;
 for i := 0 to 30 do
   if Myarr[i] then DayStr := DayStr + chr(i);
 table1.filtered := false;
 end;

This now gives me a stack overflow error. I don't know what step to take next.

Thanks,
aj85

Mohammed NasmanSoftware Developer
CERTIFIED EXPERT

Commented:
take a look at
http://www.tmssoftware.com/plannercal.htm
you can set the color of any day, and it's free
Commented:
Ok Lets have a look...

procedure TForm1.GetBoldDayStr(Month: word; var DayStr: ShortString);
var AStartDate, AEndDate: TDate;
  i: integer;
  myArr: array[0..30] of boolean;
  d, m, y: Word;
begin
  DayStr := '';
  AStartDate := EncodeDate(2001, Month, 1);
  //AEndDate := AstartDate +  Month;
  //We have to increase the date 1 month to find the end date.
  AEndDate := EncodeDate(2001, month + 1, 1);

  //Table1.filter :=  //(datefield > AStartdate) and(Datefield < AEndDate)
  // We have to filter our table but I described the filter because I dont Know your field names.
  Table1.Filter := 'EVENT_DATE > ' + datetostr(AstartDate) + ' AND EVENT_DATE < ' + datetostr(AEnddate) + '';
  table1.filtered := true;
  table1.open;
  for i := 0 to 30 do myarr[i] := false;
  for i := 0 to table1.RecordCount - 1 do
  begin
  // we have to use decodedate like this... We need the day
  // this fixes the overflow
    DecodeDate(Table1Event_Date.AsDateTime,y,m,d);
    Myarr[d] := true;
    table1.next;
  end;
  for i := 0 to 30 do
    if Myarr[i] then DayStr := DayStr + chr(i);
  table1.filtered := false;
end;

The filter is too important because it returns the days of the selected month. Thats all...

Commented:
I have to say the month increasing code is a little bit buggy while increasing december month. (5.13.2001 is an invalid date) But I have to answer quickly. You need a function that increases the date 1 months (1.3.2001 to 1.4.2001 or 5.12.2001 to 5.1.2002). I'm using the IncMonth function from RxPAck, dateUtils.pas. It has many datetime oriented useful functions. If you have RxPack Installed than simply add dateUtils.pas to your uses section in your unit and use IncMonth function. I'm sorry.

Author

Commented:

emreeren,

Thanks for the information trying it now.

aj85

Author

Commented:

emreeren,

Ok, I have tried the new example.  However I am still getting an error, this time on the line:

Table1.Filter := 'EVENT_DATE > ' + datetostr(AstartDate) + ' AND EVENT_DATE < ' + datetostr(AEnddate)
+ '';

When I change this to my table, which is 'EventTime_Date.Value', I get an "Arithmetic" error or it errors and says the datatypes I have are not compatible.  What do you suggest next?  I could be the way I am passing the table value, is this the case?

Thanks,
aj85

Commented:
hi;
I don't know your database structure and your field types and how you store the date values in your database. I gave you a general example. You only change the field names with your field names. I really can't understand why your filter is not working.

The Template is:
(MYDATEFIELD > STARTDATEVALUE AND MYDATEFIELD < ENDDATEVALUE)

If you are developing a calendar or an PIM application than you can try

www.jazminecomponents.com 

You'll find some useful components which may solve your problems.
Thanks.

Author

Commented:
Thanks for your help, here are your points.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.