Dynamisches Dialoglayout für MFC in Visual C++ 2015

 C Programming >> C-Programmierung >  >> Tags >> MFC
Dynamisches Dialoglayout für MFC in Visual C++ 2015

In Visual Studio 2015 kommt MFC mit neuen Features (was in den letzten Jahren selten vorgekommen ist):Unterstützung für dynamisches Dialoglayout. Das bedeutet Bibliotheksunterstützung zum Verschieben und Ändern der Größe von Steuerelementen in einem Dialogfeld. In diesem Artikel werde ich zeigen, wie diese Funktion funktioniert.

Angenommen, wir haben den folgenden Dialog:

Was wir wollen, ist, dass sich die Steuerelemente im Dialog verschieben (die Schaltflächen) oder ihre Größe ändern (das Gruppenfeld, Bearbeiten und die Liste), wenn die Größe des Dialogs geändert wird:

Der Ressourceneditor unterstützt dies, kann aber auch programmgesteuert erfolgen. Wenn Sie die Eigenschaften eines Steuerelements öffnen, gibt es eine neue Kategorie namens Dynamisches Layout Damit können Sie einen Bewegungs- und einen Größentyp auswählen.

Die Optionen zum Verschieben und Ändern der Größe sind:Keine , Horizontal Vertikal , und Beide . Diese Optionen sollten selbsterklärend sein. Wichtig ist jedoch der Wert für die Bewegung und Größenänderung der X- und Y-Achse:Dies sind Verhältnisse, keine Dialogeinheiten oder Pixel, die einen Wert zwischen 1 und 100 haben und definieren, wie stark ein Steuerelement bewegt oder in der Größe geändert wird, wenn der Host Dialogfeld ändert Größe.

Um nun das im obigen Beispiel gezeigte Layout zu aktivieren, müssen wir Folgendes tun:

  • Ändern Sie die Größe des Gruppenfelds und des Listenfelds vollständig (100 %), sowohl horizontal als auch vertikal
  • Ändern Sie die horizontale Größe des Bearbeitungssteuerelements vollständig
  • Bewegen Sie die OK-Schaltfläche vollständig (100 %) vertikal
  • Bewegen Sie die Schaltfläche "Hinzufügen" vollständig horizontal
  • Bewegen Sie die Schaltflächen „Löschen“ und „Abbrechen“ vollständig horizontal und vertikal


Es ist ziemlich einfach, Werte in die dynamischen Layouteinstellungen für jedes Steuerelement einzugeben. Wenn Sie das Dialogfeld erstellen und ausführen und seine Größe ändern, werden die Steuerelemente entsprechend verschoben oder in der Größe geändert.

Diese dynamischen Layouteinstellungen werden in das Ressourcenskript (RC-Datei) der Anwendung eingefügt. Für das obige Beispiel sieht es so aus:

/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

IDD_MFCDYNLAYOUTDEMO_DIALOG AFX_DIALOG_LAYOUT
BEGIN
    0,
    0, 100, 0, 0,
    100, 100, 0, 0,
    0, 0, 100, 100,
    0, 0, 0, 0,
    0, 0, 100, 0,
    0, 0, 100, 100,
    100, 100, 0, 0,
    100, 0, 0, 0
END

In dieser Definition IDD_MFCDYNLAYOUTDEMO_DIALOG ist die Kennung des Dialogs, für den die Einstellungen definiert werden und die Zahlen im BEGIN-END Block darstellen:

  • Die erste Zeile ist ein Header, der die Versionsnummer der Struktur enthält (0 in dieser Version)
  • Die aufeinanderfolgenden Zeilen sind die dynamischen Layouteinstellungen (Verschiebungs- und Größenverhältnisse) für jedes Steuerelement im Dialogfeld, entsprechend der Reihenfolge, in der die Steuerelemente für das Dialogfeld in der Ressourcenskriptdatei definiert wurden.

Diese Einstellungen werden in ein CMFCDynamicLayout geladen Objekt (siehe afxlayout.h/cpp). Dies geschieht im OnInitDialog Methode des CDialog Klasse wie unten gezeigt:

