Forum: PC-Programmierung *.txt Dokument als Tabelle anzeigen in Visual C++ 6


von Frank (Gast)


Lesenswert?

Hallo alle zusammen,
ich möchte den Inhalt eines *.txt Dokuments als Tabelle in Microsoft 
Visual C++ darstellen.
Das Problem liegt nun aber nicht am einlesen der Daten, sondern an der 
Darstellung der Tabelle selbst.
In Visual C++ hab ich leider nur ein Listfeld, was mir glaub ich nicht 
viel bringt, da ich mehrere Spalten benötige. Was Eigenschaft 
"Mehrspaltig" eines Listenfeldes bringt kapier ich auch nicht so 
richtig^^ Da ändert sich doch nichts oder?
Nun bin ich auf die ActivX DataGrid Elemente gestoßen. Ist es sinnvoll 
da mit ActivX zu arbeiten oder gibts eine gute Alternative?

von Sebastian (Gast)


Lesenswert?

Hallo Frank,
ich fang grad erst mit Visual C++ an. Soweit ich gelesen hab gibt es 
neben der "normalen" Listbox noch ein ListControl welches sich dafür 
besser eignen soll. Leider hab ich weder damit noch mit dienen ActivX 
Elementen bisher Erfahrung. Aber vielleicht einfach mal in der MSDN 
Library nach "ListControl" suchen, da findet sich sicher was.

Grüße

Sebastian

von M. H. (doktorgnadenlos)


Lesenswert?

Du kannst in einer Listbox sehrwohl Spalten generieren.
Der Schlüsselbegriff dafür heißt "InsertColumn" bzw. "InsertItem".

Hier ein Programmausschnitt :

CListCtrl * pListCtrl = (CListCtrl *)GetDlgItem(IDC_LIST_STATISTIK);
pListCtrl->InsertColumn(0,"Spalte 1",LVCFMT_LEFT,60,0);
pListCtrl->InsertColumn(1,"Spalte 2",LVCFMT_LEFT,80,0);


Um in den Spalten Einträge vorzunehmen benötigt man sowas :

LV_ITEM item;
pListCtrl->InsertItem(0,"Text",0);
item.mask = LVIF_TEXT;
item.iItem = 0;


Einfach mal ausprobieren, wird schon hinhauen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Du kannst in einer Listbox sehrwohl Spalten generieren.
> [...]
> CListCtrl * pListCtrl = (CListCtrl *)GetDlgItem(IDC_LIST_STATISTIK);


Du verwechselst CListBox und CListCtrl.
Letzteres, auch als ListView bekannt, kann mehrspaltige Inhalte 
darstellen, ersteres ist deutlich primitiver und für eine mehrspaltige 
Darstellung annähernd völlig unbrauchbar.

von Karl H. (kbuchegg)


Lesenswert?

M. H. wrote:
> Du kannst in einer Listbox sehrwohl Spalten generieren.

ListBox != ListCtrl

> Der Schlüsselbegriff dafür heißt "InsertColumn" bzw. "InsertItem".
>
> Hier ein Programmausschnitt :
>
> CListCtrl * pListCtrl = (CListCtrl *)GetDlgItem(IDC_LIST_STATISTIK);
> pListCtrl->InsertColumn(0,"Spalte 1",LVCFMT_LEFT,60,0);
> pListCtrl->InsertColumn(1,"Spalte 2",LVCFMT_LEFT,80,0);

Sei aber nett zu deinem User und speichere die Spaltenbreite
in der Registry ab.

Beim Erzeugen der Columns (also typischerweise in der OnInitDialog
eines CDialogs):
1
  pListCtrl->InsertColumn(0,"Spalte 1",LVCFMT_LEFT,
2
                          AfxGetApp()->GetProfileInt( "List", "S1", 60 ),
3
                          0);
4
  pListCtrl->InsertColumn(1,"Spalte 2",LVCFMT_LEFT,
5
                          AfxGetApp()->GetProfileInt( "List", "S2", 80 ),
6
                          0);

und im OnClose Handler eines Dialogs
1
  CListCtrl * pListCtrl = (CListCtrl *)GetDlgItem(IDC_LIST_STATISTIK);
