VC++ MSChart (Kreisdiagramm);

  • #1
D

Don_Pazo

Bekanntes Mitglied
Themenersteller
Dabei seit
16.09.2005
Beiträge
112
Reaktionspunkte
0
Hallo,
ich habe ein Programmchen mit Visual C++ 6.0 erstellt, indem einen MSChart Element (Kreisdiagramm) gibt. Das ist eine Kreisdiagramm. Sie zeigt für eine bestimmte Person seine (geplante, laufende und fertige) Projekte. (Beispiel )

Ich will, dass noch der Anzahl bzw. Prozentualanteil auf der Diagramm erscheinen (wie z.B. im ). Weist jemand, wie ich das implementieren kann?
 
  • #2
chart_test.gif


Verwendest Du VC7 oder höher? Hast Du die Wrapper generieren lassen (wenn >=VC7: Rechtsklick auf Projekt im Solution-Explorer, Klasse hinzufügen, MFC-Klasse aus Typbibliothek, Microsoft Chart Control ... und dann alle IVc*-Klassen wählen).
Anschließend kannst Du die Header wieder aus dem Projekt entfernen, außer

CVcplot.h
CVcseriescollection.h
CVcdatapoint.h
CVcdatapoints.h
CVcSeries.h
CVcDataPointLabel.h
CVcFont.h
CVcLegend.h
CVcColor.h
CVcBrush.h

Du kannst Dir dann eine kleine Helferklasse schreiben, die das Ganze kapselt, z.B. so (ich habe als Beispiel noch ein paar andere Funktionen eingefügt):

Code:
#include mschart.h // Name kann variieren
#include CVcplot.h
#include CVcseriescollection.h
#include CVcdatapoint.h
#include CVcdatapoints.h
#include CVcSeries.h
#include CVcDataPointLabel.h
#include CVcFont.h
#include CVcLegend.h
#include CVcColor.h
#include CVcBrush.h

class PieChartHelper {
private:
  CMschart& chart;
  CVcPlot plot;
  CVcSeriesCollection serCol;

  void getDPsp(int idx, CVcDataPoint& dp) {
    CVcSeries ser = serCol.get_Item(idx);
    CVcDataPoints dps = ser.get_DataPoints();
    dp = dps.get_Item(-1);
  }

public:
  PieChartHelper(CMschart& _chart) : chart(_chart) {
    plot = chart.get_Plot();
    serCol = plot.get_SeriesCollection();    
    SetDefaults();
  }  

  void SetData(int a, int b, int c) {

    COleSafeArray sarr;
    DWORD numElements[] = {1, 3};
    sarr.Create(VT_I4, 2, numElements);

    long index[2];
    index[0] = 0;
    index[1] = 0;
    sarr.PutElement(index, (void*)&a);

    index[1] = 1;
    sarr.PutElement(index, (void*)&b);

    index[1] = 2;
    sarr.PutElement(index, (void*)&c);

    chart.put_ChartData(sarr.Detach());
    chart.Refresh();
  }

  void SetData(int values[], int len) {
    
    COleSafeArray sarr;
    DWORD numElements[] = {1, len};
    sarr.Create(VT_I4, 2, numElements);

    long index[2];
    index[0] = 0;
    for(int idx = 0; idx < len; ++idx) {
      index[1] = idx;
      sarr.PutElement(index, (void*)&values[idx]);
    }

    chart.put_ChartData(sarr.Detach());
    chart.Refresh();
  }

  void SetLabels(const CString& l1, const CString& l2, const CString& l3) {
    
    ASSERT(serCol.Count() == 3);
    
    CVcSeries ser = serCol.get_Item(1);
    ser.put_LegendText(l1);
    ser = serCol.get_Item(2);
    ser.put_LegendText(l2);
    ser = serCol.get_Item(3);
    ser.put_LegendText(l3);    
  }

  void SetLabels(const CStringArray& sl) {
    
    ASSERT(serCol.Count() == sl.GetCount());

    CVcSeries ser;
    for(int idx = 1; idx <= sl.GetCount(); ++idx) {
      ser = serCol.get_Item(idx);
      ser.put_LegendText(sl[idx-1]);
    }    
  }

