Links Titel
Pixel
     
Steuerelemente - Listenelement


Hier stehen Informationen zum Umgang mit dem Listenelement (ListControl). Die zugehörige Klasse lautet: CListCtrl. Das Listenelement ist ein sehr vielseitiges Steuerlement und wird häufig verwendet. Man kann in einem Listenfeld z.B Icons mit Text oder auch eine tabellenartige Auflistung anzeigen.

Die verschiedenen Anzeigemöglichkeiten lassen sich in den Eigenschaften des Listenelements einstellen. Der passende Menüpunkt heisst Ansicht (View). In Visual C++ 6.0 findet man ihn im Register Formate.


Übersicht

Listenelement in Berichts-Ansicht:

  - Ausgewählte Zeile ermitteln - GetNextItem()
  - Beispiel mit Listenelement in Berichts-Ansicht
  - Daten in ein Listenelement einfügen - InsertItem()/SetItemText()
  - Einträge ausrichten
  - Laden und Speichern der Daten
  - Ganze Zeile markieren - SetExtendedStyle()
  - Gitternetzlinien einfügen - SetExtendedStyle()
  - Spaltenkopf beschriften - InsertColumn()
  - Zeilenanzahl ermitteln - GetItemCount()
  - Zeilen löschen - DeleteItem()/DeleteAllItems()

 

Ausgewählte Zeile ermitteln - GetNextItem()

Eine vom Anwender markierte Zeile kann man mit der Funktion GetNextItem() ermitteln. Der Code dazu sieht so aus:

  // Ausgewählte Zeile ermitteln
  int nZeilenauswahl;
  nZeilenauswahl = m_List1.GetNextItem(-1, LVNI_SELECTED);


In der Variablen nZeilenauswahl steht nun die vom Anwender markierte Zeile. Möchte man diese Zeile löschen, kann man die Funktion DeleteItem() verwenden:

    // Ausgewählte Zeile löschen
    m_List1.DeleteItem(nZeilenauswahl);


m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Beispiel mit Listenelement in Berichts-Ansicht

Ein Listenelement welches in der Berichts-Ansicht dargestellt wird, wird oft verwendet. Man bekommt eine geordnete, tabellenartige Darstellung der Inhalte.

Beispiel:
Im folgenden Beispiel wird gezeigt, wie man ein Listenelement in Berichts-Ansicht erstellt und es mit Daten füllt. Wir werden etwas mit dem Ergebnis spielen und verschiedene Styles einbauen. Ausserdem werden wir die Daten am Ende auch noch speichern, laden und drucken können.
  • Erstelle eine Dialogfeldbasierte Anwendung mit dem Namen: LElement_Bericht und entferne alle vorhandenen Elemente. Schau dir die unten stehenden Abbildung an, um zu sehen wie die Steuerelemente angeordnet werden
  • Füge ein Listenelement ein, vergrössere es etwas und stelle in den Eigenschaften den Menüpunkt Ansicht (View) auf Bericht (Report) um. Aktiviere ausserdem noch Einzelauswahl (Single Selection). In VC++ 6.0 findet man beides im Register Formate
  • Füge eine Member-Variable für das Listenelement ein - Kategorie: Control, Variablentyp: CListCtrl, Name: m_List1
  • Als nächstes fügen wir erstmal die Bezeichnungen für den Spaltenkopf ein - wir initialisieren das Listenelement. Danach fügen wir gleich ein paar Daten in das Listenelement ein. Füge dazu folgenden Code in die Funktion OnInitDialog() ein:
  BOOL CLControl_ReportDlg::OnInitDialog()
  {
    ...
    // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
    // Listenelement initialisieren - Spaltenkopf einfügen
    m_List1.InsertColumn(0, "Produkt" ,LVCFMT_LEFT, 100);
    m_List1.InsertColumn(1, "Hersteller" ,LVCFMT_LEFT, 70);
    m_List1.InsertColumn(2, "Preis" ,LVCFMT_LEFT, 50);
    m_List1.InsertColumn(3, "Verfügbar" ,LVCFMT_LEFT, 70);

    // Elemente einfügen
    m_List1.InsertItem(0, "Druckerpatrone");
    m_List1.SetItemText(0, 1, "ASE");
    m_List1.SetItemText(0, 2, "19,95");
    m_List1.SetItemText(0, 3, "Ja");

    m_List1.InsertItem(1, "CD-ROM");
    m_List1.SetItemText(1, 1, "ASE");
    m_List1.SetItemText(1, 2, "14,95");
    m_List1.SetItemText(1, 3, "Ja");

    m_List1.InsertItem(2, "Scanner");
    m_List1.SetItemText(2, 1, "ASE");
    m_List1.SetItemText(2, 2, "84,95");
    m_List1.SetItemText(2, 3, "Nein");

    return TRUE;
  }


Listenelement

Beschreibung:
Mit Hilfe der Funktion InsertColumn() haben wir den Spaltenkopf beschriftet. Der erste Parameter steht für die jeweilige Spalte, der zweite Parameter für die anzuzeigende Beschriftung, der dritte Parameter für die Ausrichtung des anzuzeigenden Textes und der letzte Parameter gibt die Spaltenbreite in Pixel an. Der dritte Parameter, welcher für die Ausrichtung des Textes steht, kann folgende Werte annehmen: LVCFMT_LEFT, LVCFMT_RIGHT oder LVCFMT_CENTER.

