Manchen ist die Kürze der Intervalle (die "Auflösung") des VB-Timers nicht kurz und fein genug, manchen ist sie viel zu kurz. Falls Sie ebenfalls einmal einen Timer brauchen, dessen höchste Intervallspanne die runde Minute des VB-Timers überschreitet, und Sie längere Sekundenspannen, Minuten-, Stunden- oder gar Tages-Intervalle benötigen, könnte der hier vorgestellte erweiterte Timer genau das sein, was Sie brauchen.
Mit ihm können Sie Intervalle von 1 Sekunde bis zu rund 735.440 Jahren setzen (Nein, dass ich die Höchstspanne nicht ausgetestet habe, ist kein Reklamationsgrund, wirklich nicht!). Intern arbeitet der Timer zwar mit Sekunden, doch damit Sie sich nicht die Arbeit des Umrechnens machen müssen, können Sie auch Intervalle in Minuten, Stunden und Tagen angeben. Allerdings nur in ganzen Teilen - ein Intervall etwa der Art "12 Tage, 7 Stunden, 34 Minuten und 59 Sekunden" müssen Sie leider doch selbst in Sekunden umrechen (Nein, das ist auch kein Reklamationsgrund!).
Damit der Timer nun aber weiß, welche Einheit gemeint ist, setzen Sie die Eigenschaft IntervalUnit entsprechend auf eine der enumerierten Konstanten txSeconds, txMinutes, txHours oder txDays. Der Grund für mich, Tage als größte Intervalleinheit zu vorzugeben, ist reine Bequemlichkeit gewesen - der Aufwand der Berücksichtigung von Monatslängen, Schaltjahren und anderen zeitmesstechnischen Spitzfindigkeiten hätte den praktischen Nutzen dieser kleinen Komponente sicher gesprengt. Aber wenn Sie unbedingt wollen - den Quelltext haben Sie hier ja vorliegen - Sie brauchen bloß die paar entsprechenden Zeilen zur Umrechnung in die interne Sekunden-Basis in der Ereignis-Prozedur tmr_Timer nachzurüsten.
Die riesige Höchstspanne ergibt sich übrigens daraus, dass ich Long als Datentyp für die Eigenschaft Interval verwendet habe und dies im Gegensatz zum VB-Original-Timer auch so gemeint habe. Das Original nimmt zwar ebenfalls Long-Werte an, motzt aber, wenn der Wert größer 65.535 wird (vorzeichenloser 16-Bit-Integer). Da maximale 268.435.455 Sekunden (der positive Höchstwert eines vorzeichenbehafteten 32-Bit-Long-Integers) gerade mal gut 3.100 Tage entsprechen bzw. rund achteinhalb Jahren, wollte ich die Lebensdauer Ihrer Software-Produkte, in denen Sie diesen Timer verwenden, nicht unnötig beschränken, indem ich VB-like eine Kappung eingebaut hätte.
Das Herz der Komponente ist natürlich ein VB-Timer. Dieser läuft nur, wenn sowohl seine Eigenschaft Enabled gleich True als auch sein Interval größer als 0 ist. Dieses Verhalten habe ich aufgegriffen - Sie brauchen also nicht weiter umzudenken. Ich denke, der übrige Code der Komponente ist soweit klar, dass fast nur noch ein Wort zur privaten Hilfsprozedur zCalcInterval übrig bleibt.
Dort wird die innere Auflösung des Timers eingestellt. Sie sehen dort, dass ich das Intervall des VB-Timers auf 100 setze, wenn die gewünschte Intervalleinheit Sekunden sein soll. Warum ich das VB-Timer-Intervall nicht auf 1.000 setze, also ebenfalls auf einen festen Sekundenschritt? Das Problem ist die Funktionsweise der wiederum hinter dem VB-Timer steckenden Windows-Timer-Geschichte.
Der Intervallablauf eines Windows-Timers wird über Nachrichten des internen Windows-Nachrichtensystems übermittelt (Wenn Sie nicht wissen sollten, was dieses Nachrichtensystem ist, wäre es vielleicht eine Idee sich damit einmal zu befassen, aber das ist hier nicht das Thema. Notwendig ist dieses Wissen nur, wenn Sie in VB unter der Motorhaube basteln möchten.). Innerhalb dieses Systems werden Nachrichten nach Prioritäten gewichtet und verschickt. Timer-Nachrichten gehören zur niedrigsten Prioritätsstufe und glänzen daher nicht gerade durch Zuverlässigkeit. Sie werden zwar ziemlich exakt zum richtigen Intervall-Zeitpunkt abgesandt, müssen aber oft Schlange stehen, bis sie ihr Ziel erreichen. Und wie das mit Warteschlangen halt so ist, ist deren Länge und damit die Verweildauer darin fast nie gleich, oder gar verlässlich kalkulierbar. Damit dieses etwas wackelige System nicht vollends aus den Fugen gerät und unter Umständen in ein Timer-Nachrichten-Stakkato ausartet, wenn eine besonders lange Warteschlange abgebaut worden sein sollte und die hintenanstehenden, mittlerweile verspäteten Nachrichten dann im Pulk eintreffen, wird dafür gesorgt, dass verspätete Nachrichten gar nicht erst ankommen, wenn bereits die nächste fällig geworden und schon unterwegs ist.
Die Festlegung des VB-Timer-Intervalls auf 100 (Tausendstel Sekunden) bedeutet daher eine Genauigkeit von +/- 1/10 Sekunde. Wenn Ihnen das zu ungenau ist, können Sie den Wert reduzieren. Das Gleiche gilt im Prinzip für die übrigen Intervalleinheiten - die von mir vorgeschlagenen Auflösungen sind eigentlich willkürliche Annahmen. So gehe ich einfach mal davon aus, dass etwa bei einem Tagesintervall eine Genauigkeit von +/- 1 Minute ausreichend ist. Letztlich ist es eine Abwägung - zwischen Genauigkeit und Systembelastung. Alle Minute ein Timer-Tick stört schließlich überhaupt nicht. Aber Sie können sich vielleicht vorstellen, wie viel das System zu tun bekommt, wenn in vielen Anwendungen viele Timer mit kürzesten Intervallen am werkeln sein sollten. Meine Annahmen zu ändern, bleibt Ihnen jedoch frei überlassen.
Das Steuerelement bleibt zur Laufzeit unsichtbar (auch wenn Sie es im Bild oben sehen können), wie der VB-Timer auch. Zur Designzeit behält das UserControl immer eine Größe von 32x32 Pixels bei. Die darauf platzierte Schaltfläche (CommandButton) hat keinerlei Funktion und wird zur Laufzeit gar nicht geladen. Sie sorgt nur für die 3D-Optik zur Designzeit und enthält auch die kleine Grafik zur Repräsentation (Style = grafisch).
Zu dem kleinen Testprogramm ist nichts Besonderes zu sagen. Erwähnenswert ist vielleicht, dass die einzeilige TextBox zur Eingabe des Intervalls unter Windows 98 rechtsbündig ausgerichtet erscheint und dass darin nur Ziffern eingegeben werden können.
Private mLastDate As Date
Public Event Timer()
Public Enum txIntervalUnitConstants
txSeconds
txMinutes
txHours
txDays
End Enum
Private pInterval As Long
Private pIntervalUnit As txIntervalUnitConstants
Public Property Get Enabled() As Boolean
Enabled = tmr.Enabled
End Property
Public Property Let Enabled(ByVal New_Enabled As Boolean)
tmr.Enabled = New_Enabled
zCalcInterval
PropertyChanged "Enabled"
End Property
Public Property Get Interval() As Long
Interval = pInterval
End Property
Public Property Let Interval(ByVal New_Interval As Long)
Select Case New_Interval
Case 0
pInterval = 0
tmr.Interval = 0
Case Is > 0
pInterval = New_Interval
zCalcInterval
Case Else
Err.Raise 380
Exit Property
End Select
PropertyChanged "Interval"
End Property
Public Property Get IntervalUnit() As txIntervalUnitConstants
IntervalUnit = pIntervalUnit
End Property
Public Property Let IntervalUnit(ByVal New_IntervalUnit _
As txIntervalUnitConstants)
Select Case New_IntervalUnit
Case txSeconds To txDays
pIntervalUnit = New_IntervalUnit
zCalcInterval
PropertyChanged "IntervalUnit"
Case Else
Err.Raise 380
End Select
End Property
Private Sub tmr_Timer()
Dim nInterval As Double
Select Case pIntervalUnit
Case txSeconds
nInterval = pInterval
Case txMinutes
nInterval = pInterval * 60
Case txHours
nInterval = pInterval * 3600
Case txDays
nInterval = pInterval * 86400
End Select
If Abs(DateDiff("s", Now, mLastDate)) >= nInterval Then
RaiseEvent Timer
mLastDate = Now
End If
End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Me.Enabled = PropBag.ReadProperty("Enabled", False)
Me.Interval = PropBag.ReadProperty("Interval", 0)
Me.IntervalUnit = PropBag.ReadProperty("IntervalUnit", txSeconds)
End Sub
Private Sub UserControl_Resize()
Static sInProc As Boolean
If sInProc Then
Exit Sub
Else
sInProc = True
End If
UserControl.Size 32 * Screen.TwipsPerPixelX, _
32 * Screen.TwipsPerPixelY
sInProc = False
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Enabled", tmr.Enabled, False
PropBag.WriteProperty "Interval", pInterval, 0
PropBag.WriteProperty "IntervalUnit", pIntervalUnit, txSeconds
End Sub
Private Sub zCalcInterval()
With tmr
If .Enabled Then
If pInterval Then
Select Case pIntervalUnit
Case txSeconds
.Interval = 100
Case txMinutes
.Interval = 1000
Case txHours
.Interval = 30000
Case txDays
.Interval = 60000
End Select
mLastDate = Now
End If
End If
End With
End Sub
|
Code des ActiveX-Controls avbTimerEx

|
|