0

I am doing a project for school. I have written code to populate two string grids in a calendar style. The second string grid (StringGrid2) shows the month after the first (StringGrid1). This is all done in the following upDateCalendar() procedure that receives an integer iMonthChange to indicate if the user wants to view one month forward (iMonthchange := 1) or one month back (iMonthChange := -1).

Here is the populating procedure:

procedure TStudentDashboard.upDateCalendar(iMonthChange: integer);
var
  iNumDaysinMonth, iWeekCount: integer;
  tNewMonthStart, tTempMonth: TDateTime;
  tYear, tMonth, tDay: Word;
  I, ARow: integer;
begin
  // date inputs
  tFirstStudyDate := dtpicker.Date;

  updateCheckBoxes;

  iStudySessionDuration :=
    strToInt((cmbStudySessionLength.Items[cmbStudySessionLength.ItemIndex][1] +
    cmbStudySessionLength.Items[cmbStudySessionLength.ItemIndex][2]));

  tLastStudyDate := arrCourses[iCourseArrayPointer].lastStudyDate
    (tFirstStudyDate, iStudySessionDuration, arrWeekDaysSelected);

  bDrawPastTestDate := false;

  // populate fixed row
  for I := 0 to StringGrid1.RowCount - 1 do
    StringGrid1.Rows[I].Clear;

  StringGrid1.Cells[0, 0] := 'Mon';
  StringGrid1.Cells[1, 0] := 'Tue';
  StringGrid1.Cells[2, 0] := 'Wed';
  StringGrid1.Cells[3, 0] := 'Thu';
  StringGrid1.Cells[4, 0] := 'Fri';
  StringGrid1.Cells[5, 0] := 'Sat';
  StringGrid1.Cells[6, 0] := 'Sun';

  // increment month
  tNewMonthStart := IncMonth(tCurrentShowMonthStart, iMonthChange);
  iWeekCount := 1;
  tTempMonth := tNewMonthStart;

  for I := 1 to DaysInMonth(tNewMonthStart) do
  begin

    tTempMonth := IncDay(tNewMonthStart, I - 1);

    if (arrCourses[iCourseArrayPointer].getTestDate <> 'click here to book') or
      (bBookingTest = true) then
    begin
      if (arrCourses[iCourseArrayPointer].getTestDate <> 'click here to book') then
      begin
        tTestDate := strToDate(arrCourses[iCourseArrayPointer].getTestDate);
      end
      else
      begin
        tTestDate := dtpckerTestDate.Date;
      end;

      if tTempMonth = tTestDate then
      begin

        StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));

        iCalendarATestDateXVal := DayOfTheWeek(tTempMonth) - 1;
        iCalendarATestDateYVal := iWeekCount;
        bDrawPastTestDate := true;
      end
      else
      begin

        StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));
      end;

      if (tTempMonth < StrToDate(arrCourses[iCourseArrayPointer].getTestDate))
        and (I = DaysInMonth(tNewMonthStart)) then
      begin
        bDrawPastTestDate := true;
        iCalendarATestDateXVal := 6;
        iCalendarATestDateYVal := 6;
      end;
      if (tTempMonth > StrToDate(arrCourses[iCourseArrayPointer].getTestDate))
        and (I = 1) then
      begin

        bDrawPastTestDate := true;
        iCalendarATestDateXVal := 0;
        iCalendarATestDateYVal := 0;
      end;

    end
    else
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      bDrawPastTestDate := false;
    end;

    if (tTempMonth < tLastStudyDate) and (I = DaysInMonth(tNewMonthStart)) then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set at last cell because date to stop colouring is on next stringgrid/month
      iCalendarALastStudyDateXVal := 6;
      iCalendarALastStudyDateYVal := 6;
    end;

    if (tTempMonth > tLastStudyDate) and (I = 1) then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set at last cell because date to stop colouring is on previous month
      iCalendarALastStudyDateXVal := 0;
      iCalendarALastStudyDateYVal := 0;
    end;

    if tTempMonth = tLastStudyDate then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      iCalendarALastStudyDateXVal := DayOfTheWeek(tTempMonth) - 1;
      iCalendarALastStudyDateYVal := iWeekCount;

    end;

    if (tTempMonth < tFirstStudyDate) and (I = DaysInMonth(tNewMonthStart)) then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set at last cell because date to stop colouring is on next stringgrid/month
      iCalendarAFirstStudyDateXVal := 6;
      iCalendarAFirstStudyDateYVal := 6;
    end;

    if (tTempMonth > tFirstStudyDate) and (I = 1) then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set at last cell because date to stop colouring is on previous month
      iCalendarAFirstStudyDateXVal := 0;
      iCalendarAFirstStudyDateYVal := 0;
    end;

    if tTempMonth = tLastStudyDate then
    begin
      StringGrid1.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set date x and y values to be highlighted
      iCalendarAFirstStudyDateXVal := DayOfTheWeek(tTempMonth) - 1;
      iCalendarAFirstStudyDateYVal := iWeekCount;

    end;

    if DayOfTheWeek(tTempMonth) = 7 then
    begin
      Inc(iWeekCount);
    end;

  end;
  // iMonth1LastXVal := DayOfWeek(IncDay(tNewMonthStart,
  // (DaysInMonth(tNewMonthStart) - 1) - 1)) - 1;
  // iMonth1LastYVal := iWeekCount;

  tCurrentShowMonthStart := tNewMonthStart;
  lblMonth1.Caption := LongMonthNames[MonthOf(tCurrentShowMonthStart)];
  lblMonth2.Caption := LongMonthNames
    [MonthOf(IncMonth(tCurrentShowMonthStart))];
  lblYear1.Caption := IntToStr(YearOf(tCurrentShowMonthStart));
  lblYear2.Caption := IntToStr(YearOf(IncMonth(tCurrentShowMonthStart)));

  // populate fixed row
  for I := 0 to StringGrid2.ColCount - 1 do
    StringGrid2.Cols[I].Clear;

  with StringGrid2 do
  begin
    Cells[0, 0] := 'Mon';
    Cells[1, 0] := 'Tue';
    Cells[2, 0] := 'Wed';
    Cells[3, 0] := 'Thu';
    Cells[4, 0] := 'Fri';
    Cells[5, 0] := 'Sat';
    Cells[6, 0] := 'Sun';
  end;
  // increment to month after first stringgrid
  tNewMonthStart := IncMonth(tCurrentShowMonthStart, 1);
  iWeekCount := 1;
  tTempMonth := tNewMonthStart;

  for I := 1 to DaysInMonth(tNewMonthStart) do
  begin

    tTempMonth := IncDay(tNewMonthStart, I - 1);

    if arrCourses[iCourseArrayPointer].getTestDate <> 'click here to book' then
    begin
      if tTempMonth = strToDate(arrCourses[iCourseArrayPointer].getTestDate) then
      begin
        // save x and y values if test date is on this stringgrid
        StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));
        iCalendarBTestDateXVal := DayOfTheWeek(tTempMonth) - 1;
        iCalendarBTestDateYVal := iWeekCount;
        bDrawPastTestDate := true;
      end
      else
      begin
        // otherwise just populate
        StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));
      end;
      if (tTempMonth < StrToDate(arrCourses[iCourseArrayPointer].getTestDate))
        and (I = DaysInMonth(tNewMonthStart)) then
      begin
        // set x and y values to last cell because date to be highlightted is in next month
        StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));
        bDrawPastTestDate := true;
        iCalendarBTestDateXVal := 6;
        iCalendarBTestDateYVal := 6;

      end;
      if (tTempMonth > StrToDate(arrCourses[iCourseArrayPointer].getTestDate))
        and (I = 1) then
      begin
        // set x and y values to first because date to be highlightted is in prior month/stringgrid
        StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
          IntToStr(DayOfTheMonth(tTempMonth));
        bDrawPastTestDate := true;
        iCalendarBTestDateXVal := 0;
        iCalendarBTestDateYVal := 0;

      end;

    end
    else
    begin
      StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // otherwise just populate
      bDrawPastTestDate := false;
    end;
    if (tTempMonth < tLastStudyDate) and (I = DaysInMonth(tNewMonthStart)) then
    begin
      StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set x and y values to last cell because last cell to be higlighted is in next month
      iCalendarBLastStudyDateXVal := 6;
      iCalendarBLastStudyDateYVal := 6;
    end;

    if (tTempMonth > tLastStudyDate) and (I = 1) then
    begin
      StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set x and y values to last cell because last cell to be higlighted is in prior month/stringgrid
      iCalendarBLastStudyDateXVal := 0;
      iCalendarBLastStudyDateYVal := 0;

    end;
    if tTempMonth = tLastStudyDate then
    begin
      StringGrid2.Cells[DayOfTheWeek(tTempMonth) - 1, iWeekCount] :=
        IntToStr(DayOfTheMonth(tTempMonth));
      // set x and y values to current cell
      iCalendarBLastStudyDateXVal := DayOfTheWeek(tTempMonth) - 1;
      iCalendarBLastStudyDateYVal := iWeekCount;

    end;

    if DayOfTheWeek(tTempMonth) = 7 then
    begin
      Inc(iWeekCount);
    end;
  end;

  // iMonth2LastXVal := DayOfWeek(IncDay(tNewMonthStart,
  // (DaysInMonth(tNewMonthStart) - 1) - 1)) - 1;
  // iMonth2LastYVal := iWeekCount;
  StringGrid1.refresh;
  StringGrid2.refresh;
  // ShowMessage(IntToStr(iCal2LastStudyDateXVal));
  // ShowMessage(IntToStr(iCal2LastStudyDateYVal));