2
3
  AfxGetApp()->WriteProfileInt( "List", "S1", pListCtrl->GetColumnWidth( 0 ) );
4
  AfxGetApp()->WriteProfileInt( "List", "S2", pListCtrl->GetColumnWidth( 1 ) );

Mich ärgert sowas immer tierisch, wenn sich Programme die GUI
Einstellungen nicht merken. Als Benutzer bist du dann immer am
ständigen Verschieben von Spalten, um überhaupt arbeiten zu können
nur damit dann beim nächsten Programmstart alles wieder von vorne
losgeht. Dabei lässt sich das mit einfachsten Mitteln und ohne
großen Aufwand abstellen.

von Frank (Gast)


Lesenswert?

also irgendwie tut das nicht, bisher hab ich das so gemacht

Im RessourcenEditor ein "Listenfeld" über meine FormView gelegt, dort 
bei Eigenschaft "Mehrspaltig" aktiviert. Dann hab ich in der Klassen 
meiner FormView eine Membervariable vom Typ CListCtrl * definiert.
Nun hab ich für meine FormView-Klasse die Funktion "PreCreateWindow" 
angelegt und führe dort folgenden Code aus.

*****************MEIN CODE********************

m_pList = (CListCtrl *)GetDlgItem(IDC_TEST_LIST);
m_pList->InsertColumn(0,"Spalte 1",LVCFMT_LEFT,60,0);
m_pList->InsertColumn(1,"Spalte 2",LVCFMT_LEFT,80,0);

**************MEIN CODE ENDE******************

Wenn das ganze nun gestartet wird erhalte ich eine "DEBUG ASSERTION 
FAILED" mit nem Fehler in der winocc.cpp. Klicke ich auf "Ignorieren" 
kommt folgende Fehlermeldung:

"Unbehandelte Ausnahme in Test.exe (MFC42D.DLL): 0xC0000005: Access 
Violation."


Ist das mit dem "Listenfeld" überhaupt richtig?

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:

>
> Wenn das ganze nun gestartet wird erhalte ich eine "DEBUG ASSERTION
> FAILED" mit nem Fehler in der winocc.cpp. Klicke ich auf "Ignorieren"
> kommt folgende Fehlermeldung:

Ignorieren ist bei sowas immer schlecht.
Klicke auf "Weiter" und schau dir im Code an, wie es zu dieser
Assertion kommt. Das gibt dann normalerweise schnell Auskunft
darüber, was schief läuft.

> Im RessourcenEditor ein "Listenfeld"

Du willst kein Listenfeld (also eine ListBox), du willst ein
Listenelement (ein ListControl).
Das steht in der Toolbox in der linken Spalte, gleich unter
dem Schieberegler.
Das setzt du auf den FormView.
Danach in die Eigenschaften und umstellen:
Unter "Formate" stellst du die 'Ansicht' um auf: Bericht

Damit sollte das dann erst mal laufen.

von Karl H. (kbuchegg)


Lesenswert?

Übrigens:

Diesen HickHack
m_pList = (CListCtrl *)GetDlgItem(IDC_TEST_LIST);

kannst du dir auch sparen. Lass dir doch vom Resource-Wizard gleich
eine Control Variable für dieses Control bauen, dann brauchst du
dich nicht selbst drum kümmern, dass du Zugang zum Control hast.

von Frank (Gast)


Lesenswert?

das mit der Ressource vom Wizard hab ich auch so umgesetzt und die 
dazugehörende Zeile im Code dann auch rausgeschmissen.
Aber das Einfügen von Spalten geht trotzdem nicht.


_AFXCMN_INLINE int CListCtrl::InsertColumn(int nCol, const LVCOLUMN* 
pColumn)
{ ASSERT(::IsWindow(m_hWnd)); return (int) ::SendMessage(m_hWnd, 
LVM_INSERTCOLUMN, nCol, (LPARAM)pColumn); }

Das Problem liegt in dieser Funktion bei "IsWindow(m_hWnd)" da kennt er 
kein m_hWnd bzw. hat einen ungültigen Ausdruck darin stehn.

