Neuen Tab einfügen 2

UInt

Mitglied
Hallo noch einmal, :)

meine dynamischen Tabs auf CFormView funktionieren jetzt halbwegs, aber noch nicht genau so wie ich es gerne möchte.:mad:
Also, ich kann Tabs mit List Control erzeugen oder löschen während das Programm läuft. Nun möchte ich der List Control des ausgewählten Tabs ein Item hinzufügen.

Es gibt aber 2 Probleme:

Ein Bisschen Code zum Anfang:
Code:
////////////////////////////////////////////////////////
// New Tab
//static int TabIndex = 1 (es werden 2 tabs bei der Initialisierung eingefüg)
// CDialog1Dlg *pDialog1

bool CTest_8View::NewTab()
{
	pDialog1 = new CDialog1Dlg;
	

	TCITEM TabItem;
	TabIndex++;
	
	// create the Tabs
	CString s;
	s.Format(_T("Neuer Tab %d"), TabIndex+1);
	CString TabName = s;
	TabItem.mask    = TCIF_TEXT | TCIF_PARAM;
	TabItem.lParam  = (LPARAM)pDialog1;
	TabItem.pszText = (LPTSTR)(LPCTSTR)TabName;
	m_TabCtrl.InsertItem(TabIndex, &TabItem);
	m_TabCtrl.SetItem(TabIndex, &TabItem);
	

	// create Dialogs
	pDialog1->Create(IDD_DIALOG_1, &m_TabCtrl);

	
	
	// Dialog position		   
	CRect rcItem;
	m_TabCtrl.GetItemRect(0, &rcItem);
	pDialog1->SetWindowPos( NULL,
                       rcItem.left,
                       rcItem.bottom + 1,
                       0,
                       0,
                       SWP_NOSIZE | SWP_NOZORDER );
	
 
	// show the first dialog
	pDialog1->ShowWindow(SW_SHOW);
	
	m_TabCtrl.SetCurFocus(TabIndex);
	m_TabCtrl.SetCurSel(TabIndex);
	
	return true;
}
///////////////////////////////////////////////////////////
// Neuer Tab auf Tastendruck
void CTest_8View::OnBnClickedButton1()
{
	NewTab();
}

void CTest_8View::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult)
{
    TCITEM  item;
    CWnd*   pWnd;
    int     iNewTab = m_TabCtrl.GetCurSel();

    item.mask = TCIF_PARAM;

    // aktuellen Tab ausblenden
    m_TabCtrl.GetItem(m_uiSelectedTab, &item);
    if( item.lParam )
    {
        pWnd = reinterpret_cast<CWnd*> (item.lParam);
        pWnd->ShowWindow(SW_HIDE);
    }

    // neuen Tab anzeigen
    m_TabCtrl.GetItem(iNewTab, &item);
    if(item.lParam)
    {
        pWnd = reinterpret_cast<CWnd*> (item.lParam);
        pWnd->ShowWindow(SW_SHOW);
    }

	*pResult = 0;
}

void CTest_8View::OnTcnSelchangingTab1(NMHDR *pNMHDR, LRESULT *pResult)
{
	m_uiSelectedTab = m_TabCtrl.GetCurSel();
	*pResult = 0;
}

PROBLEM 1:
Ich kann im Moment nur der List Control im letzten, neu erzeugten Tab Items hinzufügen, weil mein Pointer darauf zeigt. Wie sage ich dem Pointer, dass er auf eine andere List Control zeigen soll, wenn ich einen anderen Tab wähle?

Code:
//////////////////////////////////////////////////////////
// New Item
void CTest_8View::OnBnClickedNewitem()
{
	// TODO: Add your control notification handler code here
	CEnterItemDlg Dlg;
	// seed the random number generator so that the 
    // numbers will be different every time the programm runs
    srand( (unsigned)time(NULL) );
	char strNumber[20];

	int number1 = rand() % 999; // %999 = 3 numbers from 0 to 9
	int number2 = rand() % 999;

	sprintf_s(strNumber, _T("%d-%d"), number1, number2);
	Dlg.m_ItemNumber = strNumber;
	
	if(Dlg.DoModal() == IDOK)
	{
		LVITEM lvItem;

		lvItem.mask = LVIF_TEXT;
		lvItem.iItem = 0;
		lvItem.iSubItem = 0;
		lvItem.pszText = strNumber;
           ?------> pDialog1->m_List.InsertItem(&lvItem); <------- ?
                           //pDialog1 ist Dialog mit dem List Control
	}	
}