  void SetColors(COLORREF colors[], int len) {

    ASSERT(serCol.Count() == len);

    for(int dpointIdx = 1; dpointIdx <= serCol.Count(); ++dpointIdx) {
      CVcDataPoint dp;
      getDPsp(dpointIdx, dp);
      CVcBrush brush = dp.get_Brush();
      CVcColor cfill = brush.get_FillColor();
      cfill.Set(GetRValue(colors[dpointIdx-1]), GetGValue(colors[dpointIdx-1]),
        GetBValue(colors[dpointIdx-1]));
    }
  }
  
  // Danach hast Du gefragt.
  // Wenn->percent' false ist, werden die Werte selbst angezeigt
  // CMschart::VtChLabelLocationTypeInside = Im Kreis 

  void SetDataPointLabels(bool percent, float fntSize = 10.0f) {

    for(int dpointIdx = 1; dpointIdx <= serCol.Count(); ++dpointIdx) {

      CVcDataPoint dp;
      getDPsp(dpointIdx, dp);

      CVcDataPointLabel dpl = dp.get_DataPointLabel();

      if(percent) {
        dpl.put_PercentFormat(0%);
        dpl.put_LocationType(CMschart::VtChLabelLocationTypeInside);
        dpl.put_Component(CMschart::VtChLabelComponentPercent);
      } else {
        dpl.put_LocationType(CMschart::VtChLabelLocationTypeInside);
        dpl.put_Component(CMschart::VtChLabelComponentValue);
      }

      CVcFont dFnt(dpl.get_VtFont());
      dFnt.put_Size(fntSize); 

    }
  }

  void SetDefaultValues() {
    SetData(0, 0, 0);
  }

  void SetDefaultColors() {
    COLORREF colors[] = {RGB(0, 250, 0), RGB(250, 250, 0), RGB(250, 0, 0)};
    SetColors(colors, 3);
  }

  void SetDefaultLabels() {
    SetLabels(Fertige Projekte, Laufende Projekte, Geplante Projekte);
  }

  void SetDefaultDataPointLabels() {
    SetDataPointLabels(true);
  }

  void SetName(const CString& name) {
    chart.put_RowLabel(name);
  }

  void SetDefaults() {
    chart.put_chartType(CMschart::VtChChartType2dPie);
    chart.put_ShowLegend(TRUE);
    chart.put_RowCount(1);
    chart.put_ColumnCount(3);
    chart.put_AllowDynamicRotation(FALSE);
    chart.put_AllowSelections(FALSE);
    chart.put_AllowSeriesSelection(FALSE);
    
    SetDefaultValues();
    SetDefaultColors();
    SetDefaultLabels();
    SetDefaultDataPointLabels();
  }
};

In Deiner Dialog/PropertyPage/... -Klasse kannst Du dann eine entsprechende Variable deklarieren:

PieChartHelper* m_p3ch;

In OnInitDialog:

m_p3ch = new PieChartHelper(m_chart);

m_chart ist die Control-Variable, die Du sicher schon vom Class/Variable-Wizard hast erzeugen lassen.
CMschart oben ist die entsprechende Klasse; beides kann bei Dir natürlich anders heißen.

Dann

m_p3ch->SetData(10, 5, 7);
m_p3ch->SetName(Egbert Brendle);

etc.

Solltest Du VC6 verwenden, funktioniert der Code oben so nicht, da die Namen der Funktionen teilweise anders lauten ...
z.B. GetPlot statt get_Plot, SetRowLabel statt put_RowLabel usw.; alles mehr oder weniger nach diesem Muster.
 
  • #3
Danke,
ich benutze leider genau Visual C++ 6.0 ( das sollte ich eigentlich erwehnen). Ich habe versucht von deinen Code selbst die passende Funktionen abzuleiten, es ist mir nicht gelungen.
Ich zeige dir  wie wiet ich bin:

Code:
// Diagramm Variablen
double dGrunNiko	= 0;
double dGelbNiko	= 0;
double dRotNiko	= 0;

long index[2];	// [0][]Benutzer, [1][]Farben

COleSafeArray saDiagram;		// Diagram Objekt
DWORD numElements[] = {1, 3};	// Diagram Elemente 1(Benutzer) x 3(Farben)