end;

The next procedure is the OnDrawCell procedure that runs every time something on a string grid changes. It is basically identical for StringGrid1 as StringGrid2, except that wherever a variable is iCalendarB... it would be iCalendarA... for StringGrid1DrawCell().

Read the comments for further explanation on how the cell colouring works. But basically, it uses the x and y values saved by the upDateCalendar() procedure to determine which cells should be highlighted. See the image below for the result.

My issue is that somewhere the OnDrawCell is constantly being called, or something is causing the StringGrids to continuously update.

image

procedure TStudentDashboard.StringGrid2DrawCell(Sender: TObject;
  ACol, ARow: integer; Rect: TRect; State: TGridDrawState);
begin

  StringGrid2.Selection := TGridRect(Rect);
  // if the cell is not empty or fixed
  if (StringGrid2.Cells[ACol, ARow] <> '') and (ARow > 0) then
  begin
    // if the weekday of the cell corresponds with checkboxes
    if (arrWeekDaysSelected[ACol] = true) then
    begin
      // if the cell has these x and y values make it yellow
      if (ACol = iCalendarBLastStudyDateXVal) and
        (ARow = iCalendarBLastStudyDateYVal) then
      begin

        StringGrid2.Canvas.Brush.Color := $008BECFA; // yellow
        StringGrid2.Canvas.Font.Name := 'Roboto Lt';
        StringGrid2.Canvas.FillRect(Rect);
        StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
          StringGrid2.Cells[ACol, ARow]);
        // StringGrid2.Cells[ACol, ARow] := Copy(StringGrid2.Cells[ACol, ARow],1,StringGrid2.Cells[ACol, ARow].Length-1);
      end
      // only colour the following if the cell has x and y values smaller than i...LastStudyDate..X/Yval
      else if (ARow < iCalendarBLastStudyDateYVal) or
        ((ACol < iCalendarBLastStudyDateXVal) and
        (ARow = iCalendarBLastStudyDateYVal)) then
      begin
        // if the cell has x and y values larger than i...TestDate..X/Yval make red
        if (bDrawPastTestDate = true) and
          (((ARow >= iCalendarBTestDateYVal) and (ACol > iCalendarBTestDateXVal)
          ) or (ARow > iCalendarBTestDateYVal)) then
        begin
          StringGrid2.Canvas.Brush.Color := $00A49FF9; // red
          StringGrid2.Canvas.Font.Name := 'Roboto Lt';
          StringGrid2.Canvas.FillRect(Rect);
          StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
            StringGrid2.Cells[ACol, ARow]);
        end
        // otherwise make it green
        else
        begin
          StringGrid2.Canvas.Brush.Color := $00A4F99F; // green
          StringGrid2.Canvas.Font.Name := 'Roboto Lt';
          StringGrid2.Canvas.FillRect(Rect);
          StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
            StringGrid2.Cells[ACol, ARow]);
        end;

      end
      // otherwise make white
      else
      begin
        StringGrid2.Canvas.Brush.Color := $00F1FFFB;
        StringGrid2.Canvas.Font.Name := 'Roboto Lt';
        StringGrid2.Canvas.FillRect(Rect);
        StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
          StringGrid2.Cells[ACol, ARow]);
      end;

    end
    // if cell is the test date make blue

    else if (ACol = iCalendarBTestDateXVal) and (ARow = iCalendarBTestDateYVal)
    then
    begin

      StringGrid2.Canvas.Brush.Color := $00F9A49F; // blue
      StringGrid2.Canvas.Font.Name := 'Roboto Lt';
      StringGrid2.Canvas.FillRect(Rect);
      StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
        StringGrid2.Cells[ACol, ARow]);
      // StringGrid2.Cells[ACol, ARow] := copy(StringGrid2.Cells[ACol, ARow],1,StringGrid2.Cells[ACol, ARow].Length-1);
      bDrawPastTestDate := true;
    end
    //otherwise make white
    else
    begin
      StringGrid2.Canvas.Brush.Color := $00F1FFFB;
      StringGrid2.Canvas.Font.Name := 'Roboto Lt';
      StringGrid2.Canvas.FillRect(Rect);
      StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
        StringGrid2.Cells[ACol, ARow]);
    end;
  end
  else if gdFixed in State then
  begin
  end
    //otherwise make white
  else if gdSelected in State then
  begin
    StringGrid2.Canvas.Brush.Color := $00F1FFFB;
    StringGrid2.Canvas.Font.Name := 'Roboto Lt';
    StringGrid2.Canvas.FillRect(Rect);
    StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
      StringGrid2.Cells[ACol, ARow]);
  end
    //otherwise make white
  else
  begin
    StringGrid2.Canvas.Brush.Color := $00F1FFFB;
    StringGrid2.Canvas.Font.Name := 'Roboto Lt';
    StringGrid2.Canvas.FillRect(Rect);
    StringGrid2.Canvas.TextOut(Rect.Left + 3, Rect.Top + 3,
      StringGrid2.Cells[ACol, ARow]);
  end;
