The following serves as one, among many, solutions; it's only one approach. But it finally fixed the report. Wrapping the items became a no-go; it's not possible, as far as I know. So I decided to just have max of 10 items per row in the subreport.
I started by working on the data, which as is indicated above, came in as CSV values from the db. Not ideal, but workable.
var weightList = dataModel.Weight.Split(',')
.Select(x => int.Parse(x).ToString("##,###"))
.ToList();
var lengthList = dataModel.Length.Split(',').ToList();
reportData.RowModelList = new List<RowModel>();
for (var i = 0; i < weightList.Count; i=i+10)
{
var weightListTen = new List<string>();
var weightArrayItems = weightList.Skip(i).Take(10).ToArray();
for (var k = 0; k < 10; k++)
{
weightListTen.Add(weightArrayItems.Length - 1 < k ? "" : weightArrayItems[k]);
}
var lengthListTen = new List<string>();
var lengthArrayItems = lengthList.Skip(i).Take(9).ToArray();
for (var l = 0; l < 10; l++)
{
lengthListTen.Add(lengthArrayItems.Length - 1 < l ? "" : FeetAndInches(int.Parse(lengthArrayItems[l])));
}
var idList = new List<string>();
var diff = (i + 10) - weightList.Count;
for (var j = i+1; j <= weightList.Count + diff ; j++)
{
idList.Add(j <= weightList.Count ? j.ToString() : "");
}
var rowModel = new RowModel
{
Weight = weightListTen.ToArray(),
Length = lengthListTen.ToArray(),
Id = idList.Select(x => x.ToString()).ToArray()
};
reportData.RowModelList.Add(rowModel);
}
private static string FeetAndInches(int inches)
{
return inches / 12 + "' " + inches%12 + "\"";
}
Here's the Renderer
class that handles the RDLC rendering:
public class Renderer
{
private readonly int _id;
private ReportDataSource _dataSourceMain;
private ReportDataSource _dataSourceRows;
public Renderer(int id)
{
_id = id;
}
public async Task<byte[]> RenderPdfAsync()
{
var report = await DataService.GetReportDataAsync(_id);
Warning[] warnings;
string[] streamIds;
var mimeType = string.Empty;
var encoding = string.Empty;
var extension = string.Empty;
using (var report = new LocalReport())
{
report.ShowDetailedSubreportMessages = true;
report.EnableExternalImages = true;
report.ReportEmbeddedResource = "Renderer.Templates.Pdf.rdlc";
var dsMain = new List<DataModel> { report };
_dataSourceMain = new ReportDataSource("dsMain", dsMain);
_dataSourceRows = new ReportDataSource("dsRows", permit.RowModelList);
report.DataSources.Add(_dataSourceMain);
report.DataSources.Add(_dataSourceRows);
report.SubreportProcessing += SetSubDataSource;
report.Refresh();
var result = report.Render("PDF", null, out mimeType, out encoding, out extension, out streamIds, out warnings);
if (warnings.Any())
{
Logger.Log(LogLevel.Error, null, "Report Processing Messages: " + string.Join(", *", warnings.Select(x => new
{x.Message, x.Code, x.ObjectName, x.ObjectType, x.Severity})));
}
return result;
}
}
private void SetSubDataSource(object sender, SubreportProcessingEventArgs e)
{
e.DataSources.Clear();
e.DataSources.Add(_dataSourceMain);
e.DataSources.Add(_dataSourceRows);
}
}
The subreport RDLC uses a Tablix bound to the dsRows
and then references the array elements across ID
, Weight
, and Length
rows. The Tablix has its header row deleted and has a column on the left with the static values of those 3 row headers entered into textbox controls.
Then there are 10 columns to the right of that header information column; these display the array elements for that row. In each cell, an expression fetches the respective array value: =Fields!Weight.Value(0)
in the first Weight
column, and =Fields!Weight.Value(9)
in the last one.
There are a lot of better ways to assemble the data in the for
loop earlier (for example, merging the multiple loops into one); but it's working and we'll visit it as time allows to refactor and gain efficiencies.
Hope this helps someone else.