Danach haben wir Zeilen mit Hilfe der Funktionen InsertItem() und SetItemText() in das Listenelement eingefügt. Jede neue Zeile wird mit der Funktion InsertItem() begonnen. Der erste Parameter steht für die Zeilenzahl und der zweite Parameter für den anzuzeigenden Text. Jedes weitere Element in der Zeile wird mit der Funktion SetItemText() eingefügt. Dabei steht der erste Parameter für die Zeile in die etwas eingefügt werden soll. Der zweite Parameter steht für die Spalte in die geschrieben werden soll und der dritte Parameter steht für den anzuzeigenden Text.


Erweiterung des Beispiels - Zentrieren der Einträge
Wie oben erwähnt ist es möglich die Spalten und die zugehörigen Einträge zu zentrieren. Dies wird durch den dritten Parameter der Funktion InsertColumn() beeinflusst. Man hat die Auswahl zwischen: LVCFMT_LEFT, LVCFMT_RIGHT oder LVCFMT_CENTER. Das probieren wir gleich mal aus.
  • Ändere dazu die Zeilen, die den Text in den Spaltenkopf eintragen:
  // Listenelement initialisieren - Spaltenkopf einfügen
  m_List1.InsertColumn(0, "Produkt" , LVCFMT_CENTER, 100);
  m_List1.InsertColumn(1, "Hersteller" , LVCFMT_CENTER, 70);
  m_List1.InsertColumn(2, "Preis" , LVCFMT_CENTER, 50);
  m_List1.InsertColumn(3, "Verfügbar" , LVCFMT_CENTER, 70);


Listenelement

Beschreibung:
Wir haben jetzt also den dritten Parameter von LVCFMT_RIGHT in LVCFMT_CENTER geändert. Man sieht schön wie die Spalten und die Einträge jetzt zentriert angezeigt werden. Allerdings gibt es eine Ausnahme - die erste Spalte wird leider nicht zentriert dargestellt.


Erweiterung des Beispiels: Gitternetzlinien einfügen
Mit nur einer Zeile kann man das Aussehen des Listenelements stark beeinflussen. Mit der Zeile:

  m_List1.SetExtendedStyle(LVS_EX_GRIDLINES);

kann man Gitternetzlinien in das Listenelement einfügen.
  • Um Gitternetzlinien im Listenelement anzuzeigen, ändere die Funktion OnInitDialog() etwas ab:
  BOOL CLControl_ReportDlg::OnInitDialog()
  {
    ...
    // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
    // Gitternetzlinien einfügen
    m_List1.SetExtendedStyle(LVS_EX_GRIDLINES);

    // Listenelement initialisieren - Spaltenkopf einfügen
    m_List1.InsertColumn(0, "Produkt" , LVCFMT_CENTER, 100);
    m_List1.InsertColumn(1, "Hersteller" , LVCFMT_CENTER, 70);
    m_List1.InsertColumn(2, "Preis" , LVCFMT_CENTER, 50);
    m_List1.InsertColumn(3, "Verfügbar" , LVCFMT_CENTER, 70);

    // Elemente einfügen
    m_List1.InsertItem(0, "Druckerpatrone");
    m_List1.SetItemText(0, 1, "ASE");
    m_List1.SetItemText(0, 2, "19,95");
    m_List1.SetItemText(0, 3, "Ja");

    m_List1.InsertItem(1, "CD-ROM");
    m_List1.SetItemText(1, 1, "ASE");
    m_List1.SetItemText(1, 2, "14,95");
    m_List1.SetItemText(1, 3, "Ja");

    m_List1.InsertItem(2, "Scanner");
    m_List1.SetItemText(2, 1, "ASE");
    m_List1.SetItemText(2, 2, "84,95");
    m_List1.SetItemText(2, 3, "Nein");
    return TRUE;
  }


Listenelement



Erweiterung des Beispiels: Ganze Zeile markieren
Das einzigste was sich standardmäßig in einem Listenelement anklicken lässt ist jeweils der Eintrag in der ersten Spalte. Auch die Markierung steht immer nur auf der ersten Spalte:

Listenelement

Mit der Zeile

  m_List1.SetExtendedStyle(LVS_EX_FULLROWSELECT);

lässt sich das leicht ändern. Fügt man diese Zeile ein, kann man die ganze Zeile in einem Listenelement markieren. Da uns die Gitternetzlinien erhalten bleiben sollen, kombinieren wir die Styles.
  • Ändere dazu die Zeile zum einfügen der Gitternetzlinien etwas ab:
  // Gitternetzlinien einfügen und ganze Zeile markieren
  m_List1.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);


Listenelement