end;

My goal is to stop the redraw loop and flickering. The flickering I can easily remove using DoubleBuffered := true, however the event still runs continuously keeping other controls from updated (for example, the CheckBoxes work, but you don't see the tick). I assume that's because of the loop?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Nico Smit
  • 21
  • 3
  • 1
    Put BeginUpdate / EndUpdate around any large number of changes to StringGrid content. Usually in a Try / Finally block to make sure updates don't stay off when there is an error. – Brian Aug 28 '22 at 16:01
  • Like this -> try .. stringgrid.beginupdate ..... changes ..... endupdate .... finally? Is that the correct format? (sorry I have not used try finally as much before) Also, what counts as "a large number of changes"? PS Thank you very much for your response! @Brian – Nico Smit Aug 28 '22 at 16:25
  • 2
    no: beginupdate- try- actions - finally endupdate end – MBo Aug 28 '22 at 16:32
  • I put it just like you said, around every change to the string grid. Did not fix the flickering. Is there perhaps a property that needs to be changed? Something to do with windows perhaps? – Nico Smit Aug 28 '22 at 16:47
  • 3
    On the first line in `StringGrid2DrawCell()` you call ` StringGrid2.Selection := tGridRect(Rect);`. Why? This is the reason for the constant redrawing. Just throw it away. – Tom Brunberg Aug 28 '22 at 16:53
  • @Tom Brunberg Done that now, but unfortunately, it is still redrawing. – Nico Smit Aug 28 '22 at 17:15
  • 3
    The reason for constant redrawing of the grid was that you set the `Selection` from within the `OnDrawCell` event, thus triggering a new need to draw and subsequently call `OnDrawCell`. I don't see any other line of code within `StringGrid2DrawCell` that would do something similar. Perhaps you are changing either data or state of `StringGrid2` repeatedly somewhere else in your code. – Tom Brunberg Aug 28 '22 at 19:43

0 Answers0