//... hier werden die Werte für dGrunNiko, dGelbNiko und dRotNiko erstellt


/*========= Diagram erstellen ===============*/

// Diagram Create, 2-Dimensional, mit 1x3 Elemente
saDiagram.Create(VT_R8, 2, numElements);

for(index[0]=0; index[0]<1; index[0]++) {	// Benutzer
    for(index[1]=0; index[1]<3; index[1]++) {	// Wariable
			
	if(index[0] == 0){			// Niko
		if(index[1] == 0){		// Grün
			saDiagram.PutElement(index, &dGrunNiko);
		}
		else if(index[1] == 1){	// Gelb
			saDiagram.PutElement(index, &dGelbNiko);
		}
		else if(index[1] == 2){	// Rot
			saDiagram.PutElement(index, &dRotNiko);
		}
	}
   }
}

// Diagramm Werte einfügen
m_cDiagram.SetChartData(saDiagram.Detach());
	
// Farbenbedeutung setzen
m_cDiagram.SetColumn(1);
m_cDiagram.SetColumnLabel(fertige Projekte);

m_cDiagram.SetColumn(2);
m_cDiagram.SetColumnLabel(laufende Projekte);
	
m_cDiagram.SetColumn(3);
m_cDiagram.SetColumnLabel(geplante Projekte);
	
// Benutzer setzen
m_cDiagram.SetRow(1);
m_cDiagram.SetRowLabel(Nikolay Nenov);

/*===============================*/
//    Hier gleube ich sollte es möglich sein die
//    Prozentun in dem Kreisdiagram einzufügen (nun wie ???)
/*===============================*/
	
m_cDiagram.Refresh();

Weisst du zufällig wie das beim Visual C++ 6.0 geht?
 
  • #4
Bei VC6 werden mehr oder weniger automatisch die Wrapper (als Header- und Source-Dateien) generiert. Wenn man bei VC7 wie beschrieben vorgeht, erhält man nur eine Pseudo-Sourcedatei; der eigentliche Code steht in den ganzen Header-Dateien (alles inline).

[Wenn Du diese Dateien nicht hast:
Als Du den Klassenassistenten aufgerufen hast, um die Variable m_cDiagram erzeugen zu lassen, müsste eine solche Meldung angezeigt worden sein:
Das ActiveX-Steuerelement Microsoft Chart Control ... wurde nicht in Ihr Projekt eingefügt. Developer Studio wird dies jetzt vornehmen und eine C++-Wrapper-Klasse dafür erstellen.
Danach kannst Du Häkchen bei den Klassen setzen, die generiert werden sollen. Zuerst steht da die Klasse, welche auf jeden Fall benötigt wird: CMSChart. Darunter dann eine ganze Menge Klassen, die mit CVc beginnen. Diese (bzw. einige davon) benötigst Du hier auch, nicht nur CMSChart.
Wenn Du auf OK klickst, werden cpp- und h-Dateien erzeugt und in Dein Projekt eingefügt. Wenn unten alles klappt, kannst sie sicher teilweise auch wieder aus dem Projekt nehmen bzw. von Hand in einen eigenen Unterordner verschieben, damit es übersichtlicher ist.]

Ansonsten gleiche Vorgehensweise wie bei VC7, nur die Funktionen heißen anders (wie gesagt): Statt put_ Set als Prefix und statt get_ dann Get. Kleiner Schönheitsfehler bei VC6: Es sind keine enums für die Konstanten vorhanden.

So sollte es mit VC6 klappen (ist aber relativ unflexibel; Du kannst Dich ja an dem Code oben orientieren, wenn es erst mal funktioniert):

Code:
// ...

#include mschart.h // bzw. der gewählte Dateiname
#include vcplot.h
#include vcseriescollection.h
#include vcseries.h
#include vcdatapoints.h
#include vcdatapoint.h
#include vcdatapointlabel.h
#include vcfont.h

// ...

enum
{
  VtChLabelLocationTypeNone = 0,
  VtChLabelLocationTypeAbovePoint = 1,
  VtChLabelLocationTypeBelowPoint = 2,
  VtChLabelLocationTypeCenter = 3,
  VtChLabelLocationTypeBase = 4,
  VtChLabelLocationTypeInside = 5,
  VtChLabelLocationTypeOutside = 6,
  VtChLabelLocationTypeLeft = 7,
  VtChLabelLocationTypeRight = 8
} VtChLabelLocationType;