Ausbau des Beispiels: Daten eingeben - Zeilen löschen
Um das Beispiel kompletter werden zu lassen, fügen wir jetzt eine Möglichkeit hinzu während des Programmlaufs Daten einzufügen. Ebenfalls soll es möglich sein eine markierte Zeile per Button löschen zu können.
  • Um dies zu realisieren fügen wir ein paar Textfelder, Eingabefelder und zwei Optionsfelder ein. Schau dir die untenstehende Abbildung an, um zu sehen wie die Steuerelemente angeordnet werden
  • Füge ein Textfeld ein und beschrifte es mit: Produkt. Füge daneben ein Eingabefeld ein und ändere die ID in: IDC_PRODUKT
  • Füge ein Textfeld ein und beschrifte es mit: Hersteller. Füge daneben ein Eingabefeld ein und ändere die ID in: IDC_HERSTELLER
  • Füge ein Textfeld ein und beschrifte es mit: Preis. Füge daneben zwei Eingabefelder ein und ändere die IDs in: IDC_EURO und IDC_CENT
  • Füge ein Textfeld ein und beschrifte es mit: Verfügbar. Füge daneben zwei Optionsfelder ein, beschrifte sie mit: Ja und Nein und ändere die IDs in: IDC_JA und IDC_NEIN. Gehe in die Eigenschaften des ersten Optionfelds, also das mit der ID: IDC_JA und aktiviere die Eigenschaft: Gruppe (Group). Terminiere die Gruppe, indem du für das Steuerelement das in der Tabulator-Reihenfolge auf das Optionsfeld "Nein" folgt ebenfalls Gruppe aktivierst. Bei mir ist dies das Listenelement. Solltest du hiermit Probleme haben, kannst du alles wichtige hier: Beispiel mit Optionsfelder nachlesen
  • Füge jetzt für die eben eingefügten Steuerelemente Member-Variablen nach folgender Tabelle ein:
Steuerelement Variablenname Kategorie Variablentyp
Produkt (IDC_PRODUKT) m_strProdukt Wert CString
Hersteller (IDC_HERSTELLER) m_strHersteller Wert CString
Preis (IDC_EURO) m_nEuro Wert int
Preis (IDC_CENT) m_nCent Wert int
Optionsfeld - Ja (IDC_JA) m_nVerfuegbar Wert int
  • Füge jetzt noch zwei Buttons ein und ändere die Beschriftung in: Daten einfügen und Zeile löschen. Ändere die IDs in: IDC_EINFUEGEN und IDC_LOESCHEN
  • Klicke doppelt auf jeden Button, um für jeden eine Nachrichtenbehandlungsroutine für die BN_CLICKED-Nachricht einzufügen und füge folgenden Code in die eben erstellten Funktionen ein:
  void CLElement_BerichtDlg::OnEinfuegen()
  {
    // Daten aus Eingabemaske holen (Variablen aktualisieren)
    UpdateData(TRUE);

    // Fehler in Eingabemaske abfangen
    if(m_strProdukt == "")
    {
      MessageBox("Kein Produkt eingetragen!", "Fehler!", MB_ICONERROR);
      return;
    }
    if(m_strHersteller =="")
    {
      MessageBox("Kein Hersteller eingetragen!", "Fehler!", MB_ICONERROR);
      return;
    }
    if(m_nEuro <=0)
    {
      MessageBox("Der eingegeben Betrag ist zu gering!", "Fehler!", MB_ICONERROR);
      return;
    }

    // Auswertung der Optionsfelder
    CString str_Verfuegbar;
    if(m_nVerfuegbar == 0)
      str_Verfuegbar = "Ja";
    else
      str_Verfuegbar = "Nein";

    // int in String wandeln - nötig für den Preis
    CString str_Preis;
    str_Preis.Format("%d,%d", m_nEuro, m_nCent);

    // Ermitteln wieviele Zeilen das Listenelement hat
    int nZeilenanzahl;
    nZeilenanzahl = m_List1.GetItemCount();

    // Zeile in Listenelement einfügen
    m_List1.InsertItem(nZeilenanzahl, m_strProdukt);
    m_List1.SetItemText(nZeilenanzahl, 1, m_strHersteller);
    m_List1.SetItemText(nZeilenanzahl, 2, str_Preis);
    m_List1.SetItemText(nZeilenanzahl, 3, str_Verfuegbar);
  }

  void CLElement_BerichtDlg::OnLoeschen()
  {
    // Ausgewählte Zeile ermitteln
    int n_Zeilenauswahl;
    n_Zeilenauswahl = m_List1.GetNextItem(-1, LVNI_SELECTED);

    // Ausgewählte Zeile löschen
    m_List1.DeleteItem(n_Zeilenauswahl);
  }
  • Jetzt müssen wir nur noch die Variable des Optionfelds initialisieren, damit beim Programmstart direkt schon ein Optionsfeld ausgewählt ist. Wir müssen dazu nur eine Zeile Code in die Funktion OnInitDialog() einfügen. Zur Vollständigkeit folgt nochmal unser kompletter Code incl. die Initialisierung des Optionsfelds:
  BOOL CLControl_ReportDlg::OnInitDialog()
  {
    ...
    // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
    // Radio Button initialisieren
    m_nVerfuegbar = 0;
    UpdateData(FALSE);

    // Gitternetzlinien einfügen und ganze Zeile markieren
    m_List1.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);

    // Listenelement initialisieren - Spaltenkopf einfügen
    m_List1.InsertColumn(0, "Produkt" , LVCFMT_CENTER, 100);
    m_List1.InsertColumn(1, "Hersteller" , LVCFMT_CENTER, 70);
    m_List1.InsertColumn(2, "Preis" , LVCFMT_CENTER, 50);
    m_List1.InsertColumn(3, "Verfügbar" , LVCFMT_CENTER, 70);

    // Elemente einfügen
    m_List1.InsertItem(0, "Druckerpatrone");
    m_List1.SetItemText(0, 1, "ASE");
    m_List1.SetItemText(0, 2, "19,95");
    m_List1.SetItemText(0, 3, "Ja");

    m_List1.InsertItem(1, "CD-ROM");
    m_List1.SetItemText(1, 1, "ASE");
    m_List1.SetItemText(1, 2, "14,95");
    m_List1.SetItemText(1, 3, "Ja");

    m_List1.InsertItem(2, "Scanner");
    m_List1.SetItemText(2, 1, "ASE");
    m_List1.SetItemText(2, 2, "84,95");
    m_List1.SetItemText(2, 3, "Nein");
    return TRUE;
  }


