Dynamische Tabelle

Sehr geehrte Damen und Herren,

ist es möglich eine dynamische Tabelle innerhalb eines Reports zu generieren?
Das bedeutet, dass die Anzahl der Spalten und Zeilen variieren kann.

Wie verwenden VisualStudio, C#, .NET 7

Da fallen mir zwei Möglichkeiten ein:

  1. Direkt im Designer
    Je nach Grad der Flexibilität könnte man hier die Spalten fest definieren und jeweils mit einer Darstellungsbedingung versehen. Macht vielleicht für ein oder zwei Spalten noch Sinn und Spaß… aber irgendwann leidet vermutlich die Lesbarkeit und Wartungsmöglichkeit der Projektdatei im Designer.

  2. Das Projekt im Source-Code erstellen
    Hier kann man die DOM API von List & Label verwenden - siehe auch combit.Reporting.Dom Namespace. Darüber ist es möglich komplett im Source-Code entweder eine Projektdatei vollständig zu erzeugen oder auch vorhandene zu laden und anzupassen. Es werden dafür auch .NET Beispiele im Source-Code mitgeliefert… schau mal ins Verzeichnis ..\Beispiele\Microsoft .NET\.NET 6\WinForms\ - da findest sich drei oder view Beispiele zu diesem Thema.

Hi Vitalij,
einfachste Methode wäre über die DOM API z.B. über

So kannst du on runtime das Projekt, die Objekte dynamisch erstellen.
combit hat im .net Ordner wenn ich mich nicht täusche Beispiele hierzu rum liegen :wink:

Ansonsten im Designer eventuell mit Exists() GetValue usw.
Also Spalten mit Bedingungen anzeigen oder nicht und Inhalt per
If (Exists(“Kunde.Status”), GetValue(“Kunde.Status”), “Inaktiv”) z.B. holen, dementsprechend dann die Breite der Tabelle bzw. Berichtscontainers anpassen, wobei DOM hier schon cooler ist :wink:

der Oli war schneller :slight_smile:

1 Like

Hallo Oliver,
Hallo Erdal,

danke erstmal für den Hinweis.

Ich habe mir die Beispiele angeschaut. Das geht schon in die richtige Richtung.
Nun stellt sich die Frage ob es auch möglich ist ein Report zu laden und eine bestehende Tabelle zu ändern oder erweitern?

Gruß Vitalij