PROBLEM 2:
Ich würde gerne die ganzen, neu erzeugten Dialoge und Tabs löschen, wenn das Programm geschlossen wird. Der Code funktioniert beim Löschen der einzelnen Tabs. Aber im Destruktor von CFormView scheitert er mit eine Fehermeldung „Debug assertion failed“, wenn ich das Programm mit geöffneten Tabs schliesse. Ich bekomme keine Fehler beim Kompilieren.
Code:
/////////////////////////////////////////////////////////////////////////////////
// Destruktor

CTest_8View::~CTest_8View()
{
	
TCITEM  item;
    CWnd*   pWnd;
      for (int i = 0; i < TabIndex; i++)
	  {
		  m_TabCtrl.GetItem(i, &item); <------ DEBUG ASSERTION FAILED
		if(item.lParam)
		{
			pWnd = reinterpret_cast<CWnd*> (item.lParam);
			pWnd->DestroyWindow();
			delete pWnd;
		}
		
		m_TabCtrl.DeleteItem(i);
	  }
	 
	  
}

/////////////////////////////////////////////////////////
// delete Tab
void CTest_8View::OnBnClickedButton2()
{
	// TODO: Add your control notification handler code here

	TCITEM  item;
    CWnd*   pWnd;
	item.mask = TCIF_PARAM;

    int  iSelTab = m_TabCtrl.GetCurSel();

	m_TabCtrl.GetItem(iSelTab, &item);
	if(item.lParam)
    {
	pWnd = reinterpret_cast<CWnd*> (item.lParam);
    if(!pWnd->DestroyWindow())
		MessageBox("Freigeben des Tabsheets 1 fehlgeschlagen");
	delete pWnd;
	}
	m_TabCtrl.DeleteItem(iSelTab);
	
	// neuen Tab anzeigen
	if (iSelTab == TabIndex)
	{
		m_TabCtrl.SetCurFocus(iSelTab--);
		m_TabCtrl.SetCurSel(iSelTab);

		m_TabCtrl.GetItem(iSelTab, &item);
		if(item.lParam)
		{
			pWnd = reinterpret_cast<CWnd*> (item.lParam);
			pWnd->ShowWindow(SW_SHOW);
		}

		TabIndex--;
		return;
	}

	m_TabCtrl.SetCurFocus(iSelTab);
	m_TabCtrl.SetCurSel(iSelTab);

    m_TabCtrl.GetItem(iSelTab, &item);
    if(item.lParam)
    {
        pWnd = reinterpret_cast<CWnd*> (item.lParam);
        pWnd->ShowWindow(SW_SHOW);
    }

	TabIndex--;
}

Danke im Voraus.
 
Hallo,

PROBLEM1: Mache es genauso, wie beim Tab-Wechsel. Über "GetItem()" bekommst du ja die nötigen Infos zum Dialog.
C++:
TCITEM  item;
m_TabCtrl.GetItem(m_uiSelectedTab, &item);
    
if( item.lParam )
{
    CDialog1Dlg *pDialog = reinterpret_cast<CDialog1Dlg*>(item.lParam);

    // ... Item einfügen        
}
PROBLEM2: An welcher Stelle genau kommt denn der Fehler? Ich vermute, dass im Destruktor die Controls des FormViews nicht mehr existieren, so dass ein "DestroyWindow()" hier Probleme machen könnte. Versuche stattdessen, einen Handler für die WM_DESTROY Message des FormViews einzufügen und dort die Aufräumarbeiten durchzuführen.

Gruß
MCoder
 
Hi,

PROBLEM 1:
gelöst, danke.:) :) :) :)

PROBLEM 2:
Der Error taucht in der mit „<-----------„ markierten Zeile. Es scheint wirklich so als ob die Controls da nicht mehr existieren.

