Steuerelemente dynamisch erstellen
-
Moin,
In einer linearen Liste speichere ich Namen,Vornamen und das Datum des Eingangs, die ich aus einer Datei auslese.Bei manchen Listenelementen fehlt ab und zu das Datum, welches nachgetragen werden muss.
Nun wollte ich mir dazu einen Dialog basteln, der dann tabellarisch jeweils die Namen und Vornamen ( CStatic Element ) und ein Eingabefeld ( CEdit) enthält. Nun habe ich aber das Problem, das sich die fehlenden Werten immer verschieden sind. Deshalb wollte ich sie dynamisch erstellen.
int CPersonenliste::AnzahlOfMissingValues() { int anzahl=0; it_personenliste=m_personenliste.begin(); while(it_personenliste != m_personenliste.end()) { person = *it_personenliste; if (strcmp(person.item,"")==0) anzahl++; } }
Mit dieser Methode ermittle ich, wieviele Werte fehlen
... int anz; CPersonenliste liste; anz=liste.AnzahlOfMissingValues(); if (anz > 0) { //modalen Dialog aufrufen CUpdatePersonenliste updListe; CUpdatePersonenliste.DoModal(); } ... ...
Nun ja soweit so gut. Nun habe ich vor die Anzahl der fehlenden Werte an den modalen Dialog zu übergeben. Mit Hilfe der OnInitDialog Methode möchte ich dann mit Hilfe einer schleife von 1 bis Anzahl jeweils die dynamischen Controls erstellen.Nun geht das ?
Angenommen die OnInitDialog Methode sieht so aus :
BOOL CUpdatePersonenliste::OnInitDialog() { CEdit* pEdit = new CEdit; for (int i =0;i< anzahl;i++) { pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, CRect(x1+10, y1+ 10,x2+10, y2+10), this, i); } return true; }
So wie es in dieser Schleife steht, wird es generell nicht gehen, das wei0ß ich. Aber ist es allgemein möglich, die Controls mit Hilfe der Schleife im Dialogfeld zu plazieren ?
Danke
Bodo
-
Und wieso machst du nicht z.b. ein CListCtrl, mit 3 Kolonnen.
1. Kolonne: Name
2. Kolonne: Vorname
3. Kolonne: Datum (Anfangs komplett leer)Dann noch ein CEdit unterhalb oder nebendran oder wo auch immer. Wenn du nun auf ein Item in der Liste klickst, wird der Edit-Box Inhalt aktualisiert, bzw. wenn noch kein Datum eingegeben wurde gelöscht.
Wenn ein anderes Item ausgewählt wird, wird das, was aktuell in der Edit-Box ist als Datum des alten Items gespeichert und dann in der 3. Kolonne aufgeführt.Viel einfacher als deine Idee, würd ich sagen. ^^
Grüssli
-
mmh,ja, hascht recht
Warum bin ich nicht selber auf diese Idee gekommen
Liegt wohl an der Hitze
Danke
Bodo
-
ok, ich habe inzwischen geschafft die Liste mit den Daten zu füllen. Jedoch habe ich Probleme mit dem aktualiesieren der Daten. Kannst du mal büdde erläutern wie das geht, das wenn man auf ein Item in der Liste klickt, der Inhalt im Editfeld angezeigt wird und falls man da was ändert, die Daten übernommen werden und auch angezeigt werden ?
Ich habe mir einen zusätzlichen Button erstellt, der dann den Inhalt des Editfeld'sn ausließt und den Index der markierten Spalte ermittelt. Ich bekomm es irgendwie nicht hin, das er dann an dieser Postion, die markiert war, das Datum aktualisiert :
void CUpdatePersonData::OnBnClickedUpdatedataOk() { CString Datum; int pos=-1; m_Edit=(CEdit*)GetDlgItem(IDC_EDIT1); m_Edit->GetWindowText(Datum); m_listCtrl = (CListCtrl*)GetDlgItem(IDC_LIST1); pos=m_listCtrl->GetSelectionMark(); m_listCtrl->SetItemText(pos,3,Datum); // TODO: Fügen Sie hier Ihren Kontrollbehandlungscode für die Benachrichtigung ein. }
-
am besten fügst du deinem Edit eine CString Variable hinzu und deiner Liste eine CListCtrl Control Variable. Über UpdateData(TRUE) speicherst du den Inhalt des Editfeldes in die CString Variable und über UpdateData(FALSE) lädst du das zeugs von der CString Variable in das Editfeld. Immer GetDlgItem oder SetDlgItemText usw. zu verwenden ist viel zu umständlich! Das eignet sich nur mal für zwischen durch. Ansonsten sollte man es über das DoDataExchange realisieren. Der Assisten hilft einem da ja auch wunderschön.
Also nun zum eigentlichen. Du fügst dir einen Eventhandler zu deinem ListCtrl-Steuerelement hinzu, welcher auf NM_CLICK reagiert.
void C...Dlg::OnNMClick...(NMHDR *pNMHDR, LRESULT *pResult) { LPNMITEMACTIVATE pNMITEMA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); UpdateData(TRUE); // Daten aus dem Edit holen. m_ListCtrl.SetItemText(nOldItem, 2, m_strEditDate); // Text aus dem Edit in die 3. Kolonne des alten Items schreiben. m_nOldItem = pNMITEMA->iItem; // Altes Item aktualisieren. m_strEditDate = m_ListCtrl.GetItemText(pNMITEMA->iItem, 2); // Neuer Text für das Edit holen. UpdateData(FALSE); // Daten ins Edit schreiben. *pResult = 0; }
Dabei braucht man noch in der Klassendefinition ein
int m_nOldItem;
In diesem wird immer der alte Wert gespeichert, auf welchem beim nächsten Klick zugegriffen wird und verändert wird.
Ehm jo ... so in etwa würde ich es machen. Du kannst ja auch selber noch ein wenig rumtüftlen ist dir ja nicht verboten
Grüssli
-
Ich habe es so gemacht, wie du es geschrieben hast. Leider bekomme ich dann beim Ausführen folgende Meldung :
Bei dlgdata.cpp (Zeile 703 ) meldet er mir dann eine Debug Assertion Failure
Der Code :
class CUpdatePersonData : public CDialog { DECLARE_DYNAMIC(CUpdatePersonData) public: CUpdatePersonData(CWnd* pParent = NULL); // Standardkonstruktor virtual ~CUpdatePersonData(); // Dialogfelddaten enum { IDD = IDD_UPDATEDATA }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung DECLARE_MESSAGE_MAP() public: afx_msg void OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult); void link(CEingang *obj); BOOL OnInitDialog(); CListCtrl *m_listCtrl; CEingang *eingang; int m_nOldItem; afx_msg void OnBnClickedUpdatedataOk(); afx_msg void OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult); CListCtrl m_ListCtrl; CString m_strEditDate; };
und die Implementierung der Methode OnNMClickList1(....)
void CUpdatePersonData::OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult) { LPNMITEMACTIVATE pNMITEMA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); UpdateData(TRUE); // Daten aus dem Edit holen. m_listCtrl->SetItemText(m_nOldItem, 1, m_strEditDate); // Text aus dem Edit in die 3. Kolonne des alten Items schreiben. m_nOldItem = pNMITEMA->iItem; // Altes Item aktualisieren. m_strEditDate = m_listCtrl->GetItemText(pNMITEMA->iItem, 1); // Neuer Text für das Edit holen. UpdateData(FALSE); // Daten ins Edit schreiben. *pResult = 0; }
-
Der Fehler tritt in deiner DoDataExchange Funktion auf, bei einem DDX_Control. So wie ich das sehe, denke ich mal machst du ein UpdateData(TRUE); bevor das entsprechende Fenster erstellt ist. Das geht nicht und führt zu einem Fehler. Mehr kann ich dazu jetzt auch nicht sagen ...
Grüssli
-
Ich habe es inzwischen hinbekommen, hab allerdings noch eine Frage. In der FAQ steht ein Thread wie man verhindern kann, das die Columnen nicht zur Verlaufzeit geändert werden können. Ich hab den Code 1:1 kopiert und hab das Problemn das beim Listenfenster nichts angezeigt wird. Irgendwie habe ich das Gefühl das die OnPaint Methode nicht korrekt geht. Kann das sein ?
-
Mir fällts irgendwie schwer zu verstehen, wieso ein Abfangen von WM_NOTIFY dazu führt, dass die vertikalen linien nicht mehr gezeichnet werden.
Was passiert bei Dir denn wenn du OnPaint() nicht überschreibst?
-
Wie gesagt, ich hab folgendes Problem :
Der Dravere hat mir ja schon dazu geraten den Datenaustausch über DoDataExchange zu machen. Bei mir jedoch funzt das net, er schmirrt mit einer Debug Fehlermeldung ab.Warum er abschmirrt, das weiss ich nicht. Dieser Dialog ist allerdings nicht mein Primärer Dialog der Anwendung, sondern es wird ein modaler Dialog in einer Klasse aufgerufen ( die auch nicht von CDialog abgeleitet ist).
Bei mir klappt es nur, wenn ich es alles mit GetDlgItem() anspreche. Ich nehme mal an das der Fehler bei mir ist, nur ich finde ihn nichtIn der Klasse CEingang verwalte ich eine lineare Liste, mit Namen,Vornamen und Geburtstag.
class CEingang { public: CEingang(void); ~CEingang(void); CPerson *item; list <CPerson*> m_personenliste; list <CPerson*> ::iterator it_personenliste; public: void AddPerson(CString Name,CString Vorname,CString Ueberweiser,CString Einsendedatum,CString befund,CString DayOfEndBefund); CString GetDayOfEingang(CString Name,CString Vorname); CString GetUeberweiser(); CString GetBefundArt(CString Name,CString Vorname); int GetNumberOfMissingValues(); void SetDayOfEingang(CString Name,CString Vorname,CString Datum); int len(const char *wert); };
Mit Hilfe der Methode GetNumberOfMissingValues ermittel ich die Anzahl der Kanditaten, bei denen das Eingangsdatum fehlt - welches dann nachgetragen werden muss in dem Listenelement beim modalen Dialog.Diesen Dialog starte ich dann so :
.. .. if(anzahl > 0) { CUpdatePersonData m_data; m_data.link(eingang); m_data.DoModal(); } .. ..
Beim modalen Dialog habe ich dann als erstes die OnInitDialog Methode überschirieben, damit er mir gleich bei der Initialisierung des Dialoges das Listenfeld mit den Daten füllt.
BOOL CUpdatePersonData::OnInitDialog() { m_ListCtrl = (CUpdateList*)GetDlgItem(IDC_LIST1); m_ListCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); m_ListCtrl->InsertColumn(0,"Name",LVCFMT_CENTER,120,1); m_ListCtrl->InsertColumn(1, "Vorname",LVCFMT_CENTER,120,1); m_ListCtrl->InsertColumn(2,"Eingangsdatum",LVCFMT_CENTER,108,1); eingang->it_personenliste=eingang->m_personenliste.begin(); while(eingang->it_personenliste != eingang->m_personenliste.end()) { eingang->item=*eingang->it_personenliste; if(strcmp(eingang->item->EinsendeDatum,"")==0) { m_ListCtrl->InsertItem(0,eingang->item->Name); m_ListCtrl->SetItemText(0,1,eingang->item->Vorname); m_ListCtrl->SetItemText(0,2,eingang->item->EinsendeDatum); } eingang->it_personenliste++; } return true; }
Dann habe ich mir eine Klasse von CListCtrl abgeleitet :
#pragma once #include "afxcmn.h" class CUpdateList : public CListCtrl { public: CUpdateList(void); ~CUpdateList(void); afx_msg void OnPaint(); BOOL OnNotify(WPARAM wParam,LPARAM lParam,LRESULT *pResult); DECLARE_MESSAGE_MAP() };
und hab dann die OnPaint Methode und OnNotify Methode überschrieben.Ich bin mir auch nicht ganz sicher, das ich die Message WM_NOTIFY korrekt abfange. In der Message Map habe ich nur folgendes stehen :
BEGIN_MESSAGE_MAP(CUpdateList,CListCtrl)
ON_WM_PAINT()
END_MESSAGE_MAP()Muss ich da eventuell noch was eintragen ?
Gruss Bodo