Programmieren mit C++

Borland C++

MFC mit BC++ benutzen

Schaltfläche mit automatischer Wiederholung

Wie läßt sich in Zusammenarbeit mit MFC eine Schaltfläche realisieren, die permanent eine Nachricht auslöst, nicht nur beim Drücken oder Loslassen?

Frage

Das Erzeugen von Nachrichten während des Drückens einer Schaltfläche ist von Windows nicht vorgesehen. Man muß es emulieren, indem eine Nachricht zyklisch ausgelöst wird.

Als Beispiellösung wird nachfolgend die Klasse CRepeatButton implementiert, die von CButton abgeleitet ist. Diese Klasse implementiert dabei gleich auch noch etwas Service zusätzlich.

Lösung

CRepeatButton(int InitialValue = 0, 
              int Direction = FORWARD, 
              int Increment = 1,
              int min = -10000,
              int max =  10000 );

Der Konstruktor erzeugt das Objekt und ruft zur Initialisierung die Methode SetParameters() auf, die alle fünf Parameter des Konstruktors übernimmt.

Im Parameter Direction wird festgelegt, ob die Laufrichtung aufsteigend oder absteigend sein soll. Als mögliche Werte sind definiert:

Konstante

Bedeutung

FORWARD

Der aktuelle Wert wird jeweils um Increment erhöht

BACKWARD

Aktueller Wert wird um Increment erniedrigt

Increment legt die Schrittweite fest, mit der erhöht bzw. erniedrigt werden soll. Dies spielt dann eine Rolle, wenn der aktuelle Wert von der darüberliegenden Anwendung bzw. dem Nachrichten empfangenden Fenster ausgewertet werden soll. Mithin wird dann nicht nur eine Nachricht erzeugt, sondern auch ein Fortschritt angezeigt.

Die Grenzen des Durchlaufens, während die Schaltfläche gedrückt ist, geben min und max vor. Per Default ist hier +/- 10.000 vorgegeben. Es lassen sich jedoch beliebige Grenzen angeben. Ist eine Grenze erreicht, setzt die Klasse den aktuellen Wert selbständig auf die jeweils andere Grenze, d.h. im Falle des Erreichens von min wird max gesetzt und umgekehrt. Als Defaultfenster, an das die Benachrichtigung gesendet wird, nimmt der Konstruktor das Elternfenster an, das vor dem Versenden einer Nachricht automatisch ermittelt wird, wenn als aktives Fenster NULL spezifiziert ist.

Konstruktor CRepeatButton

void SetParameters(HWND hWnd,
                   int  InitialValue, 
                   int  Direction, 
                   int  Increment,
                   int  min,
                   int  max );

Die Methode SetParameters() setzt die internen Eigenschaften der Wiederholung und entspricht bis auf hWnd den Parametern des Konstruktors. Der Parameter hWnd legt das Fenster fest, an das die Benachrichtigung gesendet werden soll. Die Nachricht selbst, die versendet wird, ist in der Header-Datei der Klasse deklariert.

#define WM_SEEKBUTTONVALUECHANGED (WM_USER+10)

Zum Erzeugen der Benachrichtigungen klinkt sich die Klasse in das Ereignis WM_LBUTTONDOWN ein. Sobald der Anwender den linken Button drückt, übernimmt die Methode OnLButtonDown die Kontrolle in Form einer Schleife, die solange durchlaufen wird, bis der Button wieder losgelassen wird.

Als erstes ermittelt die Methode das Fenster, an das die Nachricht gesendet werden soll, falls mhWnd den Wert NULL besitzt.

Methode SetParameters

