Archive for September 2006

XMLDataSet component

September 9, 2006

There’s this nifty XML import-export component we use a lot in our projects. Unfortuantely the development of this has ended and there are quite a lot of bugs in the code. Here are some of them that I’ve found in XMLDataSet.pas:

Someone forgot to clear the Result value:


{$IFDEF HUMAN_READABLE}
  Function MimeEncodeString(
    Const AInputString: AnsiString): AnsiString; //rjMime
  ..
  Begin
    Result := ''; {!! indrek 20060901}
    If Pointer(AInputString) <> Nil Then Begin
    ..

That’s a bad one: every time You had some nonallowed character (like space) in the datapacket name, an EXMLInvalidpacket exception was created but not raised causing 1) a memory leak 2) illegal characters in datapacket name. After making this modification You have to check all Your datapacket names because they might have nonallowed characters.


Procedure TXMLDataset.SetPacketName(AName: AnsiString);
    ..
    While tI1 <= Len1 Do Begin
      // If Not (AName[tI1] In AllAllowed) Then
      // EXMLInvalidPacket.Create('Invalid DataPacket name');
      {!! indrek 20051010}
      If Not (AName[tI1] In AllAllowed) Then
        Raise EXMLInvalidPacket.Create('Invalid DataPacket name');
      ..

This bug caused a lot of headache for us at first. The apostrophe (‘) character has to be converted to XML entity (&apos;) when exporting and reconverted back during importing. The converting was OK, but it left the trailing ‘s’ in the string (i.e. “Harry’s” become “Harry’ss” after exporting-importing).


Procedure TXMLDataset.ConvertDatasetToXML;

  'P': Case Chars1[3] Of
      'O': Case Chars1[4] Of
          'S': Case AIgnoreSemicolon Of
              True: Begin
                  AOutput[OutputIndex1] := APOSTROPHE_CHAR;
                  // Inc(InputIndex1, 4);
                  Inc(InputIndex1, 5); { indrek 20050216 }
                End;
              False:
                Case Chars1[5] Of
                  SEMICOLON_CHAR: Begin
                      AOutput[OutputIndex1] := APOSTROPHE_CHAR;
                      // Inc(InputIndex1, 5);
                      Inc(InputIndex1, 6); { indrek 20050216 }
                    End;

TDateTimePicker and NULL dates

September 6, 2006

Using TDateTimePicker control with database can be a PITA. The main problem is that <NULL> dates are shown as 30.12.1899 and after posting changes to DB, the same date will appear in the DB as well. Here’s a hack I did using an empty TComboBox.

Hide the TDateTimePicker with <NULL> dates:

procedure TForm1.DisplayData;
begin
  ..
  if DataModule1.IBDataset1.FieldByName('DateColum').IsNull then
    ComboBox1.BringToFront()
  else begin
    ComboBox1.SendToBack();
    DateTimePicker1.Date :=
      DataModule1.IBDataset1.FieldByName('DateColumn').AsDateTime;
  end;
  ..
end;

In the OnEnter event handler hide the hack TComboBox and make the TDateTimePicker as the active control:

procedure TForm1.ComboBox1Enter(Sender: TObject);
  Combobox1.SendToBack();
  ActiveControl := DateTimePicker1;
end;

Let the OnEnter event handler to handle OnChange and OnClick events as well.

Now that we have TDateTimePicker visible, user can change a date and we must save it to database using the OnChange event handler:

procedure TForm1.DateTimePicker1Change(Sender: TObject);
begin
  DataModule1.IBDataSet1.FieldByName('DateColumn').AsDateTime :=
    StrToDate(DateToStr(DateTimePicker1.Date));
end;

And of course we want to clear the date in DB as well and we will do this in TDateTimePicker’s OnKeyDown event handler by cathring the Delete-key:

procedure TForm1.DateTimePicker1KeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if Key = vk_Delete then begin
    DataModule1.IBDataSet1.FieldByName('DateColum').Clear();
    ComboBox1.BringToFront();
    // move control away from here
    ActiveControl := NextControlInTheForm;
  end;
end;