enum
{
    VtChLabelComponentValue = 1,
    VtChLabelComponentPercent = 2,
    VtChLabelComponentSeriesName = 4,
    VtChLabelComponentPointName = 8
} VtChLabelComponent;

// ...

void CDeinIrgendwas::SetChart(const CString& name, bool dispPercent, float componentFntSize, int gruen, int gelb, int rot) {

  COleSafeArray saDiagram;	                // Hilfsobjekt zum Füllen des Diagramms
  DWORD numElements[] = {1, 3};	// 1(Kreisdiagramm) x 3(Kreisabschnitte)
  long index[2];

  saDiagram.Create(VT_I4, 2, numElements);
  
  // index[0] = row index = 0
  // In diesem Fall haben wir immer nur ein Diagramm gleichzeitig, also ist der row index immer gleich 0.
  index[0]=0;

  // index[1] (column) bestimmt, um welchen Kreisabschnitt es geht; also hier 0 bis 2  index[0]=0;
  index[1]=0;
  saDiagram.PutElement(index, &gruen);
  index[1]=1;
  saDiagram.PutElement(index, &gelb);
  index[1]=2;
  saDiagram.PutElement(index, &rot);

  // Diagramm mit Werten belegen
  m_cDiagram.SetChartData(saDiagram.Detach());
	
  m_cDiagram.SetRowLabel(name);
	
  // Legende setzen  
  m_cDiagram.SetColumn(1);
  m_cDiagram.SetColumnLabel(fertige Projekte);
  m_cDiagram.SetColumn(2);
  m_cDiagram.SetColumnLabel(laufende Projekte);	
  m_cDiagram.SetColumn(3);
  m_cDiagram.SetColumnLabel(geplante Projekte);
  
  // Dem Chart-Control mitteilen, ob bzw. wie es Prozente/Werte der Kreisabschnitte anzeigen soll
  CVcPlot plot(m_cDiagram.GetPlot());
  CVcSeriesCollection serCol = plot.GetSeriesCollection();
  CVcSeries ser;
  CVcDataPoints dps;
  CVcDataPoint dp;
  CVcDataPointLabel dpl;
  for(int idx = 1; idx <= 3; ++idx) {
    ser = serCol.GetItem(idx);
    dps = ser.GetDataPoints();
    dp = dps.GetItem(-1);
    dpl = dp.GetDataPointLabel();

    if(dispPercent) {
        // Prozente anzeigen
        dpl.SetPercentFormat(0%);
        dpl.SetLocationType(VtChLabelLocationTypeInside);
        dpl.SetComponent(VtChLabelComponentPercent);
    } else {
        // Werte selbst anzeigen
        dpl.SetLocationType(VtChLabelLocationTypeInside);
        dpl.SetComponent(VtChLabelComponentValue);
    }
    // Größe der Prozent- bzw. Wertedarstellung setzen (ist sonst wahrscheinlich etwas zu klein)
    CVcFont dFnt(dpl.GetVtFont());
    dFnt.SetSize(componentFntSize);
  }
  
  m_cDiagram.Refresh();  
}

// Mit Anzeige der Prozente (hier 25%, 33%, 42%)
SetChart(Nikolay Nenov, true, 10.0f, 3, 4, 5);

// Mit Anzeige der Werte (hier 3, 4, 5)
SetChart(Nikolay Nenov, true, 10.0f, 3, 4, 5);

Die Farben des Diagramms musst Du natürlich noch auf grün, gelb und rot ändern. Standard ist rot, grün, blau. Auch die Möglichkeit des Selektierens etc. würde ich ausschalten (in den Eigenschaften/Properties).
 
  • #5
Vielen Dank für die Hilfe. Es klapt prima !!!
 
Thema:

VC++ MSChart (Kreisdiagramm);

ANGEBOTE & SPONSOREN

Statistik des Forums

Themen
113.840
Beiträge
707.965
Mitglieder
51.494
Neuestes Mitglied
Flensburg45
Oben