Ich habe letztens versucht Informationen zu obigem Thema hier zu
finden. Leider nur mit mäßigem Erfolg. Es gibt ungeheuer viele
Beiträge dazu, aber nur wenige Lösungsvorschläge.
Darum nun hier mal ein Vorschlag von mir (Hab lange überlegt und
vieles probiert).
Die Aufgabe war, dass in dem Formular zum Einen das Mausrad normal
(Springen zwischen den Datensätzen) funktioniert, zum Anderen aber in
einem enthaltenen Memofeld zum Scrollen dieses Feldes benutzt werden
soll.
Ich habe in das Formular statt des Memofeldes ein Unterformular
eingebaut, in welchem nur das Memofeld existiert. Beide Formulare sind
über die ID des Datensatzes verknüpft. Bei der Eigenschaft "Anfügen
zulassen" für das U-Form ist "NEIN" angegeben. Dadurch wird ein
Datensatzwechsel mit dem Mausrad im Memofeld verhindert, da nur ein
einziger Datensatz im Unterformular angezeigt werden kann.
Im Unterformular ist folgender Code enthalten:
### Code Anfang
###############################################################
Option Compare Database
Option Explicit
Dim TextPosition As Long
Private Sub Form_MouseWheel(ByVal Page As Boolean, ByVal Count As Long)
'Die Variable Page kann getrost ignoriert werden, diese hat keinerlei
Auswirkungen.
'zumindest hab ich keine feststellen können.
'Die Variable Count zeigt an, in welche Richtung gescrollt wird.
' Count = -1 nach oben scrollen
' Count = 1 nach unten scrollen
Dim TextLänge As Long
'Textlänge des Memofeldes bestimmen
TextLänge = Len(Memofeld)
If IsNull(TextLänge) Then TextLänge = 0
'neue Position der Einfügemarke (Cursor) errechnen
'über (Count*100) springt der Cursor 100 Zeichen vor oder zurück
TextPosition = TextPosition + (Count * 100)
If TextPosition < 0 Then TextPosition = 0
If TextPosition > TextLänge Then TextPosition = TextLänge
'Einfügemarke (Cursor) im Memofeld neu setzen
With Memofeld
.SelLength = 0
.SelStart = TextPosition
End With
End Sub
Private Sub Memofeld_KeyPress(KeyAscii As Integer)
'Unterformular mit Tab-Taste verlassen. Das Feld Text8 ist im
Hauptformular
'das, in der Reihenfolge nächste Feld nach dem Unterformular.
If KeyAscii = 9 Then
Forms!hFormTest.SetFocus
Forms!hFormTest.Form!Text8.SetFocus
End If
End Sub
### Code Ende
###############################################################
Die Funktion ist, wie folgt. Wenn das Memofeld im Unterformular den
Focus erhält, würde das Mausrad nun die Datensätze des
Unterformulars wechseln. Da nur ein einziger Datensatz angezeigt werden
kann, unterbleibt das aber. Normal wird beim Mausrad zuerst das
Ereignis "Bei Mausrad" ausgeführt, und dann der Datensatz gewechselt.
Kann der Datensatz nicht gewechselt werden, wird nur der Code von "Bei
Mausrad" ausgeführt, wodurch der Cursor durch den Text wandert, und
damit das Memofeld gescollt wird.
Es geht übrigends auch ohne Unterformular. Bei dem Ereignis "Bei
Focuserhalt" einen Filter auf das Formular legen, der die Anzeige auf
den aktuellen Datensatz beschränkt. Dann den gleichen Code, wie oben
für das Mausrad verwenden. Beim Ereignis "Bei Focusverlust" den Filter
wieder aufheben. Hier gibts aber das Problem, wie bei meiner Frage
"Welches Feld bekommt als nächstes den Focus??" (Ich hab leider keine
Ahnung, wie ich hier Links einfügen kann. Hab keinen sepziellen
Newsreader.)
Ich wollte diese Lösung hier mal zur Diskusion stellen, und bin auf
Eure Reaktionen sehr gespannt. Und wenn mir jemand sagen kann, wies
noch besser geht, würde ich mich über Anregungen sehr freuen.
Viele Grüße
Sven
[Code gesnipt}
> Es geht übrigends auch ohne Unterformular. Bei dem Ereignis "Bei
> Focuserhalt" einen Filter auf das Formular legen, der die Anzeige auf
> den aktuellen Datensatz beschränkt. Dann den gleichen Code, wie oben
> für das Mausrad verwenden. Beim Ereignis "Bei Focusverlust" den Filter
> wieder aufheben. Hier gibts aber das Problem, wie bei meiner Frage
> "Welches Feld bekommt als nächstes den Focus??" (Ich hab leider keine
> Ahnung, wie ich hier Links einfügen kann. Hab keinen sepziellen
> Newsreader.)
Dazu hatte ich Dir zwei Lösungsansätze genannt.
> Ich wollte diese Lösung hier mal zur Diskusion stellen, und bin auf
> Eure Reaktionen sehr gespannt. Und wenn mir jemand sagen kann, wies
> noch besser geht, würde ich mich über Anregungen sehr freuen.
Aktiviere die vertikale Scrollbar für das Feld (Eigenschaften:
Format\Scroll Bars\->vertical).
--
Grüßle vom Bodensee
Jörg Ostendorp
- Access-FAQ: www.donkarl.com
- my home: www.meersburg.de
> > "Welches Feld bekommt als nächstes den Focus??" (Ich hab leider keine
> > Ahnung, wie ich hier Links einfügen kann. Hab keinen sepziellen
> > Newsreader.)
>
> Dazu hatte ich Dir zwei Lösungsansätze genannt.
Danke nochmal an dieser Stelle dafür. Du hattest ja schon die
eventuellen Probleme mit dem Scrollen des Formulars und die fehlende
Eleganz ;-) in den Vorschlägen angemerkt. Im Übrigen, ich hab auch
das probiert, und bin erst durch das Testen daran auf diese Lösung
gekommen.
> Aktiviere die vertikale Scrollbar für das Feld (Eigenschaften:
> Format\Scroll Bars\->vertical).
Die hab ich schon an, damit man mit diesen durch das Feld scrollen
kann. Aber mein "Kunde" hängt sehr an seinen Gewohnheiten von den
anderen Anwendungen her, und erwartet das scrollen mit dem Mausrad in
dem Memofeld.
Viele Grüße
vom Main
Nur damit wir nicht aneinander vorbeireden:
- Es geht doch schon um ein normales Textfeld von Access, welches als
Datenherkunft ein Memofeld einer Tabelle zugewiesen bekommt (also ganz
normal), nicht um irgendein RTF-/ActiveX-Control o.ä.?
- Meine Antwort mit der ScrollBar war nicht so gemeint, daß Du *anstelle*
des Mausrades mit der Scrollbar scrollen sollst, sondern so, daß Access bei
Textfeldern ohne ScrollBar die Mausradbewegung auf Formularebene umsetzt,
sprich einen Datensatzwechsel erzwingt, bei eingeschalteter ScrollBar
hingegen die Mausradbewegung, so wie von Dir gewünscht, auf das Textfeld
überträgt, dieses also scrollt, ohne den Datensatz zu wechseln.
So sollte IMHO jedenfalls das Standardverhalten von Access sein.
-Welche Access-Version verwendest Du
Ich verwende Access 2003, und es ist ein normales Textfeld mit Memo als
Datenherkunft, die vertikale Leiste ist an. Trotzdem wechselt im
normalen Formular das Mausrad den Datensatz. Ich habs grad nochmal
probiert. Hab ich im Access was nicht richtig eingestellt?
Ah, nein, sorry, mein Fehler. Ich habe hier für das Scrollen im VBE ein
Zusatztool laufen (FreeWheel,
http://www.geocities.com/SiliconValley/2060/freewheel.html),
welches das von mir beschriebene Verhalten bewirkt.
Also zurück, lese mir jetzt geschwind noch mal Dein OP durch ;-)
also der zweite Versuch :-)
Ich mach mal Fullquote:
Comments:
1. Schöne Idee, noch schöner, daß Du sie in der NG vorstellst!
2. Was mich spontan stört:
a) Die Notwendigkeit des Ufos, das macht Bezüge undggf noch weitere Dinge
komplizierter (Tab etc)
b) Du scrollst, indem Du den Cursor bzw. Caret um 100 Zeichen
weiterschiebst.
- Eigentliche sollte dieser beim Scrollen bleiben, wo es ist
- Du kannst nicht wissen, wei weit Du scrollst, eine Zeile kann ggf. nur
ein Zeichen, evtl aber auch 1024 enthalten.
- Beides ist nicht gerade "Windows-like".
Alternative:
Eine Alternative ist das Subclassing des Formulares und das eigene
Verwalten der Mousemessages. Da die ganzen Miesepeter hier in der NG an
sowas aber wenig Freude haben, hier noch eine einfachere Alternative per
PeekMessage:
Das Prinzip:
- Das Scrollen des Textfeldes erfolgt nicht über das Verschieben des
Cursors sondern ganz normal über eine SendMessage (WM_VSCROLL) an das
Textfeld (Fenster).
- Dabei stellt sich das Problem, daß das Handle des Textfeldes bekannt sein
muß. Das richtige Fenster wird von Access erzeugt, wenn das Feld den Fokus
erhält. Leider werden GotFocus und OnEnter jedoch bereits *vor* der
Abarbeitung der WIndowsnachrichten gefeuert. Das Fenster existiert zu
diesem Zeitpunkt noch nicht in jedem Fall.
-Daher wird hier mit einer ms Verzögerung ein Timer gestartet (unschön) in
dem die weiteren Prozesse ablaufen. Zu diesem Zeitpunkt existiert das
Fenster dann endlich, und dessen Handle wird per GetFocus() festgehalten.
Zudem wird mit der Funktion PeekWheelMessage ein Loop gestartet, der auf
eine entsprechende Mausradbewegung wartet (Peekmessage).
-Wird diese Mausnachricht von Windows gefeuert, wird sie in eine
VScroll-Message übersetzt (->Textfeld scrollt) und zugleich aus dem
Message-Queue entfernt, sprich nicht mehr ans Formular weitergeleitet
(->kein Datensatz wechsel).
Zu den Apis und Konstanten siehe ggf. MSDN.
'#########
Option Compare Database
Option Explicit
Private Declare Function GetFocus _
Lib "user32" ( _
) As Long
Private Declare Function SendMessage _
Lib "user32.dll" _
Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Any _
) As Long
Private Declare Function PeekMessage _
Lib "user32" _
Alias "PeekMessageA" ( _
lpMsg As Msg, _
ByVal hWnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long _
) As Long
Private Declare Function WaitMessage _
Lib "user32" ( _
) As Long
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type Msg
hWnd As Long
Message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
Private Const WM_MOUSEWHEEL As Long = &H20A
Private Const WM_VSCROLL As Long = &H115
Dim h As Long
Private Sub Form_Load()
End Sub
Private Sub Form_Timer()
Me.TimerInterval = 0
h = GetFocus
PeekWheelMessage
End Sub
Private Sub PeekWheelMessage()
Dim Message As Msg
Do While Not h = 0
WaitMessage
If PeekMessage(Message, _
h, _
WM_MOUSEWHEEL, _
WM_MOUSEWHEEL, _
1) _
Then
SendMessage h, _
WM_VSCROLL, _
Abs(HIWord(Message.wParam)) - 120, _
0
End If
DoEvents
Loop
End Sub
Private Function HIWord(dw As Long) As Integer
If dw And &H80000000 Then
HIWord = (dw \ 65535) - 1
Else
HIWord = dw \ 65535
End If
End Function
Private Sub MeinTextfeld_GotFocus()
Me.TimerInterval = 1
End Sub
Private Sub MeinTextfeld_LostFocus()
h = 0
End Sub
'###########
Zu2. Das sind Dinge, die mich an meinem Code ebenfalls stören, darum
ja auch der Versuch mit dem Filter (komplizierte Bezüge, Tab, usw.).
Mit meinen zwar soliden, aber nie ganz ausreichenden Kenntnissen in VBA
war es mir auch nicht möglich, das Feld anders, als mit dem Cursor zu
Scrollen zu bewegen. Ich muss halt noch viel lernen. :-))
Ich hab Deinen Code einmal ausprobiert. Er funktioniert super, und er
gefällt mir viel besser, als meine "Krücke". Vor Allem, weil sich das
Feld genauso verhällt, wie man es von Windows her kennt. Dann noch
ohne das komplizierte Gebastel wegen Tab oder Filter rein und wieder
raus.
Einen kleinen Schönheitsfehler gibt es aber noch bei Deiner Lösung.
Wenn man am Ende des Textes (egal, ob vorn oder hinten) ankommt, und
weiter am Rad dreht, kommt von Windows der Klang, wie bei einer
Fehlermeldung aber ohne Fehlermeldung. Ich hab mal versucht
herauszufinden, was das verursacht, um es eventuell abzufangen. Ich
kann die Stelle aber nicht finden. Hast Du dazu vielleicht noch einen
Tip für mich?
Vielen Dank für Deine Unterstüzung. Ich hab mal wieder viel
dazugelernt.
Viele Grüße
Sven
> Einen kleinen Schönheitsfehler gibt es aber noch bei Deiner Lösung.
> Wenn man am Ende des Textes (egal, ob vorn oder hinten) ankommt, und
> weiter am Rad dreht, kommt von Windows der Klang, wie bei einer
> Fehlermeldung aber ohne Fehlermeldung. Ich hab mal versucht
> herauszufinden, was das verursacht, um es eventuell abzufangen. Ich
> kann die Stelle aber nicht finden. Hast Du dazu vielleicht noch einen
> Tip für mich?
Ich habe bei mir sämtliche Geräusche, die Windows so von sich gibt,
deaktiviert, weil sie mich unglaublich nerven :-)
Der Beep kommt, wenn die SendMessage nicht mehr ausgeführt werden kann,
weil, wie Du ja auch schreibst, die maximale Scrollposition erreicht ist.
Also: Vor der SendMessage zunächst noch die ScrollPosition abfragen und die
Sendmessage eben nur bedingt abschicken. Ergänze mal den Code:
'Im Deklarationsbereich:
Private Declare Function GetScrollInfo _
Lib "user32.dll" ( _
ByVal hWnd As Long, _
ByVal n As Long, _
ByRef lpScrollInfo As SCROLLINFO _
) As Long
Private Const SIF_RANGE = &H1
Private Const SIF_PAGE = &H2
Private Const SIF_POS = &H4
Private Const SIF_ALL = SIF_RANGE Or SIF_PAGE Or SIF_POS
Private Const SB_VERT As Long = 1
Private Type SCROLLINFO
cbSize As Long
fMask As Long
nMin As Long
nMax As Long
nPage As Long
nPos As Long
nTrackPos As Long
End Type
'Funktion ersetzen:
Private Sub PeekWheelMessage()
Dim Message As Msg
Dim sb As SCROLLINFO
Dim sb1 As SCROLLINFO
Dim updown As Long
Do While Not h = 0
WaitMessage
If PeekMessage(Message, _
h, _
WM_MOUSEWHEEL, _
WM_MOUSEWHEEL, _
1) _
Then
sb.cbSize = LenB(sb)
sb.fMask = SIF_ALL
GetScrollInfo h, SB_VERT, sb
updown = Abs(HIWord(Message.wParam)) - 120
'(sb.nmin = 0, sb.nmax = 100)
If (updown = 0 And sb.nPos > 0) Or _
(updown = 1 And sb.nPos < 100) _
Then
SendMessage h, _
WM_VSCROLL, _
updown, _
0
End If
End If
DoEvents
'(sicherheitshalber zurücksetzen:)
sb = sb1
Loop
End Sub
Jetzt funktioniert es super. Vielen Dank für Deine Hilfe.
Ich habe meine Gurke von Code (im Vergleich zu Deinem) grad in die
Tonne getreten. :-))
Naja, nicht ganz. Vielleicht brauch ich ihn ja nochmal für was
Anderes.
Ich werde mir bei Gelegenheit mal die Zeit nehmen, und mir das Was und
Wie von Deinem Code ganz genau anschauen. Wie schon gesagt, ich muss
noch viel lernen.
Nochmal vielen Dank und viele Grüße
Sven