Mit „einen Handler für die WM_DESTROY Message des FormViews einzufügen“ meinst du etwa:
Code:
BEGIN_MESSAGE_MAP(CTest_8View, CFormView)
	// Standard printing commands
	
	ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CTest_8View::OnTcnSelchangeTab1)
	ON_NOTIFY(TCN_SELCHANGING, IDC_TAB1, &CTest_8View::OnTcnSelchangingTab1)
	MESSAGE_HANDLER(WM_DESTROY, OnDestroy)  <--------------
END_MESSAGE_MAP()

LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
  {
      //... delete Windows

PostQuitMessage(0);
      bHandled = FALSE;
      return 0;
  }

------------------------------------ODER------------------------------------------

// es scheint aber auchnicht zu klappen

//afx_msg void OnDestroy();

CTest_8View::OnDestroy()
{
	CFormview::OnDestroy();
}

Ich mache aber irgendwas falsch. In der Zeile “<--------“ bekomme ich einen Fehler beim Kompilieren:

“error C3861: 'MESSAGE_HANDLER': identifier not found”

Könnest du mir eventuell den richtigen Code zeigen.
 
Hallo,

stimmt, du hattest ja das Auftreten des Fehlers extra gekennzeichnet. Hab ich glatt übersehen :eek:

Den WM_DESTROY - Handler kann man doch eigentlich automatisch einfügen lassen. Wie kommst du denn da auf die "MESSAGE_HANDLER" - Syntax?
Im Header kommt irgendwo vor DECLARE_MESSAGE_MAP die Deklaration der OnDestroy-Methode und in der .cpp steht ON_WN_DESTROY. Deine Implementation der Methode ist ok.
C++:
// in der .h-Datei

// ...
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()

// in der .cpp-Datei

BEGIN_MESSAGE_MAP(CTest_8View, CFormView)
    // Standard printing commands
    
    ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CTest_8View::OnTcnSelchangeTab1)
    ON_NOTIFY(TCN_SELCHANGING, IDC_TAB1, &CTest_8View::OnTcnSelchangingTab1)
    ON_WM_DESTROY() 
END_MESSAGE_MAP()
Gruß
MCoder
 
Das ist doch der schon bekannte Fehler?

Du gehst mit einer For-Schleife alle Items durch und rufst mit dem Index DeleteItem auf. Da rutschen natürlich alle oberen Items nach unten.
 
MCoder,

danke für den Code. Es funktioniert. Kannst du mir sagen, wie man es automatisch in VS einfügen kann? Ich konnte es nirgendwo finden.

Endurion,

danke für den Tipp. Daran habe ich wirklich nicht gedacht. Ich habe es so gelöst, dass die Löschreihenfolge beim letzten Tab anfängt.
Code:
int iTabs = m_TabCtrl.GetItemCount();
    	for (int i = iTabs; i > 0; i--)
	  {
		m_TabCtrl.GetItem(i-1, &item);
		...............
		
		m_TabCtrl.DeleteItem(i-1);
	  }
Kennst du einen kürzeren Code dafür?
 
Kannst du mir sagen, wie man es automatisch in VS einfügen kann?
Hallo, das hängt von der VS-Version ab. Bei VS6 geht das über den Klassenassistenten und bei den neuen Versionen über die Eigenschaften zum FormView. Da gibt es einen Abschnitt (ich glaube, mit dem Blitz-Symbol), der die Windows-Nachrichten enthält.

Was das Löschen betrifft: Sehr viel kürzer wird's nicht, aber hier noch eine andere Variante:
C++:
while( m_TabCtrl.GetItemCount() )
{
    TCITEM  item;
    m_TabCtrl.GetItem(0, &item);

    // ... 

    m_TabCtrl.DeleteItem(0);
}
Gruß
MCoder
 
MCoder,

danke für deine Hilfe. Ich hab’s. In VS 2005 geht es mit der Taste „Messages“ rechts von dem „Blitz“, aber ohne deinen Hinweis hätte ich sie nie gefunden.:)

Und danke für die andere Löschvariante. Sie sieht ordentlicher aus.:) :) :) :)
 
Zurück