Kurz und bündig, ja du kannst dir das Projekt mit Open holen und die Objekte per proj.Objects[Name oder Index] holen, z.b
ReportContainer container = (ReportContainer)proj.Objects[“Dein Container Name”];
SubItemTable table = (SubitemTable)container.SubItems[“Dein Tabellen Name”];
dein table hat dann ein Collection von fields usw. usw. (table.Fields[…]

Um an ein existierendes Projekt zu gelangen gibt es zwei Varianten:

Und sonst müsste man wie @Erdal_Alacali schon geschrieben hatte auf das Element SubItemTable Klasse zugreifen.

Hier mal ein Code-Snippet dazu, dass eine Projektdatei öffnet zum Schreiben und dort im Berichtscontainer in jeder dort enthaltenen Tabelle eine Kopfzeile und eine Datenzeile hinzufügt (ungetestet):

...
using (ProjectList proj = new ProjectList(LL))
{
	proj.Open(Path.Combine(Application.StartupPath, "project.lst"), LlDomFileMode.Open, LlDomAccessMode.ReadWrite);
	
	ObjectReportContainer container;
	foreach (DomItem obj in proj.Objects)
	{	
		if(obj is ObjectReportContainer)
        {		
			container = (ObjectReportContainer)obj;
			foreach (SubItemTable table in container.SubItems)
			{
				TableLineHeader tableLineHeader = new TableLineHeader(table.Lines.Header);
				TableFieldText header = new TableFieldText(tableLineHeader.Fields);
				header.Contents = "Spalte in Kopfzeile";
				header.Width = "50";

				TableLineData tableLineData = new TableLineData(table.Lines.Data);
				TableFieldText tableField = new TableFieldText(tableLineData.Fields);
				tableField.Contents = "DATATABLE.FIELDNAME";
				tableField.Width = "50";
			}
		}
	}
	proj.Save();
}
...
1 Like

Hallo Oliver,

muss ich die GetFromParent Methode in einem bestimmten Event aufrufen?

In der Dokumentation steht: Im datengebundenen Modus muss diese Methode im Kontext des DefinePrintOptions-Events aufgerufen werden.

Folgendes habe ich ausprobiert, leider ohne Erfolg:

ll.DefinePrintOptions += (s, e) =>
{
    var proj = new ProjectList(ll);
    proj.GetFromParent();
                 

    ObjectReportContainer orc = (ObjectReportContainer)proj.Objects["MedicationsContainer"];

    SubItemTable table = (SubItemTable)orc.SubItems["VerblistertTable"];

    TableLineHeader tableLineHeader = (TableLineHeader)table.Lines.Header[0];
    TableFieldText header = new TableFieldText(tableLineHeader.Fields);
    header.Contents = string.Format("\"{0}\"", "DoseCount");
    header.Width = "50";

    TableLineData tableLineData = (TableLineData)table.Lines.Data[0];
    TableFieldText tableField = new TableFieldText(tableLineData.Fields);
    tableField.Contents = string.Format("\"{0}\"", "MedicationReportItem.DoseCount");
    tableField.Width = "50";
    tableField.Filling.Pattern = "0";
};

Eigentlich ja in dem Event, was heißt den ohne Erfolg? Läufst du durch, gibts nen Fehler oder was genau?

Läuft alles durch, die Fields-Collection wird um ein Field ergänzt, jedoch taucht die neue Spalte im Report nicht auf.

Magst mal ein wenig mehr Informationen spendieren, was genau machst du, welcher Ablauf, druckst oder exportierst du das korrekte Dokument, eigentlich ist das hier genau dafür gedacht, das man in diesem Event während bzw. vor dem Druck noch Anpassungen durchführen kann auf das AKTUELL geladene Projekt, das dann auch gedruckt werden soll.

Vielleicht kannst ja mal debwin mit laufen lassen und schauen ob der was ungewöhnliches ausspuckt :wink:

Hallo zusammen,

ich habe feste und variable Spalten in meiner DataTable. Die DataTable wird zur Laufzeit generiert und die Variablen Spalten sollen hinzugefügt werden.

Im Designer sind alle Spalten in der Datenquelle vorhanden und können hinzugefügt und angezeigt werden.

image

Ich habe die Spalte ArticleName fest im Designer hinzugefügt, diese wird auch angezeigt.

Ich hänge mich an das Event an und möchte die restlichen Spalten dynamisch hinzufügen:

ll.DefinePrintOptions += (s, e) =>
 {
     var proj = new ProjectList(ll);
     proj.GetFromParent();

     ObjectReportContainer orc = (ObjectReportContainer)proj.Objects["MedicationsContainer"];

     SubItemTable table = (SubItemTable)orc.SubItems["VerblistertTable"];

     TableLineHeader tableLineHeader = (TableLineHeader)table.Lines.Header[0];
     TableLineData tableLineData = (TableLineData)table.Lines.Data[0];

     //TableLineHeader tableLineHeader = new TableLineHeader(table.Lines.Header);
     //TableLineData tableLineData = new TableLineData(table.Lines.Data);

     foreach (DataColumn col in medicationsDataTable.Columns)
     {
         var headerContents = string.Format("\"{0}\"", @$"{col.ColumnName}");
         if (tableLineHeader.Fields.Cast<TableFieldText>().Any(x => x.Contents == headerContents))
             continue;

         var templateHeaderColor1 = tableLineHeader.Fields[0].Filling.Color;
         var tableWidth = "7500";
         
         TableFieldText header = new TableFieldText(tableLineHeader.Fields);
         header.Contents = headerContents;
         header.Width = tableWidth;
                      
         TableFieldText tableField = new TableFieldText(tableLineData.Fields);
         tableField.Contents = table.TableId + "." + col.ColumnName;
         
         tableField.Width = tableWidth;
         tableField.AlignmentVertical = "1";

     }

Beim Header funktioniert alles, aber beim Content wird ein Fehler geworfen:

DebugLog:

CXLL27  : 09:48:08.718 00000bb8/02 3 [L02 DOM]:   >LlDomSetProperty(000001EE2A7D3D38,'Contents','Medications.ActiveArticleSubstanceNames')
CXLL27  : 09:48:08.718 00000bb8/02 4 [L01 GEN]:    *Syntaxfehler: 'Medications.ActiveArticleSubstanceNames' kann nicht interpretiert werden
CXLL27  : 09:48:08.718 00000bb8/02 5 [L04 GEN]:    error parsing formula 'Medications.ActiveArticleSubstanceNames': Syntaxfehler: 'Medications.ActiveArticleSubstanceNames' kann nicht interpretiert werden
CXLL27  : 09:48:08.718 00000bb8/02 6 [L02 NTF]:    >@NOTIF.(code= 51, param=000001EE27B64418, user=0000000000000000)
CXLL27  : 09:48:08.718 00000bb8/02 7 [L02 NTF]:    <@NOTIF.(code= 51) -> 0000000000000000
CXLL27  : 09:48:08.718 00000bb8/02 8 [L01 GEN]:      [formula was 'Medications.ActiveArticleSubstanceNames']
CXLL27  : 09:48:08.718 00000bb8/02 9 [L02 DOM]:   <LlDomSetProperty() -> -23 (0xffffffe9) (Einer der verwendeten Ausdrücke hat einen Fehler. Beim Designstart werden die Fehler interaktiv angezeigt. In anderen Fällen benutzen Sie den Debug-Modus zur Bestimmung des Fehlers.)
CXLL27  : 09:48:08.721 00000bb8/02 0 [L02 API]:   >LlGetErrortext(-23,000001EE2A8E4060,16384)
CXLL27  : 09:48:08.721 00000bb8/02 1 [L02 API]:   <LlGetErrortext() -> 0 (0x00000000) ['Einer der verwendeten Ausdrücke hat einen Fehler. Beim Designstart werden die Fehler interaktiv angezeigt. In anderen Fällen benutzen Sie den Debug-Modus zur Bestimmung des Fehlers.']
CXLL27  : 09:48:10.123 00000bb8/07 2 [L04 EXT]:   Caught LL_Expression_Exception (Einer der verwendeten Ausdrücke hat einen Fehler. Beim Designstart werden die Fehler interaktiv angezeigt. In anderen Fällen benutzen Sie den Debug-Modus zur Bestimmung des Fehlers.). 
CXLL27  : 09:48:10.123 00000bb8/07 3 [L04 EXT]:    Inner Exception:  () 
CXLL27  : 09:48:10.123 00000bb8/07 4 [L04 EXT]:    Stack Trace: 
CXLL27  : 09:48:10.123 00000bb8/07 5 [L04 EXT]:       at combit.Reporting.LLException.CheckReturn(Int32 returnValue)
CXLL27  : 09:48:10.123 00000bb8/07 6 [L04 EXT]:      at combit.Reporting.LlCore.LlDomSetProperty(IntPtr domObject, String propertyName, String propertyValue)
CXLL27  : 09:48:10.123 00000bb8/07 7 [L04 EXT]:      at combit.Reporting.Dom.DomItem.SetProperty(String propertyName, String propertyValue, Boolean throwException)
CXLL27  : 09:48:10.123 00000bb8/07 8 [L04 EXT]:      at combit.Reporting.Dom.TableFieldText.set_Contents(String value)
CXLL27  : 09:48:10.123 00000bb8/07 9 [L04 EXT]:      at BlisterCenter.CustomerClient.Modules.PatientManagement.ViewModels.PatientsManagementViewModel.<>c__DisplayClass211_0.<ShowMedicationPlan>b__0(Object s, EventArgs e)
CXLL27  : 09:48:10.123 00000bb8/07 0 [L04 EXT]:      at combit.Reporting.ListLabel.PrintReportFromRelationalDataSourceNewMode(IDataProvider dataSource, String projectFile, Boolean showFileSelect, LlPrintMode printMode, LlBoxType boxType, String dialogTitle, Boolean showPrintOptions, String tempPath)
CXLL27  : 09:48:10.123 00000bb8/07 1 [L04 EXT]:      at combit.Reporting.ListLabel.AutoPrint(LlProject projectType, String projectFile, Boolean showFileSelect, LlPrintMode printMode, LlBoxType boxType, String dialogTitle, Boolean showPrintOptions, String tempPath)

Interessant ist, dass wenn ich die Property ''ActiveArticleSubstanceNames" im Designer setze, funktioniert die Anzeige und wenn ich mir das Object ansehe, steht in der Eigenschaft: Contents des Feldes “Medications.ActiveArticleSubstanceNames”. Wenn ich die Eigenschaft Contents zur Laufzeit setze: tableField.Contents = “Medications.ActiveArticleSubstanceNames”, kommt es zu dem Fehler.

Bitte um Hilfe :slight_smile:

Gruß Vitalij

Das Problem dürfte sein, dass das Feld (aus Optimierungsgründen) nicht angemeldet wird, weil es zum Zeitpunkt des Druckstarts als “nicht benötigt” geflagged ist. Du könntest mal versuchen, die Eigenschaft CheckUsedIdentifiers deiner ListLabel-Instanz auf false zu setzen. Dann werden alle Felder angemeldet, unabhängig davon ob sie (augenscheinlich) gebraucht werden oder nicht.

1 Like

Ich habe die Eigenschaft CheckUsedIdentifiers auf false gesetzt. Nun konnten die Spalten hinzugefügt werden. Jetzt ist es aber so, dass die neu hinzugefügten Spalten nicht in Report angezeigt werden.

Kannst du im Designer mal auf die Tabelle doppelklicken? Sind die Felder da angelegt? Was bedeutet “nicht angezeigt werden”? Sind sie leer oder gar nicht sichtbar? Vielleicht ist die Tabelle jetzt nicht breit genug? Poste doch mal ein paar Screenshots und Infos.

Hallo Jochen,

danke für den Hinweis. Die neuen Spalten waren im nicht sichtbaren Bereich. Nachdem ich der festen Spalte eine feste Breite zugewiesen habe sind sie da. Nun funktioniert alles wie es soll.

Vielen Dank!

Gruß Vitalij

1 Like