# [C++] Schließen des Dialogs verhindern



## proBier (22. Juli 2003)

Ich arbeite mit Visual C++ .Net und MFC. Meine Anwendung ist Dialog based und besteht aus einem Eingabefeld und einem Button. Nach dem Start der Anwendung kann diese durch ein betätigen der Eingabetaste geschlossen werden, das ist aber nicht gewünscht. Gewünscht ist: Nach Eingabe in das Eingabefeld und drücken der Entertaste soll das gleiche Ereignis ausgeführt werden wie wenn ich auf den Button klicke. Stattdessen schließt sich die Anwendung.

Also konkret am Beispiel: Im Eingabefeld wird ein Suchbegriff eingegeben und nach klicken auf den Button Suchen wird nach dem String gesucht. Wenn ich aber den String eingebe und Enter drücke schließt sich die Anwendung.

Wie kann ich das Verhalten korrigieren?


----------



## Kachelator (23. Juli 2003)

Öffne die Dialogressoure und selektiere den OK-Button.
Anschliessend den Classwizard aufmachen (z.B. mit Ctrl-W).
Nun kannst Du einen eigenen Messagehandler für den Button (IDOK) erzeugen. Selektiere dazu rechts BN_CLICKED (das wird ausgelöst, wenn man diesen Button klickt). Nun noch ganz rechts auf "Funktion erzeugen und editieren" (IIRC) klicken. 

Jetzt sollte Deine Dialogklasse eine Methode names OnOk() haben, die aufgerufen wird, wenn man den OK-Button klickt. Darin kannst Du
1. CDialog::OnOK(); rausnehmen (das schliesst den Dialog)
2. Deinen eigenen Code reinsetzen, z.B. Suche starten oder so

HTH


----------



## proBier (23. Juli 2003)

Danke für die hilfreiche Antwort!

Das Prinzip war richtig und es funzt jetzt auch so wie es soll. Hier mal der Weg wie ich vorgegangen bin:

1. Dialog Ressource auswählen
2. "Ok" Button auswahlen und Doppelklicken (oder rechte Maustaste auf Button und "Add Eventhandler" wählen)
3. Aus der erstellten Funktion OnOk(); entfernen und durch eigene Funktion/Methode z.b. DoSearch() ersetzen. Fertig.

(Vorgehen gilt für fürs .NET Framework 1.0.3705)


----------



## Kachelator (23. Juli 2003)

Richtig, mit Doppelklick geht es schneller!


----------



## proBier (23. Juli 2003)

Halt, das geht doch nicht so einfach! Denn dann funktioniert der Button "Beenden" oder "OK" (also der mit IDOK) nicht mehr.
Als Ausweg hab ich nen neuen Button mit IDCANCEL erstellt um das programm über einen Beenden Button zu schließen.

Gibt es eigentlich eine Möglichkeit festzustellen, in welchem Eingabefeld ich mich befinde? Also ne Art getFocus()?


----------



## Kachelator (26. Juli 2003)

Ach so, Du willst den OK-Button behalten und nur den Druck auf Return abfangen, wenn Deine Edit fokussiert ist?

Dann könntest Du Dir vom Wizard ein PreTranslateMessage() in den Dialog einbauen lassen. Diese Funktion bekommt alle Windowsnachrichten als Erste, bevor was passiert. Auch die von der Editbox. Wenn Du dort den übergebenen HWND mit dem der Editbox vergleichst und bei Übereinstimmung guckst, ob ein WM_KEYDOWN (Taste gedrückt) mit dem Wert für Return kommt, kannst Du die Message selbst behandeln und abfangen. Ein zurückgegebenes TRUE (in PreTranslateMessage()) sollte eigentlich die weitere Verarbeitung (sprich Dialog schliessen) auch verhindern, IIRC.

HTH


----------



## proBier (28. Juli 2003)

oh, Jetzt wird es mir zu hoch.

Also nochmal zum Problem: Ich habe eine Editbox (ein Feld zur Eingabe des Suchbegriffes) und einen Button, auf den man klickt um die Suche zu starten. Außerdem existiert ein Button zum Beenden (mit ID IDCANCEL und nicht mehr IDOK).
Wenn ich den Suchbegriff in das Suchfeld eingebe und Enter drücke soll gesucht werden, was es jetzt auch macht. Nur als noch der Beenden Button IDOK war wurde das programm beendet.

Zur PreTranslateMessage(): Wie füge ich die denn mit dem Wizard ein? Wo finde ich den Wizard? Ich hab bei mir folgendes gefunden:


```
void CpbsDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_SEARCHBOX, strSearchString);
	DDX_Text(pDX, IDC_EDIT_SEARCHPATH, strSearchPath);
}

BEGIN_MESSAGE_MAP(CpbsDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BUTTON_INFO, OnBnClickedButtonInfo)
	ON_BN_CLICKED(IDC_BUTTON_SEARCH, OnBnClickedButtonSearch)
	ON_EN_KILLFOCUS(IDC_EDIT_SEARCHPATH, OnEnKillfocusEditSearchpath)
	ON_BN_CLICKED(IDOK, OnBnClickedOk)
END_MESSAGE_MAP()
```

Hat das vielleicht etwas damit zu tun?
Wie kann ich den HWND (was issn das eigentlich?) mit der Editbox vergleichen?
Wie kann ich feststellen welche Taste gedrückt (also Return) wurde?
Wie fang ich Messages ab und behandle diese?

Wie gesagt C++ ist Neuland für mich, bin daher für nen guten Rat dankbar!


----------



## Kachelator (29. Juli 2003)