In welcher Funktion lass ich am besten die Spalten einfügen?
Da ich kein Dialog sondern FormViews verwende gibt es leider keine 
"OnInit"-Funktion. Bisher füge ich sie in der "OnCreate" ein aber wie 
gesagt geht das nicht.

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:

> "OnInit"-Funktion. Bisher füge ich sie in der "OnCreate" ein aber wie
> gesagt geht das nicht.

OnCreate wird zu früh sein.
Solche Dinge macht man in der OnInitialUpdate

von Frank (Gast)


Lesenswert?

bei den ganzen Funktionen blickt doch niemand mehr durch.
Leider macht es keinen Unterschied ob ich die InsertColumn in der 
OnCreate oder in der OnInitialUpdate ausführe. Der Fehler bleibt 
derselbe.

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:
> bei den ganzen Funktionen blickt doch niemand mehr durch.
> Leider macht es keinen Unterschied ob ich die InsertColumn in der
> OnCreate oder in der OnInitialUpdate ausführe. Der Fehler bleibt
> derselbe.

Dann machst du irgendetwas falsch.
Habe hier ein Testprojekt (VC++ 6.0) mit einem FormView aufgesetzt.
Auf die Form ein ListControl gesetzt. In der OnInitialUpdate des
Views die InsertColumns und alles funktioniert so wie es soll.

Du hast nicht zufällig vergessen, die InsertColumns aus der
OnCreate wieder rauszunehmen?

von Frank (Gast)


Lesenswert?

die Spalten fügst du auch so ein

"m_pEmpfList.InsertColumn(1,"Code",LVCFMT_LEFT,60,-1);"

rufst du sonst irgendwo irgendwelche Funktionen für die ListCtrl auf?

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:
> die Spalten fügst du auch so ein
>
> "m_pEmpfList.InsertColumn(1,"Code",LVCFMT_LEFT,60,-1);"

Yep.

>
> rufst du sonst irgendwo irgendwelche Funktionen für die ListCtrl auf?

Nein.

Hier ist meine OnInitialUpdate
1
void Test::OnInitialUpdate() 
2
{
3
  CFormView::OnInitialUpdate();
4
5
  m_ctrlListe.InsertColumn( 0, "Test1",LVCFMT_LEFT,60,-1 );  
6
  m_ctrlListe.InsertColumn( 1, "Test2" );  
7
}

Du hast doch wohl nicht den Aufruf der OnInitialUpdate der
Basisklasse rausgeschmissen? Der muss vor deinen eigenen
Aufrufen passieren! Erst dort werden die Control Variablen
mit den GUI Elementen verknüpft.

von Frank (Gast)


Lesenswert?

Hab gerade ein neues Projekt gemacht und es dort ausprobiert, dort tut 
alles ohne Probleme. Keine Ahnung was ich an dem anderen Projekt 
verbockt hab. Aber da das andere noch nicht wirklich weit war mach ich 
nun das neue weiter und übertrag kurz die bisher gemachten Änderungen. 
Dürfte schneller gehn als ne Fehlersuche.

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:
> Hab gerade ein neues Projekt gemacht und es dort ausprobiert, dort tut
> alles ohne Probleme. Keine Ahnung was ich an dem anderen Projekt
> verbockt hab.


Könnte irgendein Problem mit dem im Resource-Editor vergebenen
ID sein. Sowas kommt schon mal vor.

> Aber da das andere noch nicht wirklich weit war mach ich
> nun das neue weiter und übertrag kurz die bisher gemachten Änderungen.
> Dürfte schneller gehn als ne Fehlersuche.

Da ist meist was drann :-)

von Frank (Gast)


Lesenswert?

Wenn wir schon dabei sind hätte ich noch 2 Sachen^^
Ich füge nun 4 Spalten in die ListCtrl ein, diese Spalten werden auch 
wunderbar eingefügt. Aber es ist noch eine 5. Spalte ohne Titel dabei, 
kann ich die rauswerfen?