BOOL CDialog::OnInitDialog()
{
	// execute dialog RT_DLGINIT resource
	BOOL bDlgInit;
	if (m_lpDialogInit != NULL)
		bDlgInit = ExecuteDlgInit(m_lpDialogInit);
	else
		bDlgInit = ExecuteDlgInit(m_lpszTemplateName);

	if (!bDlgInit)
	{
		TRACE(traceAppMsg, 0, "Warning: ExecuteDlgInit failed during dialog init.\n");
		EndDialog(-1);
		return FALSE;
	}

	LoadDynamicLayoutResource(m_lpszTemplateName);

Hinweis:für CPaneDialog , CDialogBar und CFormView andererseits geschieht dies im HandleInitDialog .

Diese LoadDynamicLayoutResource ist eigentlich ein Mitglied von CWnd die andere Methoden zum Arbeiten mit dynamischen Layouts enthält:

  • DynamischesLayout aktivieren :aktiviert oder deaktiviert den Layout-Manager für ein Fenster
  • IstDynamicLayoutEnabled :gibt an, ob die Layoutverwaltung für ein Fenster aktiviert ist
  • GetDynamicLayout :Ruft einen Zeiger auf den Layout-Manager ab
  • Größe des dynamischen Layouts ändern :Passen Sie die Position der vom dynamischen Layout-Manager behandelten Steuerelemente als Antwort auf WM_SIZE neu an
  • InitDynamicLayout :initialisiert den Dynamic Layout Manager als Antwort auf WM_CREATE Nachricht
	// controls dynamic layout:

	/// <summary>
	/// Enables or disables layout manager for a window.</summary>
	/// <param name="bEnable"> TRUE - enable layout management, FALSE - disable layout management.</param>
	void EnableDynamicLayout(BOOL bEnable = TRUE);

	/// <summary>
	/// This function returns TRUE, if layout management is enabled for a window; otherwise FALSE.</summary>
	/// <remarks>
	/// Call EnableDynamicLayout in order to enable or disable layout management for a window.</remarks>
	/// <returns> 
	/// TRUE, if layout management is enabled for a window; otherwise FALSE.</returns>
	BOOL IsDynamicLayoutEnabled() const { return m_pDynamicLayout != NULL; }

	/// <summary>
	/// Call this function to retrieve a pointer to layout manager.</summary>
	/// <remarks>
	/// Call EnableDynamicLayout in order to enable or disable layout management for a window.</remarks>
	/// <returns> 
	/// Returns a pointer to the window layout manager or NULL if layout wasn't enabled.</returns>
	CMFCDynamicLayout* GetDynamicLayout() { return m_pDynamicLayout; }

	/// 
	/// The method is called to adjust positions of child controls. 
	/// It recalculates positions of child controls if layout management is enabled for a window.
	virtual void ResizeDynamicLayout();
	void InitDynamicLayout();
	BOOL LoadDynamicLayoutResource(LPCTSTR lpszResourceName);

Mit diesen Methoden können Sie die dynamische Layoutverwaltung spontan aktivieren oder deaktivieren.

  1. Anfangs ist die dynamische Layoutverwaltung so eingestellt, dass sich die Steuerelemente bewegen und ihre Größe ändern, wenn die Größe des Dialogs geändert wird.

  2. Deaktivieren Sie die dynamische Layoutverwaltung und die untergeordneten Steuerelemente werden nicht mehr angepasst.
  3. Aktivieren Sie das dynamische Layout-Management erneut und es funktioniert wieder.

Der Haken dabei ist, dass einfach CWnd::EnableDynamicLayout aufgerufen wird funktioniert nicht, da diese Methode nur das CMFCDynamicLayout löscht und neu erstellt Beispiel.

void CWnd::EnableDynamicLayout(BOOL bEnable)
{
	if (m_pDynamicLayout != NULL)
	{
		delete m_pDynamicLayout;
		m_pDynamicLayout = NULL;
	}

	if (!bEnable)
	{
		return;
	}

	m_pDynamicLayout = new CMFCDynamicLayout;
}

Genauso wie CDialog::OnInitDialog Sie müssten CWnd::LoadDynamicLayoutResource aufrufen . Daher sollte der korrekte Code zum Aktivieren und Deaktivieren der dynamischen Layoutverwaltung wie folgt aussehen:

void CMFCDynLayoutDemoDlg::EnableDynamicLayoutHelper(bool const enable)
{
   if (enable && this->IsDynamicLayoutEnabled())
      return;

   this->EnableDynamicLayout(enable ? TRUE : FALSE);

   if (enable) 
   {
      this->LoadDynamicLayoutResource(m_lpszTemplateName);
   }
}

Wie bereits erwähnt, kann das Festlegen der Verschiebungs- und Größenwerte für die dynamische Layoutverwaltung programmgesteuert mithilfe von CMFCDynamicLayout erfolgen Klasse. Dies ist wichtig, wenn die Steuerelemente dynamisch und nicht in der Ressourcenvorlage erstellt werden. Was Sie tun müssen, ist:

  • erstellen Sie das CMFCDynamicLayout Objekt
  • Speichern Sie das Host-Fenster (den Dialog) in diesem Objekt
  • Fügen Sie die untergeordneten Steuerelemente mit ihren Bewegungs- und Größeneinstellungen hinzu

Der folgende Code bietet dieselbe dynamische Layoutfunktionalität wie zuvor gezeigt, außer dass alles vom Code festgelegt wird. Beachten Sie, dass Sie EnableDynamicLayoutHelper aufrufen müssen von OnInitDialog .

void CMFCDynLayoutDemoDlg::EnableDynamicLayoutHelper(bool const enable)
{
   if (enable && this->IsDynamicLayoutEnabled())
      return;

   this->EnableDynamicLayout(enable ? TRUE : FALSE);

   if (enable)
   {
      SetupDynamicLayout();
   }
}

void CMFCDynLayoutDemoDlg::SetupDynamicLayout()
{
   auto manager = this->GetDynamicLayout();
   if (manager != nullptr)
   {
      auto movenone = CMFCDynamicLayout::MoveSettings{};
      auto moveboth100 = CMFCDynamicLayout::MoveSettings {};
      moveboth100.m_nXRatio = 100;
      moveboth100.m_nYRatio = 100;
      auto movex100 = CMFCDynamicLayout::MoveSettings {};
      movex100.m_nXRatio = 100;
      auto movey100 = CMFCDynamicLayout::MoveSettings {};
      movey100.m_nYRatio = 100;

      auto sizenone = CMFCDynamicLayout::SizeSettings{};
      auto sizeboth100 = CMFCDynamicLayout::SizeSettings{};
      sizeboth100.m_nXRatio = 100;
      sizeboth100.m_nYRatio = 100;
      auto sizex100 = CMFCDynamicLayout::SizeSettings{};
      sizex100.m_nXRatio = 100;

      manager->Create(this);

      manager->AddItem(IDC_STATIC_GROUPBOX, movenone, sizeboth100);
      manager->AddItem(IDC_LIST1, movenone, sizeboth100);
      manager->AddItem(IDC_EDIT1, movenone, sizex100);
      manager->AddItem(IDC_BUTTON_ADD, movex100, sizenone);
      manager->AddItem(IDC_BUTTON_CLEAR, moveboth100, sizenone);
      manager->AddItem(IDOK, movey100, sizenone);
      manager->AddItem(IDCANCEL, moveboth100, sizenone);
   }
}

Tatsächlich kann der gleiche Code wie oben mit Hilfe mehrerer statischer Methoden von CMFCDynamicLayout anders ausgedrückt werden die Instanzen von MoveSettings erstellen und Größeneinstellungen .

void CMFCDynLayoutDemoDlg::SetupDynamicLayout()
{
   auto manager = this->GetDynamicLayout();
   if (manager != nullptr)
   {
      manager->Create(this);

      manager->AddItem(IDC_STATIC_GROUPBOX, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontal(100));
      manager->AddItem(IDC_LIST1, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
      manager->AddItem(IDC_EDIT1, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontal(100));
      manager->AddItem(IDC_BUTTON_ADD, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
      manager->AddItem(IDC_BUTTON_CLEAR, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone());
      manager->AddItem(IDOK, CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeNone());
      manager->AddItem(IDCANCEL, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone());
   }
}

Eine wichtige Sache, die hier zu beachten ist, ist, dass dieser Code CWnd::LoadDynamicLayoutResource nicht aufruft weil es keine Einstellungen in der Ressourcenskriptdatei gibt. Alle diese Einstellungen werden in diesem Fall nur programmgesteuert bereitgestellt.

Wenn sich Steuerelemente vollständig (100 %) über eine oder beide Achsen bewegen oder in der Größe ändern müssen, ist das Festlegen der richtigen Werte für das Layout einfach. Es wird jedoch kompliziert, wenn Steuerelemente nicht seitlich positioniert sind oder mit komplizierteren Regeln verschoben oder in der Größe geändert werden müssen. Nehmen wir ein Beispiel, bei dem die Schaltflächen OK und Abbrechen vertikal unten und horizontal zentriert positioniert sind. Wenn die Größe des Dialogs geändert wird, sollten sie die ursprüngliche Größe behalten, aber sie sollten immer unten in der Mitte bleiben.

In diesem Fall ist das Y-Verhältnis für die Bewegung wieder 100. Aber wie ist das Bewegungsverhältnis auf der X-Achse? Zur Bestimmung braucht man Papier und Stift. Grundsätzlich müssen wir herausfinden, wie viel sich die Schaltflächen auf X bewegen, wenn die Breite um 100 Einheiten zunimmt. Das ist das Verhältnis, das wir einstellen müssen.

Anfangs hat der Dialog 251 Einheiten, also zwei Hälften von 125 und 126 Einheiten. Wir wollen die Tasten um 10 Einheiten auseinander halten. Das bedeutet, dass die Schaltfläche „OK“ bei 70 Einheiten links ausgerichtet ist und die Schaltfläche „Abbrechen“ bei 130 Einheiten leuchtet.

Dann vergrößern wir den Dialog um 100 Einheiten. Es ist jetzt 351 und die Habenden haben 175 und 176. Die Knöpfe sind immer noch 10 Einheiten voneinander entfernt und ihr Mit beträgt immer noch jeweils 50 Einheiten. Das bedeutet, dass die Schaltfläche „OK“ jetzt bei 120 Einheiten linksbündig ausgerichtet ist und die Schaltfläche „Abbrechen“ bei 180 Einheiten linksbündig ausgerichtet ist.

Die Schlussfolgerung ist, dass sich ihre beiden linken Ränder um 50 Einheiten bewegt haben, und das ist der Wert Wir müssen das X-Verhältnis ihrer Bewegungseinstellung festlegen. (Denken Sie daran, dass der Wert ein Verhältnis ist, aber 50 Einheiten von 100 Einheiten sind auch 50 %.)

Was ist, wenn die Schaltflächen OK und Abbrechen beide in der Mitte ihrer jeweiligen Hälfte auf der X-Achse ausgerichtet werden sollen und die Ränder immer beibehalten werden sollen? Mit anderen Worten, sie sollten sich wie folgt ändern:

In diesem Beispiel hat der Dialog zunächst 231 Einheiten und das bedeutet zwei Hälften von 115 und 116 Einheiten. Die Schaltflächen haben beide eine Breite von 60 Einheiten, sind also 27 oder 28 Einheiten zu den Rändern ausgerichtet.

Wenn die Breite des Dialogs um 100 Einheiten auf 331 Einheiten zunimmt, werden die beiden Hälften haben 165 und 166 Einheiten. Die Schaltflächen behalten ihre Ränder bei, sodass ihre neue Breite 110 Einheiten beträgt.

(Beachten Sie, dass das obige Bild gestreckt ist und die Ränder irreführend sein können.)

Die Schlussfolgerung lautet:

  • Der OK-Button bewegte sich nicht horizontal, sondern vergrößerte seine Breite von 60 Einheiten auf 110 Einheiten, also 50 %.
  • Die Schaltfläche "Abbrechen" wurde horizontal verschoben und ist jetzt linksbündig bei 193 Einheiten anstatt der ursprünglichen 143 Einheiten ausgerichtet. Das bedeutet, dass er sich horizontal um 50 % bewegt hat. Seine Größe stieg von 60 Einheiten auf 110 Einheiten, also ebenfalls um 50 %.

Wenn diese Werte eingestellt sind, werden die Schaltflächen wie beabsichtigt in der Größe angepasst und positioniert.

Weitere Informationen finden Sie unter Dynamisches MFC-Dialoglayout.

Demo-Quellcode:
Dynamisches MFC-Layoutmanagement - Demo 1 (2318 Downloads)
Dynamisches MFC-Layoutmanagement - Demo 2 (1858 Downloads)
Dynamisches MFC-Layoutmanagement - Demo 3 (1756 Downloads)