Listenelement

Beschreibung
Das Beispiel beinhaltet jetzt eine Eingabemöglichkeit, um auch Elemente in das Listenelement während des Programmlaufs einzufügen und auch wieder zu löschen. Optimal wäre hier noch eine Abfrage, bevor eine Zeile gelöscht wird.


Erweiterung des Beispiels - Laden und Speichern der Daten
Jetzt bauen wir noch eine Möglichkeit ein alle Artikel des Listenelements zu speichern und wieder zu laden.
  • Füge dazu zwei weitere Buttons ein und ändere die Beschriftung in: Laden und Speichern. Ändere die IDs in: IDC_LADEN und IDC_SICHERN
  • Klicke doppelt auf jeden Button, um für jeden eine Nachrichtenbehandlungsroutine für die BN_CLICKED-Nachricht einzufügen und füge folgenden Code in die eben erstellten Funktionen ein:
  void CLElement_BerichtDlg::OnLaden()
  {
    CString strLesePuffer, strDateiName, strPfad;
    CStdioFile DateiLesen;
    int i=0;

    // Holt Pfad und Name mit .exe
    strPfad = GetCommandLine();
    // Dateierweiterung abschneiden
    int nPos = strPfad.ReverseFind( '\\');
    strPfad = strPfad.Mid(1, nPos-1);

    // Ermittelten Pfad + Dateiname in strDateiName schreiben
    strDateiName = strPfad +"//text.txt";

    // Kompletten Inhalt des Listenelements löschen
    m_List1.DeleteAllItems();

    // Datei auslesen
    if (DateiLesen.Open(strDateiName, CFile::modeRead))
    {
      while(DateiLesen.ReadString(strLesePuffer))
      {
        // Datei zeilenweise auslesen
        m_List1.InsertItem(i, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 1, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 2, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 3, strLesePuffer);
        i++;
      }
    }
    else
       MessageBox ("Fehler beim Laden der Datei", "Fehler", MB_ICONINFORMATION);
  }

  void CLElement_BerichtDlg::OnSichern()
  {
    CString m_strPreis, m_strVerfuegbar, strDateiName, strPfad;
    CStdioFile DateiSpeichern;

    int i, nZeilenanzahl;

    // Holt Pfad und Name mit .exe
    strPfad = GetCommandLine();
    // Dateierweiterung abschneiden
    int nPos = strPfad.ReverseFind( '\\');
    strPfad = strPfad.Mid(1, nPos-1);

    // Ermittelten Pfad + Dateiname in strDateiName schreiben
    strDateiName = strPfad +"//text.txt";

    // Daten in Datei sichern
    if (DateiSpeichern.Open(strDateiName, CFile::modeCreate | CFile::modeWrite ))
    {
      // Anzahl der Zeilen im Listenfeld holen
      nZeilenanzahl = m_List1.GetItemCount();

      for(i=0; i < nZeilenanzahl; i++)
      {
        m_strProdukt = m_List1.GetItemText(i, 0);
        m_strHersteller = m_List1.GetItemText(i, 1);
        m_strPreis = m_List1.GetItemText(i, 2);
        m_strVerfuegbar = m_List1.GetItemText(i, 3);
        DateiSpeichern.WriteString (m_strProdukt+"\n" + m_strHersteller+"\n" + m_strPreis+"\n" + m_strVerfuegbar +"\n" ) ;
      }

      DateiSpeichern.Close ();
      MessageBox ("Daten wurden gespeichert", "Daten gespeichert", MB_ICONINFORMATION);
    }
    else
       MessageBox ("Fehler beim Öffnen der Datei - Speichern wurde abgebrochen", "Fehler", MB_ICONINFORMATION);
 }


Listenelement

Beschreibung
Jetzt ist es auch möglich eingebene Daten zu speichern und wieder herzustellen. In der Funktion OnLaden() wird zuerst der Pfad der Anwendung ermittelt und die Dateierweiterung abgetrennt. Danach wird mit Hilfe der Funktion DeleteAllItems() der komplette Inhalt des Listenelements gelöscht. Falls bereits eine Datei mit Daten vorhanden ist wird diese geöffnet und zeilenweise in das Listenelement eingelesen.

In der Funktion OnSichern() wird wieder zuerst der Pfad der Anwendung ermittelt und die Dateierweiterung abgetrennt. Dann wird eine Textdatei erstellt und zeilenweise in diese Textdatei geschrieben.