Das 2. wäre die Größe der ListCtrl. Das Projekt läuft als MDI und die 
ListCtrl werden als Dokumente angezeigt. Wenn ich auf "Maximieren" in 
dem Dokument klicke, dann maximiert zwar das Dokument aber die ListCtrl 
behält ihre Größe bei. Ich hätte aber gerne das die Liste immer das 
komplette Dokument ausfüllt. Wofür ich eigentlcih eine dynamische 
Spaltengröße bräuchte oder?

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:
> Wenn wir schon dabei sind hätte ich noch 2 Sachen^^
> Ich füge nun 4 Spalten in die ListCtrl ein, diese Spalten werden auch
> wunderbar eingefügt. Aber es ist noch eine 5. Spalte ohne Titel dabei,
> kann ich die rauswerfen?

Das müsste dann der Restplatz des Controls, also der Teil ohne
Spalten sein. Den wirst du leider nicht los, aber du kannst
natürlich die Spalten entsprechend in die Breite ziehen, so dass
sie den kompletten Platz einnehmen.
Dazu dann noch das Speichern und Wiederherstellen der Spaltenbreite,
wie oben schon gezeigt, und dann sollte das kein Problem mehr sein.

Edit: hab erst später gelesen, dass du die Spalten per Programm
dynamisch größer und kleiner ziehen willst.

> Das 2. wäre die Größe der ListCtrl. Das Projekt läuft als MDI und die
> ListCtrl werden als Dokumente angezeigt. Wenn ich auf "Maximieren" in
> dem Dokument klicke, dann maximiert zwar das Dokument aber die ListCtrl
> behält ihre Größe bei.

Da musst du dich dann an die WM_SIZE Message hängen und
alle Controls auf der FormView resizen.
Aber Achtung: Die aus der WM_SIZE Message generierte Funktion
OnSize wird ganz am Anfang des Programms schon mal mit Größen
von 0 aufgerufen. So einen Aufruf einfach ignorieren
1
void TestView::OnSize(UINT nType, int cx, int cy) 
2
{
3
  CFormView::OnSize(nType, cx, cy);
4
5
  if( cx == 0 || cy == 0 )
6
    return;
7
  
8
  // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
9
}


> Ich hätte aber gerne das die Liste immer das
> komplette Dokument ausfüllt.

Vorsicht: Der Begriff "Dokument" wird in der MFC für etwas
anderes benutzt. Was du hast ist ein "View".

Dokument: zuständig für das Halten und Verwalten von Daten
View:     eine Anzeige der, im zugehörigen Dokument, gespeicherten Daten

Diese Aufgabentrennung ist wichtig! Wenn du dich nicht daran hältst,
kriegst du Probleme mit dem MDI. Unterschiedliche Views zeigen
dann dasselbe Dokument in unterschiedlichen Stadien an.

> Wofür ich eigentlcih eine dynamische
> Spaltengröße bräuchte oder?

Ja. SetColumnWidth ist dein Freund.

von Frank (Gast)


Lesenswert?

Meine Mum hatte doch recht als sie sagte, das ich komische Freunde hab 
^^

von Frank (Gast)


Lesenswert?

Also funktionieren tut soweit alles. Hab das jetzt mal mit dem folgenden 
Code gemacht.

void CEmpfangsverlauf::OnSize(UINT nType, int cx, int cy)
{
  CFormView::OnSize(nType, cx, cy);


  m_cList.MoveWindow(0,0,cx,cy,TRUE);
  m_cList.SetColumnWidth(3,LVSCW_AUTOSIZE_USEHEADER);
  // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
}

Funktionieren tuts, aber beim Kompilieren kommt wieder mal der "DEBUG 
ASSERTION FAILED" mit "Ignorieren" durchklicken und es tut. Aber der 
Fehler kommt ja sicher nicht ohne Grund. In den CListCtrl Funktionen wie 
MoveWindow usw wird ja die Zeile:

"ASSERT(::IsWindow(m_hWnd));"

Dabei hat m_hWnd den Wert 0x00000000

Also falls jmd das gleiche Problem schonmal hatte und nen Tipp zu einer 
Lösung parat hätte, wäre ich echt dankbar.