void CRepeatButton::OnLButtonDown( 
   UINT nFlags, CPoint point ) 
{
  CButton::OnLButtonDown( nFlags, point );
 
  int   nbiter = 0;
  MSG   mess;
  int   id  = GetDlgCtrlID( );
 
  HWND hWnd = GetSafeHwnd();
 
  if( mhWnd == NULL )
    mhWnd = GetParent()->GetSafeHwnd();

Im Anschluß daran fällt die Methode in eine while-Schleife, die erst dann wieder verlassen wird, wenn mittels PeekMessage() festgestellt wird, daß WM_LBUTTONUP eingetroffen ist. Als spezielles Feature implementiert die Methode hier auch eine Beschleunigung beim Generieren der Benachrichtigungen. Die ersten Nachrichten werden in längeren Zeitabständen erzeugt, als nachfolgende. Ab der 50. Benachrichtigung bleibt die Geschwindigkeit jedoch konstant. Realisiert wird diese Verzögerung über einen Aufruf von Sleep() mit fallenden Werten, die über die interne Indexvariable mbiter gesteuert wird.

Ereignishandler OnLButtonDown

  while( !PeekMessage( &mess, hWnd, 
                       WM_LBUTTONUP, 
                       WM_LBUTTONUP, 
                       PM_REMOVE ) )
  {
    if( nbiter < 10 )
    {
      Sleep( 100 );
    }
    else if( nbiter < 20 )
         {
           Sleep( 75 );
         }
         else if( nbiter < 30 )
              {
                Sleep( 50 );
              }
              else {
                     Sleep( 25 );
                   }
    nbiter++;

Nach der Verzögerung erfolgt das Inkrementieren des aktuellen Werts in Abhängigkeit der Laufrichtung.

    mCurrentValue += ( mDirection == FORWARD ) ? mIncrement 
                                               : -mIncrement;

    if( mCurrentValue < mMin )
    {
      mCurrentValue = mMax;
    }
    if( mCurrentValue >  mMax)
    {
       mCurrentValue = mMin;
    }

Der so aktualisierte Wert wird dann im lParam der Nachricht mit der ID der Control gemäß GetDlgCtrlID( ) im wParam an das gewünschte Fenster gesendet.

Message-Schleife

   ::SendMessage(mhWnd, 
                 WM_REPEATBUTTONVALUECHANGED,
                 id, mCurrentValue);
  }

Als Abschluß ist dann nach dem Verlassen der Schleife das Ereignis WM_LBUTTONUP neu zu erzeugen, da dieses von der Funktion PeekMessage() in der while-Schleife aus der Nachrichtenschlange gelöscht wurde.

Nachricht versenden

  SendMessage( WM_LBUTTONUP );
}

Einsatz der Klasse

Das Einbinden der Klasse CRepeatButton erfolgt typischerweise durch das Erzeugen einer Instanz im OnInitDialog- oder OnCreate-Ereignis.

backwardBut.Create       ("<", WS_CHILD | WS_VISIBLE, 
                          CRect( 50, 10, 90, 30 ), this, 
                          IDC_BACKWARD_BUTTON );
backwardBut.SetParameters(SomeCWnd.GetSafeHwnd(), 0, 
                          CRepeatButton::BACKWARD, 1, 0, 10);

Soll das Elternfenster der Schaltfläche die Benachrichtigung erhalten, kann anstelle des Fensterhandles auch NULL als erster Parameter der Methode SetParameters() übergeben werden.

Für die Bearbeitung der Benachrichtigung muß eine Behandlungsmethode implementiert und deklariert werden.

afx_msg LONG OnRepeatButtonValueChanged(UINT id, LONG value);

Die Methode OnRepeatButtonValueChanged() übernimmt als Parameter die id und den aktuellen Wert des Button, die in wParam und lParam der Nachricht enthalten sind. In die zugehörige Nachrichtentabelle ist die Nachricht aufzunehmen, hier für eine von CEdit abgeleitete Klasse CMyEdit:

WM_LBUTTONUP durchreichen

BEGIN_MESSAGE_MAP(CMyEdit, CEdit)
 //{{AFX_MSG_MAP(CMyEdit)
 //}}AFX_MSG_MAP
ON_MESSAGE( WM_REPEATBUTTONVALUECHANGED, 
            OnRepeatButtonValueChanged )
END_MESSAGE_MAP()

Das folgende Beispiel für die Implementation des Nachrichtenhandlers ist rudimentär gehalten – es wird nur der Fenstertitel geändert, indem der aktuelle Wert des Button-Zählers angezeigt wird. Hier sind beliebige Aktionen denkbar.

Nachrichtentabelle

LONG CMyEdit:: OnRepeatButtonValueChanged ( 
   UINT /* id */, LONG value )
{
 static CString str;
 str.Format("%d", value );
 SetWindowText( str );
 RedrawWindow();
 
 return 0L;
}

Die Klasse CRepeatButton kann natürlich in Kombination mit beliebigen Fenstern eingesetzt werden, so wie auch die Reaktion auf das Eintreffen der Benachrichtigung individuell gestaltet werden kann.

Methode OnRepeatButton-ValueChanged





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 03:09:39 von textarchiv.alojado.de