Erweiterung des Beispiels - Drucken der Daten
Was jetzt noch dringend fehlt ist eine Möglichkeit die eingegeben Daten auszudrucken.
  • Füge dazu einen weiteren Button ein, ändere die Beschriftung in: Drucken und die ID in: IDC_DRUCKEN
  • Klicke doppelt auf den Button, um eine Nachrichtenbehandlungsroutine für die BN_CLICKED-Nachricht einzufügen und füge folgenden Code in die eben erstellte Funktion ein:
  void CLElement_BerichtDlg::OnDrucken()
  {
    CPrintDialog dlgPrint(FALSE, PD_ALLPAGES, this);

    // GetDefaults liefert den Standarddrucker
    dlgPrint.GetDefaults();

    // Verbinden des Drucker-DC mit einem CDC-Objekt
    CDC dcPrint;
    dcPrint.Attach(dlgPrint.GetPrinterDC());

    // DOCINFO-Struktur erzeugen (wird für Druck benötigt)
    DOCINFO ListControlPrint;
    ListControlPrint.cbSize = sizeof(ListControlPrint);
    ListControlPrint.lpszDocName = "Listenelement Druck";
    ListControlPrint.lpszOutput = NULL;
    ListControlPrint.lpszDatatype = NULL;
    ListControlPrint.fwType = NULL;

    //Maße des druckbaren Bereiches des Papiers ermitteln (in 0,1mm)
    dcPrint.SetMapMode(MM_LOMETRIC);
    int nLaenge = dcPrint.GetDeviceCaps(VERTSIZE);
    int nBreite = dcPrint.GetDeviceCaps(HORZSIZE);
    int nRechts = nBreite * 10 - 40;
    int nUnten = -nLaenge * 10 + 40;

    // Rahmen für Druck festlegen
    dcPrint.Rectangle(50, 0, nRechts, nUnten);

    // Schriftart und Grösse für die Überschrift festlegen
    CFont font, *p_OldFont;
    font.CreateFont(-100, 0, 0, 0, 600, 0, 0, 0, ANSI_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Verdana");
    p_OldFont= dcPrint.SelectObject(&font);

    // Ausdruck des Dokuments starten
    if (dcPrint.StartDoc(&ListControlPrint)>=0)
    {
      // Eine Seite beginnen
      dcPrint.StartPage();

      // Mit grosser Schrift Überschrift ausgeben
      dcPrint.TextOut(450, -100, " Listenelement Druck");

      // Standard Font für den Rest benutzen
      dcPrint.SelectObject(p_OldFont);

      // Obere Linie zeichnen (von links nach rechts)
      dcPrint.MoveTo(50, -500);
      dcPrint.LineTo(nRechts, -500);

      // Header ausgeben
      dcPrint.TextOut(200,-530, "Produkt");
      dcPrint.TextOut(800,-530, "Hersteller");
      dcPrint.TextOut(1350,-530, "Preis");
      dcPrint.TextOut(1680,-530, "Verfügbar?");

      // Linie unter dem Header zeichnen (von links nach rechts)
      dcPrint.MoveTo(50, -600);
      dcPrint.LineTo(nRechts, -600);

      // Zweite Linie unter dem Header zeichnen (von links nach rechts)
      dcPrint.MoveTo(50, -605);
      dcPrint.LineTo(nRechts, -605);

      // Linie ganz unten zeichnen (von links nach rechts)
      dcPrint.MoveTo(50, nUnten+450);
      dcPrint.LineTo(nRechts, nUnten+450);

      // Ab hier kommen die vertikalen Linien (von oben nach unten)
      // Line ganz nRechts zeichnen (von oben nach unten)
      dcPrint.MoveTo(nRechts, -500);
      dcPrint.LineTo(nRechts, nUnten+450);

      // Zweite Linie von rechts zeichnen, also zwischen Preis und Verfügbar
      dcPrint.MoveTo(1600, -500);
      dcPrint.LineTo(1600, nUnten+450);

      // nächste Linie zeichnen, also Linie zwischen Hersteller und Preis
      dcPrint.MoveTo(1180, -500);
      dcPrint.LineTo(1180, nUnten+450);

      // nächste Linie, also zwischen Produkt und Hersteller
      dcPrint.MoveTo(630, -500);
      dcPrint.LineTo(630, nUnten+450);

      // Ganz linke Linie zeichnen
      dcPrint.MoveTo(50, -500);
      dcPrint.LineTo(50, nUnten+450);

      //Zeit und Datum ausgeben
      CTime time = CTime::GetCurrentTime();
      CString date = time.Format("Datum: %d.%m.%Y ");
      CString str_Time = time.Format("Uhrzeit: %H:%M");
      dcPrint.TextOut(50, -350, date);
      dcPrint.TextOut(50, -400, str_Time);

      // Ausgabe der Elemente des Listenelements
      CString str, m_strPreis, m_strVerfuegbar;

      // Seitenzahl ausgeben
      int nSeite= 1;
      str.Format("Seite %d ",nSeite);
      dcPrint.TextOut(1000, nUnten+250, str);

      // Ab hier wird der Ausdruck der Artikel vorbereitet
      // Ermitteln der Zeilen des Listenelement
      int nAnzahl = m_List1.GetItemCount();
      int nLaufendeNr = 0;

      for(int i=0; i < nAnzahl; i++)
      {
        nLaufendeNr++;

        // Ausruckbare Artikel auf 20 begrenzen
        if(i >= 20)
        {
           MessageBox("Es werden nur die ersten 20 Artikel ausgedruckt!", "Info!", MB_ICONEXCLAMATION);
           break;
        }

        // Einträge aus Listenelement lesen
        m_strProdukt = m_List1.GetItemText(i, 0);
        m_strHersteller = m_List1.GetItemText(i, 1);
        m_strPreis = m_List1.GetItemText(i, 2);
        m_strVerfuegbar = m_List1.GetItemText(i, 3);

        // und ausgeben
        dcPrint.TextOut(80, -550-nLaufendeNr*80, m_strProdukt);
        dcPrint.TextOut(650, -550-nLaufendeNr*80, m_strHersteller);
        dcPrint.TextOut(1200, -550-nLaufendeNr*80, m_strPreis);
        dcPrint.TextOut(1630, -550-nLaufendeNr*80, m_strVerfuegbar);
      }

      // Seite auswerfen
      dcPrint.EndPage();

      // Dokument schließen
      dcPrint.EndDoc();

      // Druckergerätekontext löschen
      dcPrint.DeleteDC();

      MessageBox("Daten wurden erfolgreich gedruckt", "Info", MB_ICONEXCLAMATION);
    }
    else
       MessageBox("Fehler! Dokument konnte nicht gedruckt werden", "Fehler!", MB_ICONEXCLAMATION);
  }


Listenelement

Beschreibung
Jetzt funktioniert auch der Ausdruck der Daten. Ich denke der Code ist ausreichend kommentiert. Der Ausdruck ist auf 20 Artikel begrenzt, d.h. es kann nur eine Seite gedruckt werden. Was ist wenn mehrere Seiten gedruckt werden sollen? Ganz einfach - füge einfach diese if-Anweisung ans Ende der for-Schleife ein:

  if(nLaufendeNr == 20)
  {
    // Laufende Nr auf 0 setzen, damit die nächste Seite wieder von oben gedruckt wird
    nLaufendeNr = 0;

    // Gedruckte Seite auswerfen
    dcPrint.EndPage();

    //Ab hier wiederholt sich der Code zum drucken, also:
    // Eine Seite beginnen
    dcPrint.StartPage();
    .....
    // Bis zu dieser Zeile:
    // Ab hier wird der Ausdruck der Artikel vorbereitet


Es wiederholt sich in der if-Anweisung also lediglich der Code zum drucken. Idealerweise packt man diesen Code selbstverständlich in eine Funktion, da der gleiche Code ja mehrmals aufgerufen wird. Nicht vergessen dürfen wir diesen Teil der sich in der for-Schleife befindet:

  // Ausruckbare Artikel auf 20 begrenzen
  if(i >= 20)
  {
    MessageBox("Es werden nur die ersten 20 Artikel ausgedruckt!", "Info!", MB_ICONEXCLAMATION);
    break;
  }


Dieser Code-Teil muss komplett entfernt oder auskommentiert werden, da wir sonst die Begrenzung auf 20 Artikel noch haben und nur die Tabelle ohne Inhalt ausgedruckt wird. Somit funktioniert auch der mehrseitige Druck. Wem dies noch nicht so ganz klar ist, findet den vollständigen Code mit Beschreibung im Projekt zu diesem Beispiel. Den Download gibts ein Stück weiter unten.

Um jetzt noch einen Druckdialog aufzurufen, damit ein Drucker ausgewählt werden kann, muss der gesamte Druckcode (ausser die erste Zeile) in einen if-Zweig gepackt werden:

  CPrintDialog dlgPrint(FALSE, PD_ALLPAGES, this);

  if(dlgPrint.DoModal()==IDOK)
  {
    ... // Kompletter Code zum drucken bis zum 'else'
  }



Downloads: Bericht_Beispiel.exe - Gesamtes Projekt

 

Daten in ein Listenelement einfügen - InsertItem()/SetItemText()

Um den Spaltenkopf zu beschriften verwendet man die Funktion InsertColumn(). Um nun Elemente in die einzelnen Zeilen einzufügen verwendet man für die erste Spalte die Funktion InsertItem() und für alle weiteren Spalten die Funktion SetItemText(). Um z.B ein Listelement beim Start der Anwendung zu initialisieren, also den Spaltenkopf beschriften und ein paar Elemente einzufügen, schreibt man den Code dazu am besten in die OnInitDialog() Funktion.
Hier als Beispiel eine Initialisierung eines Listenelements:

  BOOL CLControl_ReportDlg::OnInitDialog()
  {
    ...
    // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
    // Listenelement initialisieren - Spaltenkopf einfügen
    m_List1.InsertColumn(0, "Produkt" ,LVCFMT_LEFT, 100);
    m_List1.InsertColumn(1, "Hersteller" ,LVCFMT_LEFT, 70);
    m_List1.InsertColumn(2, "Preis" ,LVCFMT_LEFT, 50);
    m_List1.InsertColumn(3, "Verfügbar" ,LVCFMT_LEFT, 70);

    // Elemente einfügen
    m_List1.InsertItem(0, "Druckerpatrone");
    m_List1.SetItemText(0, 1, "ASE");
    m_List1.SetItemText(0, 2, "19,95");
    m_List1.SetItemText(0, 3, "Ja");

    m_List1.InsertItem(1, "CD-ROM");
    m_List1.SetItemText(1, 1, "ASE");
    m_List1.SetItemText(1, 2, "14,95");
    m_List1.SetItemText(1, 3, "Ja");

    m_List1.InsertItem(2, "Scanner");
    m_List1.SetItemText(2, 1, "ASE");
    m_List1.SetItemText(2, 2, "84,95");
    m_List1.SetItemText(2, 3, "Nein");

    return TRUE;
  }


Listenelement

Beschreibung:
Mit Hilfe der Funktion InsertColumn() haben wir den Spaltenkopf beschriftet. Der erste Parameter steht für die jeweilige Spalte, der zweite Parameter für die anzuzeigende Beschriftung, der dritte Parameter für die Ausrichtung des anzuzeigenden Textes und der letzte Parameter gibt die Spaltenbreite in Pixel an. Der dritte Parameter, welcher für die Ausrichtung des Textes steht, kann folgende Werte annehmen: LVCFMT_LEFT, LVCFMT_RIGHT oder LVCFMT_CENTER.

Danach haben wir Zeilen mit Hilfe der Funktionen InsertItem() und SetItemText() in das Listenelement eingefügt. Jede neue Zeile wird mit der Funktion InsertItem() begonnen. Der erste Parameter steht für die Zeilenzahl und der zweite Parameter für den anzuzeigenden Text. Jedes weitere Element in der Zeile wird mit der Funktion SetItemText() eingefügt. Dabei steht der erste Parameter für die Zeile in die etwas eingefügt werden soll. Der zweite Parameter steht für die Spalte in die geschrieben werden soll und der dritte Parameter steht für den anzuzeigenden Text.

m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Einträge ausrichten

Im Beispiel Spaltenkopf beschriften - InsertColumn() steht das der dritte Parameter der Funktion InsertColumn() für die Ausrichtung des Textes zuständig ist. Er kann kann folgende Werte annehmen: LVCFMT_LEFT, LVCFMT_RIGHT oder LVCFMT_CENTER. Die Elemente könnten also folgendermaßen z.B. zentriert werden:

  // Listenelement initialisieren - Spaltenkopf einfügen
  m_List1.InsertColumn(0, "Produkt" , LVCFMT_CENTER, 100);
  m_List1.InsertColumn(1, "Hersteller" , LVCFMT_CENTER, 70);
  m_List1.InsertColumn(2, "Preis" , LVCFMT_CENTER, 50);
  m_List1.InsertColumn(3, "Verfügbar" , LVCFMT_CENTER, 70);


Listenelement

Beschreibung:
Mit Hilfe des dritten Parameters der Funktion InsertColumn() wird also die Ausrichtung des Textes bestimmt. Man sieht schön wie die Spalten und die Einträge jetzt zentriert angezeigt werden. Allerdings gibt es eine Ausnahme - die erste Spalte wird leider nicht zentriert dargestellt.

m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Laden und Speichern

Selbstverständlich will man auch mal vom Anwender eingegebene Daten speichern bzw. wiederherstellen. Das geht ganz leicht und als Code Beispiel kommt der Code aus dem Beispiel mit Listenelement in Berichts-Ansicht zum Einsatz:

  void CLElement_BerichtDlg::OnLaden()
  {
    CString strLesePuffer, strDateiName, strPfad;
    CStdioFile DateiLesen;
    int i=0;

    // Holt Pfad und Name mit .exe
    strPfad = GetCommandLine();
    // Dateierweiterung abschneiden
    int nPos = strPfad.ReverseFind( '\\');
    strPfad = strPfad.Mid(1, nPos-1);

    // Ermittelten Pfad + Dateiname in strDateiName schreiben
    strDateiName = strPfad +"//text.txt";

    // Kompletten Inhalt des Listenelements löschen
    m_List1.DeleteAllItems();

    // Datei auslesen
    if (DateiLesen.Open(strDateiName, CFile::modeRead))
    {
      while(DateiLesen.ReadString(strLesePuffer))
      {
        // Datei zeilenweise auslesen
        m_List1.InsertItem(i, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 1, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 2, strLesePuffer);
        DateiLesen.ReadString(strLesePuffer);
        m_List1.SetItemText(i, 3, strLesePuffer);
        i++;
      }
    }
    else
       MessageBox ("Fehler beim Laden der Datei", "Fehler", MB_ICONINFORMATION);
  }

  void CLElement_BerichtDlg::OnSichern()
  {
    CString m_strPreis, m_strVerfuegbar, strDateiName, strPfad;
    CStdioFile DateiSpeichern;

    int i, nZeilenanzahl;

    // Holt Pfad und Name mit .exe
    strPfad = GetCommandLine();
    // Dateierweiterung abschneiden
    int nPos = strPfad.ReverseFind( '\\');
    strPfad = strPfad.Mid(1, nPos-1);

    // Ermittelten Pfad + Dateiname in strDateiName schreiben
    strDateiName = strPfad +"//text.txt";

    // Daten in Datei sichern
    if (DateiSpeichern.Open(strDateiName, CFile::modeCreate | CFile::modeWrite ))
    {
      // Anzahl der Zeilen im Listenfeld holen
      nZeilenanzahl = m_List1.GetItemCount();

      for(i=0; i < nZeilenanzahl; i++)
      {
        m_strProdukt = m_List1.GetItemText(i, 0);
        m_strHersteller = m_List1.GetItemText(i, 1);
        m_strPreis = m_List1.GetItemText(i, 2);
        m_strVerfuegbar = m_List1.GetItemText(i, 3);
        DateiSpeichern.WriteString (m_strProdukt+"\n" + m_strHersteller+"\n" + m_strPreis+"\n" + m_strVerfuegbar +"\n" ) ;
      }

      DateiSpeichern.Close ();
      MessageBox ("Daten wurden gespeichert", "Daten gespeichert", MB_ICONINFORMATION);
    }
    else
       MessageBox ("Fehler beim Öffnen der Datei - Speichern wurde abgebrochen", "Fehler", MB_ICONINFORMATION);
 }


Beschreibung
m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. In der Funktion OnLaden() wird zuerst der Pfad der Anwendung ermittelt und die Dateierweiterung abgetrennt. Danach wird mit Hilfe der Funktion DeleteAllItems() der komplette Inhalt des Listenelements gelöscht. Falls bereits eine Datei mit Daten vorhanden ist wird diese geöffnet und zeilenweise in das Listenelement eingelesen.

In der Funktion OnSichern() wird wieder zuerst der Pfad der Anwendung ermittelt und die Dateierweiterung abgetrennt. Dann wird eine Textdatei erstellt und zeilenweise in diese Textdatei geschrieben.

 

Ganze Zeile markieren - SetExtendedStyle()

Das einzigste was sich standardmäßig in einem Listenelement anklicken lässt ist jeweils der Eintrag in der ersten Spalte. Auch die Markierung steht immer nur auf der ersten Spalte:

Listenelement

Mit der Zeile

  m_List1.SetExtendedStyle(LVS_EX_FULLROWSELECT);

lässt sich das leicht ändern. Fügt man diese Zeile ein, kann man die ganze Zeile in einem Listenelement markieren:

Listenelement

Styles können auch kombiniert werden, z.B wie in den hier gezeigten Screenshots. Da wurden zwei Styles kombiniert, damit auch noch Gitternetzlinien sichtbar sind. Das sieht dann so aus:

  // Gitternetzlinien einfügen und ganze Zeile markieren
  m_List1.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);


m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Gitternetzlinien einfügen - SetExtendedStyle()

Mit nur einer Zeile kann man das Aussehen des Listenelements stark beeinflussen. Mit folgender Zeile fügt man Gitternetzlinien in das Listenelement ein:

  m_List1.SetExtendedStyle(LVS_EX_GRIDLINES);

Gitternetz

Beschreibung
Das Style LVS_EX_GRIDLINES sorgt also dafür das Gitternetzlinien im Listenelement angezeigt werden. Styles können kombiniert werden, z.B mit LVS_EX_FULLROWSELECT, um eine ganze Zeile zu markieren. Verwendet man mehrere Styles, werden diese mit einem | voneinander getrennt.

m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Spaltenkopf beschriften - InsertColumn()

Um den Spaltenkopf zu beschriften verwendet man die Funktion InsertColumn(). Um z.B ein Listelement beim Start der Anwendung zu initialisieren, also den Spaltenkopf beschriften, schreibt man den Code dazu am besten in die OnInitDialog() Funktion.
Hier als Beispiel eine Initialisierung eines Listenelements:

  BOOL CLControl_ReportDlg::OnInitDialog()
  {
    ...
    // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
    // Listenelement initialisieren - Spaltenkopf einfügen
    m_List1.InsertColumn(0, "Produkt" ,LVCFMT_LEFT, 100);
    m_List1.InsertColumn(1, "Hersteller" ,LVCFMT_LEFT, 70);
    m_List1.InsertColumn(2, "Preis" ,LVCFMT_LEFT, 50);
    m_List1.InsertColumn(3, "Verfügbar" ,LVCFMT_LEFT, 70);

    return TRUE;
  }


Spaltenkopf

Beschreibung
Mit Hilfe der Funktion InsertColumn() haben wir den Spaltenkopf beschriftet. Der erste Parameter steht für die jeweilige Spalte, der zweite Parameter für die anzuzeigende Beschriftung, der dritte Parameter für die Ausrichtung des anzuzeigenden Textes und der letzte Parameter gibt die Spaltenbreite in Pixel an. Der dritte Parameter, welcher für die Ausrichtung des Textes steht, kann folgende Werte annehmen: LVCFMT_LEFT, LVCFMT_RIGHT oder LVCFMT_CENTER.

m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Zeilenanzahl ermitteln - GetItemCount()

Um heraus zu finden wieviele Zeilen ein Listenelement hat, kann man die Funktion GetItemCount() verwenden:

  // Anzahl der Zeilen im Listenfeld holen
  int nZeilenanzahl = m_List1.GetItemCount();


Beschreibung
In der Variablen nZeilenanzahl steht die Anzahl der vorhandenen Zeilen im Listenelement. m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl.
Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Zeilen löschen - DeleteItem()/DeleteAllItems()

Um eine Zeile im Listenelement zu löschen kann man die Funktion DeleteItem() verwenden. Man übergibt ihr die zu löschende Zeilennummer. Möchte man z.B eine vom Anwender markierte Zeile löschen, dann sieht der Code dazu so aus:

  // Ausgewählte Zeile ermitteln
  int nZeilenauswahl;
  nZeilenauswahl = m_List1.GetNextItem(-1, LVNI_SELECTED);

  // Ausgewählte Zeile löschen
  m_List1.DeleteItem(nZeilenauswahl);


Möchte man alle Zeilen des Listenelements auf einmal löschen, kann man die Funktion DeleteAllItems() verwenden:

  // Kompletten Inhalt des Listenelements löschen
  m_List1.DeleteAllItems();


m_List1 ist eine Variable des Listenelement und vom Typ CListCtrl. Im Beispiel: Beispiel mit Listenelement in Berichts-Ansicht ist der oben gezeigte Code enthalten.

 

Die Vervielfältigung der auf diesen Seiten enthaltenen Informationen und Grafiken ist untersagt, ausgenommen davon ist sämtlicher auf diesen Seiten angezeigter Quellcode. Es wird keinerlei Gewähr für die Richtigkeit und Vollständigkeit der bereitgestellten Informationen übernommen. Haftungsansprüche für Schäden, die durch Nutzung der bereitgestellten Informationen verursacht wurden, sind ausgeschlossen. Für den Inhalt von angegebenen Verweisen wird keine Gewährleistung übernommen.
Copyright © 2005 coding-help.de