Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   Kalender (https://www.delphipraxis.net/93735-kalender.html)

Alter Mann 10. Jun 2007 17:12


Kalender
 
Hallo,

habe gerade einen kleinen Kalender fertig gestellt und stelle in hiermit zur Verfügung!

Ich habe bisher keinerlei Anpassungen bzg. anderer Delphiversionen vorgenommen, so wie es ist
läuft es unter D7 (wegen DateUtils), sollte aber mit ein paar Anpassungen auch unter andernen
Versionen laufen.

Der Kalender hat (leider) ein paar Bugs die sich leider nicht so einfach finden lassen und
weil das so ist, ist er auch Open-Source.

Die einzigste Bedingung die ich habe, ist das alle Änderungen hier veröffentlich werden.

Gruss

Alter Mann

[Edit]
Ups...

Jetzt sind alle Dateien bei. [Edit] Plus Beispiel inkl. aller Dateien für Delphi 7.[/Edit]
[/Edit]

PS Kleine Anmerkung sei noch gestattet:

Das Beispiel zeigt ungefähr wo es hingehen soll, vielleicht noch DB-Anbindung.

Was mich da ein wenig verwundert ist die Darstellung zur Desginzeit und zur Laufzeit bzw.
die unterschiedliche Darstellung zwischen Componenten auf Form plaziert und zur Laufzeit
erstellt(siehe Calendar.zip), genauso im Beispiel.

DeddyH 10. Jun 2007 17:17

Re: Kalender
 
Ööhhhmmm.... da fehlt aber die Unit uCalendar im Archiv :angel:

BFI2007 15. Jun 2007 11:17

Re: Kalender
 
echt ey

Alter Mann 19. Jun 2007 17:56

Re: Kalender
 
Hallo


Ich habe mir nartürlich Gedanken gemacht und den ganzen Code neugeschrieben.
Es sind soweit alle 'irrsinnigen' Bugs rausgeflogen und 'andere?' dafür reingekommen.

Die jeniegen die sich den alten Code angetan haben, meine Bewunderung und Entschuldigung.

Für alle Intressierten:

Es fehlt noch die DB-Anbindung und Multiselect.

Aber vielleicht ist ja der/die Eine oder Andere so Hilfsbereit und stellt
die entsprechende Ableitung zur Verfügung.

Viele Grüsse

Alter Mann

Alter Mann 20. Jun 2007 19:09

Re: Kalender
 
Hi,

auch wenn es scheinbar keiner Braucht, hier die DB-Anbindung.
Doch zuerst müssen wir die Kalender-Componente aktuallisieren:

Delphi-Quellcode:
  TCalendarControl = class(TCustomControl)
  private
  ...
    FDates       : Array of TDateTime;
    FMaxRecords  : Integer;
  ...
  protected
  ...
    procedure  DataChange(Sender: TObject); virtual;
    function   DateInArray(aDate : TDateTime) : Boolean;
  ...
  end;

...

constructor TCalendarControl.Create(aOwner : TComponent);
begin
  ...
  SetLength(FDates, 31);
  FMaxRecords  := -1;
  ...
end;

...

procedure  TCalendarControl.DataChange(Sender: TObject);
begin
end;

function   TCalendarControl.DateInArray(aDate : TDateTime) : Boolean;
var
  I : Integer;
begin
  Result := False;
  for I := Low(FDates) to High(FDates) do
   if FDates[I] = aDate then
   begin
     Result := True;
     Break;
   end;
end;

...

procedure  TCalendarControl.DoDateChange(aOldDate, aNewDate : TDateTime);
begin
  SetLength(FDates, 0);
  SetLength(FDates, 31);
  FMaxRecords  := -1;
  DataChange(Self);
  if Assigned(FOnDateChange) then FOnDateChange(Self, aOldDate, aNewDate);
end;

...

procedure  TCalendarControl.Paint;
   ...
          if DateInArray(CE.EntryDate) then Font.Style := Font.Style + [fsBold]
                                       else Font.Style := Font.Style - [fsBold];
          if FOptions.TrailingDays then
   ...
end;
Delphi-Quellcode:
  TDBCalendarControl = class(TCalendarControl)
  private
    FDataLink  : TFieldDataLink;
    function GetDataField: string;
    function GetDataSource: TDataSource;
    function GetField: TField;
    procedure SetDataField(const Value: string);
    procedure SetDataSource(Value: TDataSource);
    procedure CMGetDataLink(var Message: TMessage); message CM_GETDATALINK;
  protected
    procedure DataChange(Sender: TObject);         override;
    function IsDateTimeField : Boolean;
    procedure Loaded;                              override;
    procedure LoadDatesForMonth;
    procedure Notification(aComponent: TComponent;
                           Operation: TOperation); override;
  public
    constructor Create(aOwner: TComponent);        override;
    destructor Destroy;                           override;
    property Field: TField read GetField;
    property DataField: string read GetDataField write SetDataField;
    property DataSource: TDataSource read GetDataSource write SetDataSource;
  end;

...

{
************  TDBCalendarControl
}

(* public *)

constructor TDBCalendarControl.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);
  FDataLink              := TFieldDataLink.Create;
  FDataLink.Control      := Self;
  FDataLink.OnDataChange := DataChange;
