Here's an alternative solution with some crazy LINQ
and nested foreach
. First, I've these in print
function:
void print() {
var dialog = new PrintDialog();
if (dialog.ShowDialog() != true) return;
var width = dialog.PrintableAreaWidth;
var height = dialog.PrintableAreaHeight;
var groupedEntries =
rps.GroupBy(x => x.Control)
.Select(x => new {
Name = x.Key,
Cash = x.Sum(x => x.Cash),
Kind = x.Sum(x => x.Kind),
Total = x.Sum(x => x.Total),
Head = x.GroupBy(x => x.Head)
.Select(x => new {
Name = x.Key,
Cash = x.Sum(x => x.Cash),
Kind = x.Sum(x => x.Kind),
Total = x.Sum(x => x.Total),
Plot = x.GroupBy(x => x.Plot)
.Select(x => new {
Name = x.Key,
Cash = x.Sum(x => x.Cash),
Kind = x.Sum(x => x.Kind),
Total = x.Sum(x => x.Total),
Tenant = x.GroupBy(x => x.Tenant)
.Select(x => new {
Name = x.Key,
Cash = x.Sum(x => x.Cash),
Kind = x.Sum(x => x.Kind),
Total = x.Sum(x => x.Total),
Entry = x
})
})
})
});
List<object> list = new();
foreach (var control in groupedEntries) {
list.Add(new Tuple<int, string>(0, control.Name));
foreach (var head in control.Head) {
list.Add(new Tuple<int, string>(1, head.Name));
foreach (var plot in head.Plot) {
list.Add(new Tuple<int, string>(2, plot.Name));
foreach (var tenant in plot.Tenant) {
if(tenant.Entry.Count() > 1) {
list.Add(new Tuple<int, string>(3, tenant.Name));
foreach (var entry in tenant.Entry)
list.Add(new Tuple<int, ReceiptPayment>(4, entry));
list.Add(new Tuple<int, string, int, int, int>(3, tenant.Name, tenant.Cash, tenant.Kind, tenant.Total));
}
else list.Add(new Tuple<int, ReceiptPayment>(3, tenant.Entry.First()));
}
list.Add(new Tuple<int, string, int, int, int>(2, plot.Name, plot.Cash, plot.Kind, plot.Total));
}
list.Add(new Tuple<int, string, int, int, int>(1, head.Name, head.Cash, head.Kind, head.Total));
}
list.Add(new Tuple<int, string, int, int, int>(0, control.Name, control.Cash, control.Kind, control.Total));
}
var document = new FixedDocument();
var size = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
document.DocumentPaginator.PageSize = size;
var pages = new List<RPPage>();
var page = getPage(document, null);
pages.Add(page);
for (int i = 0; i < list.Count; i++) {
if (!page.AddEntry(list[i])) {
page = getPage(document, page);
page.AddEntry(list[i]);
pages.Add(page);
}
}
foreach (var p in pages)
p.NumberOfPage = document.Pages.Count;
dialog.PrintDocument(document.DocumentPaginator, "");
}
in the nested foreach I create Tuple
for these 3 ContentControls
:
class RPHeaderItem : ContentControl
{
public RPHeaderItem(Tuple<int, string> header) {
Content = new TextBlock() {
FontWeight = FontWeights.Bold,
Margin = new Thickness(10 * header.Item1, 0, 0, 0),
Text = header.Item2
};
}
}
class RPEntryItem : ContentControl
{
public RPEntryItem(Tuple<int, ReceiptPayment> entry) {
var text = new TextBlock() {
Margin = new Thickness(10 * entry.Item1, 0, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left
};
var cash = new TextBlock() { Text = entry.Item2.Cash.ToString(Constants.NumberFormat) };
var kind = new TextBlock() { Text = entry.Item2.Kind.ToString(Constants.NumberFormat) };
var total = new TextBlock() { Text = entry.Item2.Total.ToString(Constants.NumberFormat) };
if (entry.Item1 == 3) {
var tenant = new Run() { Text = entry.Item2.Tenant };
var space = new Run() { Text = " - " + entry.Item2.Space };
text.Inlines.Add(tenant);
text.Inlines.Add(space);
}
else text.Text = entry.Item2.Space;
Grid.SetColumn(cash, 1);
Grid.SetColumn(kind, 2);
Grid.SetColumn(total, 3);
Content = new Grid() {
ColumnDefinitions = {
new ColumnDefinition(),
new ColumnDefinition(){ Width = new GridLength(70) },
new ColumnDefinition(){ Width = new GridLength(70) },
new ColumnDefinition(){ Width = new GridLength(70) }
},
Resources = {
{
typeof(TextBlock),
new Style() {
Setters = {
new Setter(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Right)
}
}
}
},
Children = { text, cash, kind, total }
};
}
}
class RPFooterItem : ContentControl
{
public RPFooterItem(Tuple<int, string, int, int, int> footer) {
var text = new TextBlock() {
Margin = new Thickness(10 * footer.Item1, 0, 0, 0),
Text = "Total " + footer.Item2,
HorizontalAlignment = HorizontalAlignment.Left,
};
var cash = new TextBlock() { Text = footer.Item3.ToString(Constants.NumberFormat) };
var kind = new TextBlock() { Text = footer.Item4.ToString(Constants.NumberFormat) };
var total = new TextBlock() { Text = footer.Item5.ToString(Constants.NumberFormat) };
var separator = new Separator() { Background = Brushes.Black };
Grid.SetColumn(cash, 1);
Grid.SetColumn(kind, 2);
Grid.SetColumn(total, 3);
Grid.SetColumn(separator, 1);
Grid.SetColumnSpan(separator, 3);
Content = new Grid() {
RowDefinitions = {
new RowDefinition(){ Height = GridLength.Auto },
new RowDefinition()
},
ColumnDefinitions = {
new ColumnDefinition(),
new ColumnDefinition(){ Width = new GridLength(70) },
new ColumnDefinition(){ Width = new GridLength(70) },
new ColumnDefinition(){ Width = new GridLength(70) }
},
Resources = {
{
typeof(TextBlock),
new Style() {
Setters = {
new Setter(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Right),
new Setter(Grid.RowProperty, 1)
}
}
}
},
Children = { separator, text, cash, kind, total }
};
}
}
and these ContentControl
are directly added to the ItemsControl in AddEntry
function:
public bool AddEntry(object entry) {
ContentControl content;
if (entry is Tuple<int, string>) content = new RPHeaderItem((Tuple<int, string>)entry);
else if (entry is Tuple<int, ReceiptPayment>) content = new RPEntryItem((Tuple<int, ReceiptPayment>)entry);
else content = new RPFooterItem((Tuple<int, string, int, int, int>)entry);
content.Measure(PageSize);
content.UpdateLayout();
remainingHeight -= content.DesiredSize.Height;
if (remainingHeight < 0) return false;
entries.Items.Add(content);
return true;
}
not bad!
EDIT
----
There's one more issue with this printing. When I print as PDF it always shows all entries I added in a page. In real printing on page sometimes I miss the last added entry in a page. To solve the issue, I'd to add the IsComplete
property back and when a page completes, I set that to true in viewmodel and in the setter of that property I've to call entries.UpdateLayout()
to get the last added entry on real page.