Implemented printing.

This commit is contained in:
Tobias Erbshäußer
2020-06-28 15:29:23 +02:00
parent 42fb20fc3a
commit e656e373e1
8 changed files with 771 additions and 6 deletions
+3 -3
View File
@@ -186,17 +186,17 @@ namespace UCalc
} }
} }
public bool Print() public void Print()
{ {
if (Model.Root.Errors.Count > 0) if (Model.Root.Errors.Count > 0)
{ {
MessageBox.Show( MessageBox.Show(
"Bitte beheben Sie zuerst die angezeigten Fehler, bevor Sie das Dokument drucken können.", "Bitte beheben Sie zuerst die angezeigten Fehler, bevor Sie das Dokument drucken können.",
"Fehler!", MessageBoxButton.OK, MessageBoxImage.Error); "Fehler!", MessageBoxButton.OK, MessageBoxImage.Error);
return false; return;
} }
throw new NotImplementedException(); new PrintWindow(Model) {Owner = this}.ShowDialog();
} }
private void OnDetailsTabSelected(object sender, RoutedEventArgs e) private void OnDetailsTabSelected(object sender, RoutedEventArgs e)
+29
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using UCalc.Controls; using UCalc.Controls;
using UCalc.Data; using UCalc.Data;
@@ -26,5 +27,33 @@ namespace UCalc
public static readonly ImmutableList<string> CostDivisionStrs = public static readonly ImmutableList<string> CostDivisionStrs =
((CostDivision[]) Enum.GetValues(typeof(CostDivision))).Select(value => value.AsString()).ToImmutableList(); ((CostDivision[]) Enum.GetValues(typeof(CostDivision))).Select(value => value.AsString()).ToImmutableList();
public static readonly double DinA4Width;
public static readonly double DinA4Height;
public static readonly Thickness DinA4Padding;
public static double DinA4ContentWidth => DinA4Width - DinA4Padding.Left - DinA4Padding.Right;
public static readonly double PrintDefaultFontSize;
public static readonly double PrintSubjectFontSize;
public static readonly double PrintNewlineFontSize;
static Constants()
{
var converter = new LengthConverter();
// ReSharper disable PossibleNullReferenceException
DinA4Width = (double) converter.ConvertFromInvariantString("29.7cm");
DinA4Height = (double) converter.ConvertFromInvariantString("42cm");
var dinA4MarginLeftRight = (double) converter.ConvertFromInvariantString("3.18cm");
var dinA4MarginTopBottom = (double) converter.ConvertFromInvariantString("2.54cm");
PrintDefaultFontSize = (double) converter.ConvertFromInvariantString("14pt");
PrintSubjectFontSize = (double) converter.ConvertFromInvariantString("16pt");
PrintNewlineFontSize = (double) converter.ConvertFromInvariantString("4pt");
// ReSharper restore PossibleNullReferenceException
DinA4Padding = new Thickness(dinA4MarginLeftRight, dinA4MarginTopBottom, dinA4MarginLeftRight,
dinA4MarginTopBottom);
}
} }
} }
+109
View File
@@ -0,0 +1,109 @@
using System;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;
namespace UCalc.Controls
{
public class PrintableDocument : IDisposable
{
private static readonly Uri DocUri = new Uri($"pack://mietrechner{new Guid()}.xps");
private readonly Package _package;
private readonly XpsDocument _document;
private int PageCount =>
_document.GetFixedDocumentSequence()!.References.First().GetDocument(false)!.Pages.Count;
public PrintableDocument(IDocumentPaginatorSource source)
{
_package = Package.Open(new MemoryStream(), FileMode.Create, FileAccess.ReadWrite);
PackageStore.AddPackage(DocUri, _package);
_document = new XpsDocument(_package, CompressionOption.SuperFast, DocUri.AbsoluteUri);
var manager = new XpsSerializationManager(new XpsPackagingPolicy(_document), false);
var paginator = source.DocumentPaginator;
manager.SaveAsXaml(paginator);
}
public void Dispose()
{
((IDisposable) _document).Dispose();
((IDisposable) _package).Dispose();
PackageStore.RemovePackage(DocUri);
}
public void PreviewIn(DocumentViewer viewer)
{
viewer.Document = _document.GetFixedDocumentSequence();
}
public void Print(string description)
{
var dialog = new PrintDialog {UserPageRangeEnabled = true, MinPage = 1, MaxPage = (uint) PageCount};
if (dialog.ShowDialog() == true)
{
var documentSequence = _document.GetFixedDocumentSequence();
Package convPackage = null;
XpsDocument convDocument = null;
try
{
if (dialog.PageRangeSelection == PageRangeSelection.UserPages)
{
convDocument = ExtractPages(_document, dialog.PageRange.PageFrom - 1,
dialog.PageRange.PageTo - 1, out convPackage);
documentSequence = convDocument.GetFixedDocumentSequence();
}
dialog.PrintDocument(documentSequence!.DocumentPaginator, description);
}
finally
{
convDocument?.Close();
convPackage?.Close();
}
}
}
private static XpsDocument ExtractPages(XpsDocument source, int fromPage, int toPage, out Package package)
{
package = Package.Open(new MemoryStream(), FileMode.Create, FileAccess.ReadWrite);
var docUri = new Uri("pack://mietrechnerTempTicket.xps");
PackageStore.RemovePackage(docUri);
PackageStore.AddPackage(docUri, package);
var document = new XpsDocument(package, CompressionOption.SuperFast, docUri.AbsoluteUri);
var pages = source.GetFixedDocumentSequence()!.References.First().GetDocument(false)!.Pages;
var documentReference = new DocumentReference();
var fixedDocument = new FixedDocument();
documentReference.SetDocument(fixedDocument);
for (var i = fromPage; i <= toPage; ++i)
{
var page = pages[i];
var pageContentChild = new PageContent {Source = page.Source};
((IUriContext) pageContentChild).BaseUri = ((IUriContext) page).BaseUri;
pageContentChild.GetPageRoot(false);
fixedDocument.Pages.Add(pageContentChild);
}
var documentSequence = new FixedDocumentSequence();
documentSequence.References.Add(documentReference);
var writer = XpsDocument.CreateXpsDocumentWriter(document);
writer.Write(documentSequence);
return document;
}
}
}
+5 -2
View File
@@ -10,15 +10,17 @@ namespace UCalc.Data
{ {
public Tenant Tenant { get; } public Tenant Tenant { get; }
public IReadOnlyDictionary<Cost, CostCalculationResult> Costs { get; } public IReadOnlyDictionary<Cost, CostCalculationResult> Costs { get; }
public decimal SubTotalAmount { get; }
public decimal TotalAmount { get; } public decimal TotalAmount { get; }
public string Details { get; } public string Details { get; }
public string DetailsForLandlord { get; } public string DetailsForLandlord { get; }
public TenantCalculationResult(Tenant tenant, IReadOnlyDictionary<Cost, CostCalculationResult> costs, public TenantCalculationResult(Tenant tenant, IReadOnlyDictionary<Cost, CostCalculationResult> costs,
decimal totalAmount, string details, string detailsForLandlord) decimal subTotalAmount, decimal totalAmount, string details, string detailsForLandlord)
{ {
Tenant = tenant; Tenant = tenant;
Costs = costs; Costs = costs;
SubTotalAmount = subTotalAmount;
TotalAmount = totalAmount; TotalAmount = totalAmount;
Details = details; Details = details;
DetailsForLandlord = detailsForLandlord; DetailsForLandlord = detailsForLandlord;
@@ -616,6 +618,7 @@ namespace UCalc.Data
var str = $"Zwischensumme: {totalAmount.CeilToString()} €\n"; var str = $"Zwischensumme: {totalAmount.CeilToString()} €\n";
str += $"Bereits bezahlt: {tenant.PaidRent.CeilToString()} €\n\n"; str += $"Bereits bezahlt: {tenant.PaidRent.CeilToString()} €\n\n";
var subTotalAmount = totalAmount;
totalAmount -= tenant.PaidRent; totalAmount -= tenant.PaidRent;
totalAmount = totalAmount.Ceil2(); totalAmount = totalAmount.Ceil2();
@@ -624,7 +627,7 @@ namespace UCalc.Data
details.Append(str); details.Append(str);
detailsLandlord.Append(str); detailsLandlord.Append(str);
return new TenantCalculationResult(tenant, costResults, totalAmount, details.ToString(), return new TenantCalculationResult(tenant, costResults, subTotalAmount, totalAmount, details.ToString(),
detailsLandlord.ToString()); detailsLandlord.ToString());
} }
} }
+2 -1
View File
@@ -50,7 +50,8 @@ namespace UCalc.Pages
private void OnAboutClick(object sender, RoutedEventArgs e) private void OnAboutClick(object sender, RoutedEventArgs e)
{ {
throw new System.NotImplementedException(); MessageBox.Show("MietRechner Version 2.0\n\nCopyright © 2020 by Tobias Erbshäußer", "Über MietRechner",
MessageBoxButton.OK, MessageBoxImage.Information);
} }
} }
} }
+272
View File
@@ -0,0 +1,272 @@
<Window x:Class="UCalc.PrintWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UCalc"
xmlns:controls="clr-namespace:UCalc.Controls"
xmlns:drawing="clr-namespace:System.Drawing;assembly=System.Drawing.Primitives"
mc:Ignorable="d"
Title="Druckvorschau"
Width="500"
Height="600"
MinWidth="400"
MinHeight="300"
WindowStartupLocation="CenterOwner"
ShowInTaskbar="False"
Icon="logo.ico">
<DockPanel>
<FlowDocumentScrollViewer x:Name="ScrollViewer"
Visibility="Collapsed">
<FlowDocument x:Name="Document"
PageWidth="{x:Static local:Constants.DinA4Width}"
PageHeight="{x:Static local:Constants.DinA4Height}"
PagePadding="{x:Static local:Constants.DinA4Padding}"
ColumnWidth="{x:Static local:Constants.DinA4ContentWidth}" />
</FlowDocumentScrollViewer>
<DocumentViewer x:Name="Viewer">
<DocumentViewer.Resources>
<Style TargetType="{x:Type DocumentViewer}">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DocumentViewer}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Focusable="False">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.Background>
<SolidColorBrush Color="{DynamicResource ControlLightColor}" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ToolBar ToolBarTray.IsLocked="True"
KeyboardNavigation.TabNavigation="Continue">
<Button PreviewMouseUp="OnPrintClick">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
Margin="4, 4, 0, 4">
<Canvas Width="512" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Drucken"
Margin="4"
VerticalAlignment="Center"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
</Button>
<Separator Background="{x:Static local:Constants.SubMainColor}" />
<Button Click="OnTenantSelectorClick"
Tag="{Binding Path=TenantMenuItems, RelativeSource={RelativeSource AncestorType=local:PrintWindow}}">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
DockPanel.Dock="Right">
<Canvas Width="320" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Mieter auswählen"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
<Button.ContextMenu>
<ContextMenu
ItemsSource="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="Click" Handler="OnTenantMenuItemClick" />
<Setter Property="IsChecked" Value="{Binding Selected}"/>
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Separator Background="{x:Static local:Constants.SubMainColor}" />
<Button Command="NavigationCommands.IncreaseZoom"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
Margin="4, 4, 0, 4">
<Canvas Width="512" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M304 192v32c0 6.6-5.4 12-12 12h-56v56c0 6.6-5.4 12-12 12h-32c-6.6 0-12-5.4-12-12v-56h-56c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h56v-56c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v56h56c6.6 0 12 5.4 12 12zm201 284.7L476.7 505c-9.4 9.4-24.6 9.4-33.9 0L343 405.3c-4.5-4.5-7-10.6-7-17V372c-35.3 27.6-79.7 44-128 44C93.1 416 0 322.9 0 208S93.1 0 208 0s208 93.1 208 208c0 48.3-16.4 92.7-44 128h16.3c6.4 0 12.5 2.5 17 7l99.7 99.7c9.3 9.4 9.3 24.6 0 34zM344 208c0-75.2-60.8-136-136-136S72 132.8 72 208s60.8 136 136 136 136-60.8 136-136z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Vergrößern"
Margin="4"
VerticalAlignment="Center"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
</Button>
<Button Command="NavigationCommands.DecreaseZoom"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
Margin="4, 4, 0, 4">
<Canvas Width="512" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M304 192v32c0 6.6-5.4 12-12 12H124c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm201 284.7L476.7 505c-9.4 9.4-24.6 9.4-33.9 0L343 405.3c-4.5-4.5-7-10.6-7-17V372c-35.3 27.6-79.7 44-128 44C93.1 416 0 322.9 0 208S93.1 0 208 0s208 93.1 208 208c0 48.3-16.4 92.7-44 128h16.3c6.4 0 12.5 2.5 17 7l99.7 99.7c9.3 9.4 9.3 24.6 0 34zM344 208c0-75.2-60.8-136-136-136S72 132.8 72 208s60.8 136 136 136 136-60.8 136-136z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Verkleinern"
Margin="4"
VerticalAlignment="Center"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
</Button>
<Separator Background="{x:Static local:Constants.SubMainColor}" />
<Button Command="DocumentViewer.FitToWidthCommand"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
Margin="4, 4, 0, 4">
<Canvas Width="448" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M432 32H16A16 16 0 0 0 0 48v80a16 16 0 0 0 16 16h32a16 16 0 0 0 16-16v-16h120v112h-24a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-24V112h120v16a16 16 0 0 0 16 16h32a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zm-68.69 260.69C354 283.36 336 288.36 336 304v48H112v-48c0-14.31-17.31-21.32-27.31-11.31l-80 80a16 16 0 0 0 0 22.62l80 80C94 484.64 112 479.64 112 464v-48h224v48c0 14.31 17.31 21.33 27.31 11.31l80-80a16 16 0 0 0 0-22.62z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Ganze Breite"
Margin="4"
VerticalAlignment="Center"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
</Button>
<Button Command="DocumentViewer.FitToMaxPagesAcrossCommand"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="1">
<DockPanel>
<Viewbox Stretch="Uniform"
Width="16"
Height="16"
Margin="4, 4, 0, 4">
<Canvas Width="512" Height="512">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0" />
</Canvas.RenderTransform>
<Path Fill="{x:Static local:Constants.SubMainColor}">
<Path.Data>
<PathGeometry
Figures="M200 288H88c-21.4 0-32.1 25.8-17 41l32.9 31-99.2 99.3c-6.2 6.2-6.2 16.4 0 22.6l25.4 25.4c6.2 6.2 16.4 6.2 22.6 0L152 408l31.1 33c15.1 15.1 40.9 4.4 40.9-17V312c0-13.3-10.7-24-24-24zm112-64h112c21.4 0 32.1-25.9 17-41l-33-31 99.3-99.3c6.2-6.2 6.2-16.4 0-22.6L481.9 4.7c-6.2-6.2-16.4-6.2-22.6 0L360 104l-31.1-33C313.8 55.9 288 66.6 288 88v112c0 13.3 10.7 24 24 24zm96 136l33-31.1c15.1-15.1 4.4-40.9-17-40.9H312c-13.3 0-24 10.7-24 24v112c0 21.4 25.9 32.1 41 17l31-32.9 99.3 99.3c6.2 6.2 16.4 6.2 22.6 0l25.4-25.4c6.2-6.2 6.2-16.4 0-22.6L408 360zM183 71.1L152 104 52.7 4.7c-6.2-6.2-16.4-6.2-22.6 0L4.7 30.1c-6.2 6.2-6.2 16.4 0 22.6L104 152l-33 31.1C55.9 198.2 66.6 224 88 224h112c13.3 0 24-10.7 24-24V88c0-21.3-25.9-32-41-16.9z"
FillRule="NonZero" />
</Path.Data>
</Path>
</Canvas>
</Viewbox>
<Label Content="Ganze Seite"
Margin="4"
VerticalAlignment="Center"
Foreground="{x:Static local:Constants.SubMainColor}" />
</DockPanel>
</Button>
</ToolBar>
<ScrollViewer Grid.Row="1"
CanContentScroll="true"
HorizontalScrollBarVisibility="Auto"
x:Name="PART_ContentHost"
IsTabStop="true">
<ScrollViewer.Background>
<LinearGradientBrush EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1" />
</LinearGradientBrush>
</ScrollViewer.Background>
</ScrollViewer>
<ContentControl Grid.Row="2"
x:Name="PART_FindToolBarHost" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DocumentViewer.Resources>
</DocumentViewer>
</DockPanel>
</Window>
+345
View File
@@ -0,0 +1,345 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
using UCalc.Annotations;
using UCalc.Controls;
using UCalc.Data;
using UCalc.Models;
namespace UCalc
{
public class TenantMenuItem : INotifyPropertyChanged
{
public Tenant Tenant { get; }
public bool None { get; }
private bool _selected;
public bool Selected
{
get => _selected;
set
{
if (value == _selected)
{
return;
}
_selected = value;
OnPropertyChanged();
}
}
public TenantMenuItem(Tenant tenant, bool none)
{
Tenant = tenant;
None = none;
Selected = tenant != null;
}
public string Name => Tenant != null ? Tenant.Name : None ? "Kein Mieter" : "Alle Mieter";
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class PrintWindow
{
public Billing Billing { get; }
public IReadOnlyList<TenantMenuItem> TenantMenuItems { get; }
private PrintableDocument _document;
public PrintWindow(Model model)
{
Billing = model.Dump();
TenantMenuItems = new[] {new TenantMenuItem(null, false), new TenantMenuItem(null, true)}.Concat(
Billing.Tenants.Select(tenant => new TenantMenuItem(tenant, false))).ToList();
foreach (var item in TenantMenuItems)
{
item.PropertyChanged += OnSelectedTenantChanged;
}
InitializeComponent();
Preview();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_document?.Dispose();
}
private void Preview()
{
Document.Blocks.Clear();
foreach (var item in TenantMenuItems)
{
if (!item.Selected)
{
continue;
}
var result = BillingCalculator.CalculateForTenant(Billing, item.Tenant);
PreviewForSingleTenant(item.Tenant, result);
}
_document?.Dispose();
_document = new PrintableDocument(Document);
_document.PreviewIn(Viewer);
}
private static void AddLineBreaks(TableRowGroup rowGroup, int count)
{
var row = new TableRow();
rowGroup.Rows.Add(row);
var cell = new TableCell {ColumnSpan = 2};
row.Cells.Add(cell);
cell.Blocks.Add(new Paragraph(new Run(new string('\n', count))
{FontSize = Constants.PrintNewlineFontSize}));
}
private static void AddText(TableRowGroup rowGroup, string text, double? fontSize = null,
bool alignRight = false)
{
var row = new TableRow();
rowGroup.Rows.Add(row);
var cell = new TableCell {ColumnSpan = 2};
row.Cells.Add(cell);
if (alignRight)
{
cell.TextAlignment = TextAlignment.Right;
}
var paragraph = new Paragraph(new Run(text));
cell.Blocks.Add(paragraph);
if (fontSize != null)
{
paragraph.FontSize = fontSize.Value;
}
}
private void PreviewForSingleTenant(Tenant tenant, TenantCalculationResult result)
{
var section = new Section {BreakPageBefore = true, FontSize = Constants.PrintDefaultFontSize};
Document.Blocks.Add(section);
var table = new Table {CellSpacing = 0};
section.Blocks.Add(table);
table.Columns.Add(new TableColumn {Width = new GridLength(1, GridUnitType.Star)});
table.Columns.Add(new TableColumn {Width = new GridLength(1, GridUnitType.Star)});
var rowGroup = new TableRowGroup();
table.RowGroups.Add(rowGroup);
void AddLineBreaks(int count)
{
PrintWindow.AddLineBreaks(rowGroup, count);
}
void AddText(string text, double? fontSize = null, bool alignRight = false)
{
PrintWindow.AddText(rowGroup, text, fontSize, alignRight);
}
void AddCost(string name, decimal amount, bool isLast = false)
{
var row2 = new TableRow();
rowGroup.Rows.Add(row2);
if (isLast)
{
row2.Background = Brushes.LightGray;
}
var cell2 = new TableCell
{BorderBrush = Brushes.Gray, BorderThickness = new Thickness(1, 1, 0, isLast ? 1 : 0)};
row2.Cells.Add(cell2);
cell2.Blocks.Add(new Paragraph(new Run(name)) {Padding = new Thickness(6)});
cell2 = new TableCell
{
BorderBrush = Brushes.Gray, BorderThickness = new Thickness(1, 1, 1, isLast ? 1 : 0),
TextAlignment = TextAlignment.Right
};
row2.Cells.Add(cell2);
cell2.Blocks.Add(new Paragraph(new Run($"{amount.CeilToString()} €")) {Padding = new Thickness(6)});
}
AddText(
$"{Billing.Landlord.Salutation.AsString()} {Billing.Landlord.Name}\n{DateTime.Now.ToString(Constants.DateFormat)}\n\n{Billing.Landlord.Address.Street} {Billing.Landlord.Address.HouseNumber}\n{Billing.Landlord.Address.Postcode} {Billing.Landlord.Address.City}\nTelefon: {Billing.Landlord.Phone}\nEmail: {Billing.Landlord.MailAddress}");
AddLineBreaks(2);
AddText(
$"{tenant.Salutation.AsString()} {tenant.Name}\n{Billing.House.Address.Street} {Billing.House.Address.HouseNumber}\n{Billing.House.Address.Postcode} {Billing.House.Address.City}",
null, true);
AddLineBreaks(4);
AddText($"{tenant.Salutation.AsString()} {tenant.Name}");
AddLineBreaks(1);
AddText(
$"Nebenkostenabrechnung vom {Billing.StartDate.ToString(Constants.DateFormat)} zum {Billing.EndDate.ToString(Constants.DateFormat)}",
Constants.PrintSubjectFontSize);
AddLineBreaks(1);
if (!string.IsNullOrEmpty(tenant.CustomMessage1))
{
AddText(tenant.CustomMessage1);
AddLineBreaks(1);
}
foreach (var (cost, costResult) in result.Costs)
{
AddCost(cost.Name, costResult.TotalAmount);
}
AddCost("Zwischensumme", result.SubTotalAmount);
AddCost("Bereits gezahlt", tenant.PaidRent);
AddCost(result.TotalAmount > 0 ? "Einmalige Nachzahlung" : "Einmalige Rückzahlung", result.TotalAmount,
true);
AddLineBreaks(2);
if (result.TotalAmount > 0)
{
AddText(
$"Bitte überweisen Sie den einmaligen Betrag von {result.TotalAmount.CeilToString()} € auf das untenstehende Konto.");
}
else
{
AddText(
$"Der einmalige Betrag von {result.TotalAmount.CeilToString()} € wird in den nächsten Tagen auf Ihr Konto überwiesen.");
}
AddLineBreaks(1);
if (!string.IsNullOrEmpty(tenant.CustomMessage2))
{
AddText(tenant.CustomMessage2);
AddLineBreaks(1);
}
AddText("Kontoverbindung:");
AddText($"IBAN: {Billing.Landlord.BankAccount.Iban}");
AddText($"BIC: {Billing.Landlord.BankAccount.Bic}");
AddText($"Name der Bank: {Billing.Landlord.BankAccount.BankName}");
AddLineBreaks(2);
AddText("Mit freundlichen Grüßen");
if (result.Costs.Any(t => t.Key.DisplayInBill))
{
PreviewForSingleTenantDetails(result);
}
}
private void PreviewForSingleTenantDetails(TenantCalculationResult result)
{
var section = new Section {BreakPageBefore = true, FontSize = Constants.PrintDefaultFontSize};
Document.Blocks.Add(section);
var table = new Table {CellSpacing = 0};
section.Blocks.Add(table);
table.Columns.Add(new TableColumn {Width = new GridLength(1, GridUnitType.Star)});
table.Columns.Add(new TableColumn {Width = new GridLength(1, GridUnitType.Star)});
var rowGroup = new TableRowGroup();
table.RowGroups.Add(rowGroup);
void AddLineBreaks(int count)
{
PrintWindow.AddLineBreaks(rowGroup, count);
}
void AddText(string text, double? fontSize = null, bool alignRight = false)
{
PrintWindow.AddText(rowGroup, text, fontSize, alignRight);
}
AddText("Details zur Berechnung:");
AddLineBreaks(1);
foreach (var (cost, costResult) in result.Costs)
{
if (!cost.DisplayInBill)
{
continue;
}
AddText(costResult.Details);
}
}
private void OnPrintClick(object sender, RoutedEventArgs e)
{
_document.Print(
$"MietRechner Abrechnung {Billing.StartDate.ToString(Constants.DateFormat)} - {Billing.EndDate.ToString(Constants.DateFormat)}");
}
private void OnSelectedTenantChanged(object sender, PropertyChangedEventArgs e)
{
Preview();
}
private void OnTenantSelectorClick(object sender, RoutedEventArgs e)
{
var button = (Button) sender;
var contextMenu = button.ContextMenu;
// ReSharper disable once PossibleNullReferenceException
contextMenu.PlacementTarget = button;
contextMenu.Placement = PlacementMode.Bottom;
contextMenu.IsOpen = true;
e.Handled = true;
}
private void OnTenantMenuItemClick(object sender, RoutedEventArgs e)
{
var item = (TenantMenuItem) ((MenuItem) sender).DataContext;
if (item.Tenant == null)
{
foreach (var item2 in TenantMenuItems)
{
if (item2.Tenant != null)
{
item2.Selected = !item.None;
}
}
}
else
{
item.Selected = !item.Selected;
}
}
}
}
+6
View File
@@ -49,6 +49,9 @@
<Page Update="CostEntryDetailsWindow.xaml"> <Page Update="CostEntryDetailsWindow.xaml">
<Generator></Generator> <Generator></Generator>
</Page> </Page>
<Page Update="PrintWindow.xaml">
<Generator></Generator>
</Page>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -94,6 +97,9 @@
<Compile Update="CostEntryDetailsWindow.xaml.cs"> <Compile Update="CostEntryDetailsWindow.xaml.cs">
<DependentUpon>CostEntryDetailsWindow.xaml</DependentUpon> <DependentUpon>CostEntryDetailsWindow.xaml</DependentUpon>
</Compile> </Compile>
<Compile Update="PrintWindow.xaml.cs">
<DependentUpon>PrintWindow.xaml</DependentUpon>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>