end;

destructor TDBCalendarControl.Destroy;
begin
  FDataLink.Free;
  FDataLink := nil;
  inherited Destroy;
end;


(* private *)


procedure  TDBCalendarControl.DataChange(Sender: TObject);
begin
  if (FDataLink.Field <> nil) and not FDataLink.DataSourceFixed then
  begin
    if FMaxRecords <> FDataLink.DataSet.RecordCount - 1 then
    begin
      FMaxRecords := FDataLink.DataSet.RecordCount - 1;
      FDataLink.DataSourceFixed := True;
      LoadDatesForMonth;
    end;
  end;
end;

function   TDBCalendarControl.GetDataField: string;
begin
  Result := FDataLink.FieldName;
end;

function   TDBCalendarControl.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

function   TDBCalendarControl.GetField: TField;
begin
  Result := FDataLink.Field;
end;

procedure  TDBCalendarControl.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

procedure  TDBCalendarControl.SetDataSource(Value: TDataSource);
begin
  if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
    FDataLink.DataSource := Value;
  if Value <> nil then Value.FreeNotification(Self);
end;

procedure  TDBCalendarControl.CMGetDataLink(var Message: TMessage);
begin
  Message.Result := Integer(FDataLink);
end;


(* protected *)

function   TDBCalendarControl.IsDateTimeField : Boolean;
begin
  with FDataLink do
    Result := (Field <> nil) and (Field.DataType in [ftDateTime, ftTimeStamp]);
end;

procedure  TDBCalendarControl.LoadDatesForMonth;
var
  I : Integer;
  DT : TDateTime;
  Y1, Y2, M1, M2, D1, D2 : Word;
begin
  FDataLink.DataSet.First;
  for I := 0 to FDataLink.DataSet.RecordCount - 1 do
  begin
    if IsDateTimeField then
      DT := Trunc(FDataLink.Field.AsDateTime)
    else
      DT := Trunc(FDataLink.Field.AsDateTime);

    DecodeDate(DT, Y1, M1, D1);
    DecodeDate(FDate, Y2, M2, D2);
    if (Y1 = Y2) and (M1 = M2) then FDates[D1] := DT;
    FDataLink.DataSet.Next;
  end;
  Invalidate;
  FDataLink.DataSourceFixed := False;
end;

procedure  TDBCalendarControl.Loaded;
begin
  inherited Loaded;
  if (csDesigning in ComponentState) then DataChange(Self);
end;