Achja Karl Heinz, das mit dem Aufruf von OnSize mit 0 als Größen war bei 
mir nicht so. Das wird gleich mit der richtigen Größe aufgerufen. Aber 
lieber zuviel Sicherheitsabfragen als zuwenig ;)

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:

> MoveWindow usw wird ja die Zeile:
>
> "ASSERT(::IsWindow(m_hWnd));"
>
> Dabei hat m_hWnd den Wert 0x00000000

Was bedeutet, dass das Control noch nicht mit dem GUI Element
verknüpft ist.

>
> Also falls jmd das gleiche Problem schonmal hatte und nen Tipp zu einer
> Lösung parat hätte, wäre ich echt dankbar.
>
> Achja Karl Heinz, das mit dem Aufruf von OnSize mit 0 als Größen war bei
> mir nicht so. Das wird gleich mit der richtigen Größe aufgerufen. Aber
> lieber zuviel Sicherheitsabfragen als zuwenig ;)

Hast du dir auch den 2-ten oder 3-ten Aufruf der OnSize() angeschaut? 
:-)

Zu deinem Problem.
Die OnSize wird bereits aufgerufen noch bevor die Controls erstellt
wurden.
Du kannst das ganz leicht abfangen, die Assertion sagt dir sogar
wie. Solange m_hWnd noch keinen gültigen Wert hat, darfst du
die MoveWindow nicht aufrufen.
Ergo:
1
void CEmpfangsverlauf::OnSize(UINT nType, int cx, int cy)
2
{
3
  CFormView::OnSize(nType, cx, cy);
4
5
  if( ::IsWindow( m_cList.m_hWnd ) ) {
6
    m_cList.MoveWindow(0,0,cx,cy,TRUE);
7
    m_cList.SetColumnWidth(3,LVSCW_AUTOSIZE_USEHEADER);
8
  }
9
}

von Frank (Gast)


Lesenswert?

Super funktioniert wunderbar.
Hast du zufällig die Funktion im Kopf um die Größe des Views abzufragen?
Das würde ich noch für die Initialisierung benötigen, da die Größe der 
Liste erst nach einer Größenveränderung des Views umgestellt wird.
Lässt man das View bei seiner normalen Größe, so kann es sein das die 
Liste nicht das komplette View ausfüllt.

von Karl H. (kbuchegg)


Lesenswert?

PS: Wenn du sowieso nur ein List Control im View hast,
warum machst du das dann so kompliziert?
Tausch den CFormView gegen einen CListView aus und fertig.

* Mit dem Wizard eine neue View Klasse erzeugen
  Diese von CListView ableiten

* In der Applikation bei der Erzeugung des DocumentTemplates
  die neue Klasse anstelle der FormView angeben
  (Header File inkludieren nicht vergessen)

* In der neuen Klasse die PreCreateWindow überschreiben, damit
  das List Control auch im Report Modus erzeugt wird
1
  BOOL CMyList::PreCreateWindow(CREATESTRUCT& cs) 
2
  {
3
    cs.style |= LVS_REPORT;
4
    return CListView::PreCreateWindow(cs);
5
  }

* In der OnInitialUpdate die Spalten wie gehabt erzeugen. Mittels
  GetListCtrl() kommt man an das zugrunde liegende List Control
  heran:
1
  void CMyList::OnInitialUpdate() 
2
  {
3
    CListView::OnInitialUpdate();
4
    GetListCtrl().InsertColumn( 0, "Test" );
5
  }

Um Größenänderungen oder dgl. brauchst du dich dann nicht selbst
kümmern. Erledigt alles der CListView

von Karl H. (kbuchegg)


Lesenswert?

Frank wrote:
> Super funktioniert wunderbar.
> Hast du zufällig die Funktion im Kopf um die Größe des Views abzufragen?

Gibt es nicht.
Aber nichts und niemand hindert dich daran, in deinen View 2
Variablen einzubauen, die du im OnSize mit Werten versorgst.

Edit: OK. Über GetWindowRect könnte man das machen. Die Variante
über Speichern der Größe im OnSize ist aber simpler.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.