Mit Wizard meinte ich den Classwizard. Den kennst Du schon. Er lässt sich auch mit Ctrl-W öffnen. Du kannst aber auch in der Klassenansicht (normalerweise da, wo auch die Dateiübersicht ist), per Kontextmenü einer (von der MFC abstammenden) Klasse Messagehandler bzw. vieruelle FUnktionen zufügen, wie PreTranslateMessage() eine ist. 

Hier kommt ein Schnipsel. Lass Dir PreTranslateMessage() reinsetzen und dann mach sowas wie:


BOOL CpbsDlg:reTranslateMessage(MSG* pMsg) 
{
  switch( pMsg->message )
  {
  case WM_KEYDOWN:
    switch( pMsg->wParam )
    {
    case VK_RETURN:
      SuchenOderWasAuchImmer(); // Hier handeln!
      return TRUE; // TRUE unterbindet weitere Verarbeitung

    case 'X':
      return TRUE; // x wird ignoriert, nur ein beispiel
      break;
    }
  }

  return CDialog:reTranslateMessage(pMsg);
}




Das sollte funktionieren. (Tut's jedenfalls bei mir).

PS: Irgendwann krieg ich das dann auch mit der Codeformatierung hin


----------



## proBier (29. Juli 2003)

Also bei C++ .NET ist es so, dass es keinen ClassWizard mehr gibt (genauso wie WizardBar). In der Hilfe wurde folgender Weg angegeben:

1. im Class View die Klasse auswählen (einfacher Klick)
2. in den Properties auf Overrides (vorletztes Symbol)
3. PreTranslateMessage suchen und über die rechte Spalte mit <Add> PreTranslateMessage hinzufügen
4. Jetzt noch den Code anpassen

So und das hab ich mal gemacht die Pretranslate sieht jetzt so aus:


```
BOOL CpbsDlg::PreTranslateMessage(MSG* pMsg)
{
  switch( pMsg->message ) {
    case WM_KEYDOWN:
      switch( pMsg->wParam ) {
        case VK_RETURN:
          CpbsDlg::DoSearch(); // Hier handeln!
          return TRUE; // TRUE unterbindet weitere Verarbeitung}
        break;
      }
  }
  return CDialog::PreTranslateMessage(pMsg);
}
```

Jetzt wird egal wo immer bei drücken von Return die DoSearch() ausgeführt.
Nur ist es jetzt so dass die anderen Buttons nicht mehr auf Return reagieren. Z.B.: ich bewege mich mit der Tabulator Taste über den Beenden Button und drücke Return. Jetzt wird nicht wie gewünscht der Dialog geschlossen, sondern die Suche ausgeführt.
Wie kann ich dieses Verhalten entsprechend ändern?

ps. Codeformatierung: Eckige Klammer auf + CODE + eckige Klammer zu + jetzt der Code + eckige Klammer auf + /CODE + eckige Klammer zu


----------



## Kachelator (29. Juli 2003)

> Also bei C++ .NET ...


Stimmt, hast Du in deinem ersten Post erwähnt. *rot werd* Hätte genauer hinlesen sollen. Ich arbeite meist noch mit VC++6.



> Nur ist es jetzt so dass die anderen Buttons nicht mehr auf
> Return reagieren. Z.B.: ich bewege mich mit der Tabulator Taste über den Beenden Button und drücke Return. Jetzt wird nicht wie gewünscht der Dialog geschlossen, sondern die Suche ausgeführt.


Ich hab's gerade mal so gelöst (quick&dirty):

```
BOOL CDlgtestDlg::PreTranslateMessage(MSG* pMsg) 
{
  ASSERT( GetDlgItem( IDC_BUTTON1 ) );

  //- gucken, ob der mit der Message übergebene HWND derselbe ist
  //- wie bei meinem Control (dann hat dieses das Return gekriegt)
  if(  GetDlgItem( IDC_BUTTON1 )->GetSafeHwnd() == pMsg->hwnd )  
  {
    switch( pMsg->message )
    {
      case WM_KEYDOWN:
        switch( pMsg->wParam ) 
        {
          case VK_RETURN:
            MessageBox( "Return" );
            return TRUE; // TRUE unterbindet weitere Verarbeitung}
            break;
        }
    }
  }
  return CDialog::PreTranslateMessage(pMsg);
}
```
Es gibt vermutlich 50 elegantere Lösungen, z.B. ein CEdit als Member einzusetzen und/oder Verwendung einer von CEdit abgeleiteten Klasse. Wenn da jemand noch eine Idee hat...



> ps. Codeformatierung: Eckige Klammer auf + CODE + eckige Klammer zu + jetzt der Code + eckige Klammer auf + /CODE + eckige Klammer zu


Danke!


----------



## mukay (17. September 2003)

*elegantere lösung*

Ich glaube für dieses Problem gibt es allerdings eine elegantere Lösung, also bei c++ 6.0 geht das, und ich glaube nicht das sie das in .Net abgeschafft haben, denn dieses Anwendung benötigt man ständig in Dialogen. Unter Eigenschaften (Properties) zum Button gibts eine Checkbox die StandardButton (Default Button) heisst  (im 2. Register). Wenn man dies markiert, wird es dem Ok Button weggenommen und der, in diesem Fall, Suchen Button wird dann auch bei der Ausführung etwas dicker gemahlt. So wie mich diese Anwendung ansieht, würde das völlig ausreichen, als mit Messages vorher abfangen.
Aber nichts für ungut, viel Spass beim Coden


----------



## Kachelator (19. September 2003)

Natürlich, das müsste auch gehen... Vielen Dank für den Hinweis.


----------