procedure  TDBCalendarControl.Notification(aComponent: TComponent;
                                            Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (FDataLink <> nil) and
    (AComponent = DataSource) then DataSource := nil;
end;
Um die Sache Rund zu machen fehlt noch dies:

Delphi-Quellcode:

  TExDBCalendar = class(TDBCalendarControl)
  published
    property CalendarDate;
    property Colors;
    property DataField;
    property DataSource;
    property Font;
    property Options;
    property OnDayClick;
    property OnDateChange;
  end;
Das war es.


Gruss

marabu 20. Jun 2007 19:44

Re: Kalender
 
Hallo Alter Mann,

dass ein solches Control niemand braucht möchte ich nicht unterschreiben. Jedenfalls finde ich es nett von dir, dass du deine Komponente hier zur Verfügung stellst.

Beim Drüberschauen stellt sich mir die Frage, ob nicht ein Bookmark die Methode LoadDatesFromMonth() aufwerten würde. Immerhin verschiebst du den Datensatzzeiger. Dann sehe ich eine bedingte Zuweisung an DT, bei der beide Zweige gleich lauten, sodass die Bedingung eigentlich ungeprüft entfallen kann.

Für einen produktiven Einsatz würde ich eine lose Kopplung zwischen Control und Datenbank vorziehen. Hast du dir mal die Komponente TMonthCalendar und dort speziell das Ereignis OnGetMonthInfo() angeschaut?

Freundliche Grüße

Alter Mann 20. Jun 2007 19:49

Re: Kalender
 
Hi marabu,

TMonthCalendar habe ich mir schon angeschaut, allerdings nicht OnGetMonthInfo, was ich nachholen werde.
Das mit DT ist mir auch klar, habe aber noch keine bessere Idee.

Mitarbeit erwünscht.

Bis bald und Danke.

Gruss

Alter Mann

Alter Mann 20. Jun 2007 20:02

Re: Kalender
 
Hi marabu,

manchmal bin auch ich schnell(beim drüberlesen) :wink:

Klar PMonthDayState würde die Sache auch machen, aber die Compo auch 'aufblähen'.
Man muss auf OnGetMonthInfo reagieren und die Sache in Paint doch wieder umsetzen.
Es sei denn, es gibt Handfeste Argumente, die dagegen Sprechen.

MfG

Alter Mann

marabu 20. Jun 2007 20:13

Re: Kalender
 
Mir gefällt die Entkopplung von Funktionalitäten, wie sie über OnGetMonthInfo() möglich wird.

Zu der Typprüfung: Ich würde einfach eine Exception werfen, wenn sich der Feldtyp beim ersten Zugriff als falsch herausstellt. Die Prüfung sollte auf keinen Fall innerhalb einer Schleife über potentiell Tausenden von Datensätzen statt finden.

Statt DataSet.First() bietet sich vielleicht DataSet.Locate() an. So könntest du in einer riesigen Tabelle Laufzeitvorteile gewinnen. Und nicht das Bookmarking vergessen.

Gute Nacht

Alter Mann 20. Jun 2007 20:18

Re: Kalender
 
Hi marabu,

wie würde das Aussehen?
Zitat:

Statt DataSet.First() bietet sich vielleicht DataSet.Locate() an. So könntest du in einer riesigen Tabelle Laufzeitvorteile gewinnen. Und nicht das Bookmarking vergessen.
Gruss

Alter Mann

PS Entschuldigung wenn ich Dich von einer anderen 'wichtigeren' Sache abhalte.

Gute Nacht

Alter Mann 21. Jun 2007 13:39

Re: Kalender
 
Hallo,

kleine Verbesserung für den Zugriff das DB-Feld.

Bitte folgende Proceduren austauschen:

Delphi-Quellcode:
procedure  TDBCalendarControl.DataChange(Sender: TObject);
begin

  if (FDataLink.Field <> nil) and not FDataLink.DataSourceFixed then
  begin
    if FMaxRecords <> FDataLink.DataSet.RecordCount - 1 then
    begin
      FMaxRecords := FDataLink.DataSet.RecordCount - 1;
      LoadDatesForMonth;
    end;
  end;
end;

procedure  TDBCalendarControl.LoadDatesForMonth;
var
  I : Integer;
  DT : TDateTime;
  Y,
  M,
  D : Word;
  B : TBookmarkStr;
begin
  if not IsDateTimeField then
  begin
    Exception.CreateFmt('This Field ''%s'' is not a DateTimeField.', [FDataLink.FieldName]);
    Exit;
  end;

  DecodeDate(FDate, Y, M, D);
  D := 32;
  try
    while not IsValidDate(Y, M, D) do Dec(D);
  except
  end;

  B := FDataLink.DataSet.Bookmark;
  FDataLink.DataSourceFixed := True;

  for I := 1 to D do
  begin
    DT := EncodeDate(Y, M, I);
    if FDataLink.DataSet.Locate(FDataLink.FieldName,
                                DT,
                                [loCaseInsensitive, loPartialKey]) then
      FDates[I] := Trunc(FDataLink.Field.AsDateTime);
  end;
  Invalidate;
  FDataLink.DataSourceFixed := False;
  FDataLink.DataSet.Bookmark := B;
end;
Es geht jetzt schneller.

@marabu

Danke


Gruss

Alter Mann

f.siebler 22. Jun 2007 09:05

Re: Kalender
 
Moin,

gibts einen Screenshot davon? Wäre schön. :-)

Danke &
Gruß
Fabian

Alter Mann 22. Jun 2007 18:31

Re: Kalender
 
Liste der Anhänge anzeigen (Anzahl: 2)
Moin, Moin

bitteschön.

Das Bild wurde unter Vista gemacht und da ich gerade nicht auf meine Standardplatte zugreifen kann,
konnte ich die Farben nicht einstellen.

Gruss

Alter Mann 24. Jun 2007 08:48

Re: Kalender
 
Hallo,

ich habe mal eine neuere Version eingestellt.
Für alle die den Code nicht direkt übersetzen können, ist auch eine Executable bei.

Wo ich noch Schwachstellen sehe, ist das bisher realisierte Multiselect-Verfahren
(muss im Beispiel erst im Dialog ausgewählt werden).
Als Idee schwebt mir ein angezeigter Auswahlrahmen vor, aber vielleicht kann gibt
es ja auch andere Vorschläge.


Gruss

ConstantGardener 24. Jun 2007 09:58

Re: Kalender
 
Hallo Alter Mann,

nette Komponente !! Nett wären noch direkte Auswahlmöglichkeiten für das Jahr und den Monat. Evtl. noch 2 Buttons um gleich ein Jahr vor bzw. zurück springen zu können !?

Alter Mann 28. Jun 2007 16:59

Re: Kalender
 
Hallo


Ich glaube wenn bedarf besteht, dass weitere Buttons notwendig sind um gleich ein Jahr vor oder zurück springen zu können,
dann sollte es in Eigenregie erfolgen, was ja auf Grund des Sourcecode nicht weiter schwer fallen dürfte.

Die Idee jedoch ist nicht verkehrt und auch schon umgesetzt.
Standardmäßig kann mit + und - der Monat und mit PageUp und PageDown das Jahr gewechselt werden.
Sollte bedarf nach anderen Tasten bestehen, bitte schön, sie sind über die Optionen änderbar.

Viel Spaß

[Edit]
Habe alles Offline genommen.
Wer den aktuellen Source hat ...
[/Edit]


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz