Compare commits

..

2 Commits

Author SHA1 Message Date
4dd036babd ChartWindow: wip 2024-01-17 22:33:20 +01:00
b6fd62f8ca ChartWindow: Use BillingData and Curve 2024-01-17 22:08:36 +01:00
4 changed files with 273 additions and 469 deletions

View File

@@ -3,117 +3,106 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Windows.Documents;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class Graph : ICloneable { public class Graph : ICloneable {
public string Type { get; set; }
public int Num { get; set; }
private int MinX { get; set; }
private int MaxX { get; set; }
public string Contracts { get; set; }
public double[] DataX { get; set; } public double[] DataX { get; set; }
public double[] DataY { get; set; } public double[] DataY { get; set; }
public Graph(int num, int minX, int maxX) { public Graph(int minX, int maxX) {
Type = "oe"; DataX = DataGen.Range(minX, maxX + 1);
Num = num; DataY = DataGen.Zeros(maxX - minX + 1);
Contracts = "";
MinX = minX;
MaxX = maxX;
DataX = DataGen.Range(MinX, MaxX + 1);
DataY = DataGen.Zeros(MaxX - MinX + 1);
} }
public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) { public Graph(Dictionary<double, decimal> data, int minX, int maxX) {
Type = type; DataX = DataGen.Range(minX, maxX + 1);
Num = num; DataY = DataGen.Zeros(maxX - minX + 1);
Contracts = contracts; ParseGraphData(data, minX, maxX);
MinX = minX;
MaxX = maxX;
DataX = DataGen.Range(MinX, MaxX + 1);
DataY = DataGen.Zeros(MaxX - MinX + 1);
ParseGraphData(graphData);
} }
public Graph(string type, int num, int minX, int maxX, string contracts, double[] dataX, double[] dataY) { public Graph(double[] dataX, double[] dataY) {
Type = type;
Num = num;
MinX = minX;
MaxX = maxX;
Contracts = contracts;
DataX = dataX; DataX = dataX;
DataY = dataY; DataY = dataY;
} }
private void ParseGraphData(JsonObject graphData) { private void ParseGraphData(Dictionary<double, decimal> graphPoints, int minX, int maxX) {
var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue()); if (graphPoints.Keys.Count < 1) {
if (GraphPoints.Keys.Count < 1) {
return; return;
} }
var minKey = GraphPoints.Keys.Order().First(); var minKey = graphPoints.Keys.Order().First();
var maxKey = GraphPoints.Keys.OrderDescending().First(); var maxKey = graphPoints.Keys.OrderDescending().First();
if (!GraphPoints.ContainsKey(MinX)) { if (!graphPoints.ContainsKey(minX)) {
GraphPoints.Add(MinX, GraphPoints.GetValueOrDefault(minKey)); graphPoints.Add(minX, graphPoints.GetValueOrDefault(minKey));
} }
if (!GraphPoints.ContainsKey(MaxX)) { if (!graphPoints.ContainsKey(maxX)) {
GraphPoints.Add(MaxX, GraphPoints.GetValueOrDefault(maxKey)); graphPoints.Add(maxX, graphPoints.GetValueOrDefault(maxKey));
} }
var keys = GraphPoints.Keys.Order().ToArray(); var keys = graphPoints.Keys.Order().ToArray();
for (int i = 0; i < keys.Length; i++) { for (int i = 0; i < keys.Length; i++) {
double point1Value = GraphPoints[keys[i]]; decimal point1Value = graphPoints[keys[i]];
if (i + 1 < keys.Length) { if (i + 1 < keys.Length) {
double point2Value = GraphPoints[keys[i + 1]]; decimal point2Value = graphPoints[keys[i + 1]];
if (point1Value == point2Value) { if (point1Value == point2Value) {
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX; j++) { for (int j = (int)(keys[i] - minX); j < keys[i + 1] - minX; j++) {
DataY[j] = point1Value; DataY[j] = (double)point1Value;
} }
} else { } else {
int steps = Math.Abs(keys[i + 1] - keys[i]); int steps = (int)Math.Abs(keys[i + 1] - keys[i]);
double step = (point2Value - point1Value) / steps; decimal step = (point2Value - point1Value) / steps;
DataY[keys[i] - MinX] = point1Value; DataY[(int)(keys[i] - minX)] = (double)point1Value;
DataY[keys[i + 1] - MinX] = point2Value; DataY[(int)(keys[i + 1] - minX)] = (double)point2Value;
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX - 1; j++) { for (int j = (int)(keys[i] - minX); j < keys[i + 1] - minX - 1; j++) {
DataY[j + 1] = Math.Round(DataY[j] + step, 4); // TODO richtig runden DataY[j + 1] = Math.Round(DataY[j] + (double)step, 4); // TODO richtig runden
} }
} }
} }
else { else {
for (int j = keys[i] - MinX; j < DataX.Length; j++) { for (int j = (int)(keys[i] - minX); j < DataX.Length; j++) {
DataY[j] = point1Value; DataY[j] = (double)point1Value;
} }
} }
} }
} }
public JsonObject ToJson() { public void FlattenGraph(int begin, int end, double value) {
JsonObject graph = new(); for (int i = begin; i <= end; i++) {
DataY[i] = value;
}
}
public void LinearIncreaseGraph(int begin, int end, double inc) {
for (int i = begin; i < end; i++) {
DataY[i + 1] = DataY[i] + inc;
}
}
public JsonObject ToJson(string mode) {
var data = new JsonObject();
if (DataY[0] != DataY[1]) { if (DataY[0] != DataY[1]) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + mode, Math.Round(DataY[0], 4)));
} }
for (int i = 1; i < DataX.Length - 1; i++) { for (int i = 1; i < DataX.Length - 1; i++) {
if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) { if (Math.Round(DataY[i] - DataY[i - 1], 10) != Math.Round(DataY[i + 1] - DataY[i], 10)) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + mode, Math.Round(DataY[i], 4)));
} }
} }
if (DataY[^1] != DataY[^2]) { if (DataY[^1] != DataY[^2]) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + mode, Math.Round(DataY[^1], 4)));
} }
return graph; return data;
} }
public object Clone() { public object Clone() {
return new Graph(Type, Num, MinX, MaxX, Contracts, (double[])DataX.Clone(), (double[])DataY.Clone()); return new Graph((double[])DataX.Clone(), (double[])DataY.Clone());
} }
} }
} }

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace Elwig.Helpers.Billing {
public class GraphEntry {
public int Id { get; set; }
public BillingData.CurveMode Mode { get; set; }
public Graph DataGraph { get; set; }
public Graph? GebundenGraph { get; set; }
public decimal? GebundenFlatPrice { get; set; }
public List<string> Contracts { get; set; }
private int MinX { get; set; }
private int MaxX { get; set; }
public GraphEntry(int id, BillingData.CurveMode mode, int minX, int maxX) {
Id = id;
Mode = mode;
MinX = minX;
MaxX = maxX;
DataGraph = new Graph(minX, maxX);
Contracts = [];
}
public GraphEntry(int id, BillingData.CurveMode mode, Dictionary<double, decimal> data, int minX, int maxX) : this(id, mode, minX, maxX) {
DataGraph = new Graph(data, minX, maxX);
}
private GraphEntry(int id, BillingData.CurveMode mode, Graph dataGraph, Graph? gebundenGraph,
decimal? gebundenFlatPrice, List<string> contracts, int minX, int maxX) {
Id = id;
Mode = mode;
MinX = minX;
MaxX = maxX;
DataGraph = dataGraph;
GebundenGraph = gebundenGraph;
GebundenFlatPrice = gebundenFlatPrice;
Contracts = contracts;
}
public JsonObject ToJson() {
var curve = new JsonObject {
["id"] = Id,
["mode"] = Mode.ToString().ToLower(),
};
curve["data"] = DataGraph.ToJson(Mode.ToString().ToLower());
if (GebundenFlatPrice != null) {
curve["geb"] = GebundenFlatPrice.ToString();
} else if (GebundenGraph != null) {
curve["geb"] = GebundenGraph.ToJson(Mode.ToString().ToLower());
}
return curve;
}
public GraphEntry Copy(int id) {
return new GraphEntry(id, Mode, (Graph)DataGraph.Clone(), (Graph?)GebundenGraph?.Clone(), GebundenFlatPrice, Contracts, MinX, MaxX);
}
}
}

View File

@@ -1,4 +1,4 @@
<local:AdministrationWindow <local:ContextWindow
x:Class="Elwig.Windows.ChartWindow" x:Class="Elwig.Windows.ChartWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -8,7 +8,7 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF" xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF"
mc:Ignorable="d" mc:Ignorable="d"
Title="Auszahlung - Elwig" Height="700" Width="1500" Title="Auszahlung - Elwig" Height="700" Width="1500" MinWidth="1000" MinHeight="500"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
@@ -43,94 +43,77 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="19"/> <RowDefinition Height="40"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="330"/> <ColumnDefinition Width="230"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/>
<ColumnDefinition Width="200"/> <ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Grid.Row="1" Margin="5,0,0,0"> <Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="500"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<DataGrid x:Name="GraphList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single" <Label Content="Graph:" Margin="10,0,0,0" FontSize="14" Grid.Column="0" VerticalAlignment="Center"/>
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" <TextBlock x:Name="GraphNum" Margin="0,0,40,0" FontSize="14" Width="50" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right"/>
SelectionChanged="GraphList_SelectionChanged"
Margin="5,15,5,0" Grid.Row="0" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns>
<DataGridTextColumn Header="Nr." Binding="{Binding Num}" Width="40">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="40"/>
<DataGridTextColumn Header="Angewandte Verträge" Binding="{Binding Contracts}" Width="4*"/>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewButton" Content="Neu" <Label Content="Für:" Margin="10,0,0,0" FontSize="14" Grid.Column="1" VerticalAlignment="Center"/>
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2" <xctk:CheckComboBox x:Name="AppliedInput" Margin="0,10,10,10" Grid.Column="1"
Click="NewButton_Click"/> Delimiter=", " AllItemsSelectedContent="Alle"
<Button x:Name="EditButton" Content="Bearbeiten" IsEnabled="False" Width="400" HorizontalAlignment="Right">
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2" <!--<xctk:CheckComboBox.ItemTemplate>
Click="EditButton_Click"/> <DataTemplate>
<Button x:Name="DeleteButton" Content="Löschen" IsEnabled="False" <StackPanel Orientation="Horizontal">
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2" <TextBlock Text="{}" Width="40"/>
Click="DeleteButton_Click"/> </StackPanel>
</DataTemplate>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden" </xctk:CheckComboBox.ItemTemplate>-->
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2" </xctk:CheckComboBox>
Click="SaveButton_Click"/>
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="ResetButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="CancelButton_Click"/>
</Grid> </Grid>
<ListBox x:Name="GraphList" Margin="10,10,35,50" Grid.Column="0" Grid.Row="1" SelectionChanged="GraphList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" Width="40"/>
<TextBlock Text="{Binding Contracts}" Width="100"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="10,5,35,15" Grid.Column="0" Grid.Row="2"
Click="SaveButton_Click"/>
<Button x:Name="AddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" ToolTip="Neue Auszahlungsvariante hinzufügen"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,60" Grid.Column="0" Grid.Row="1"
Click="AddButton_Click"/>
<Button x:Name="CopyButton" Content="&#xE8C8;" FontFamily="Segoe MDL2 Assets" FontSize="12" Padding="0,0,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante duplizieren"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="0,0,5,0" Grid.Column="0" Grid.Row="1"
Click="CopyButton_Click"/>
<Button x:Name="DeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante löschen"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,60,5,0" Grid.Column="0" Grid.Row="1"
Click="DeleteButton_Click"/>
<Grid Grid.Row="1" Grid.Column="1"> <Grid Grid.Row="1" Grid.Column="1">
<ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/> <ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/>
</Grid> </Grid>
<Grid Grid.Row="1" Grid.Column="2" Margin="0,0,5,0"> <Grid Grid.Row="1" Grid.Column="2" Margin="0,0,5,36">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="120"/> <RowDefinition Height="120"/>
<RowDefinition Height="120"/> <RowDefinition Height="90"/>
<RowDefinition Height="210"/> <RowDefinition Height="210"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="110"/> <RowDefinition Height="110"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<GroupBox Header="Graph" Grid.Row="0" Margin="0,5,5,5"> <GroupBox Header="Datenpunkt" Grid.Row="0" Margin="0,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Nummer:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="GraphNumberInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" Text="" Width="90" TextChanged="GraphNumberInput_TextChanged" LostFocus="GraphNumberInput_LostFocus"/>
<Label Content="Typ:" Margin="10,45,0,0" Grid.Column="0"/>
<RadioButton x:Name="OechsleGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,45,0,0">Oechsle</RadioButton>
<RadioButton x:Name="KmwGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,60,0,0">KMW</RadioButton>
</Grid>
</GroupBox>
<GroupBox Header="Datenpunkt" Grid.Row="1" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/> <ColumnDefinition Width="85"/>
@@ -146,6 +129,23 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Gebunden" Grid.Row="1" Margin="0,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="10,10,0,0">
<RadioButton GroupName="GebundenType">Fix</RadioButton>
<RadioButton GroupName="GebundenType">Graph</RadioButton>
<RadioButton GroupName="GebundenType" IsChecked="True">Nein</RadioButton>
</StackPanel>
<TextBox x:Name="GebundenBonus" IsReadOnly="True" Grid.Column="1" HorizontalAlignment="Left" Margin="0,12,0,0" Text="" Width="90" TextChanged="GebundenBonus_TextChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5"> <GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -166,7 +166,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Optionen" Grid.Row="3" Margin="0,5,5,5"> <GroupBox Header="Optionen" Grid.Row="4" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -186,4 +186,4 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
</Grid> </Grid>
</local:AdministrationWindow> </local:ContextWindow>

View File

@@ -17,10 +17,11 @@ using ScottPlot;
using ScottPlot.Plottable; using ScottPlot.Plottable;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class ChartWindow : AdministrationWindow { public partial class ChartWindow : ContextWindow {
public readonly int Year; public readonly int Year;
public readonly int AvNr; public readonly int AvNr;
private readonly PaymentVar PaymentVar;
private ScatterPlot OechslePricePlotScatter; private ScatterPlot OechslePricePlotScatter;
private MarkerPlot HighlightedPoint; private MarkerPlot HighlightedPoint;
@@ -38,21 +39,18 @@ namespace Elwig.Windows {
private const int MinOechsle = 50; private const int MinOechsle = 50;
private const int MaxOechsle = 140; private const int MaxOechsle = 140;
private Graph? Graph; private List<GraphEntry> GraphEntries = [];
private GraphEntry? SelectedGraphEntry;
public ChartWindow(int year, int avnr) { public ChartWindow(int year, int avnr) {
InitializeComponent(); InitializeComponent();
Year = year; Year = year;
AvNr = avnr; AvNr = avnr;
var v = Context.PaymentVariants.Find(year, avnr); PaymentVar = Context.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
Title = $"{v?.Name} - Lese {year} - Elwig"; Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
ExemptInputs = [
GraphList, OechsleInput, PriceInput, FreeZoomInput, GradationLinesInput, TooltipInput
];
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
LockInputs();
OechslePricePlot.IsEnabled = false; OechslePricePlot.IsEnabled = false;
} }
@@ -70,32 +68,27 @@ namespace Elwig.Windows {
} }
private async Task RefreshGraphListQuery(bool updateSort = false) { private async Task RefreshGraphListQuery(bool updateSort = false) {
var paymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr); var data = ParseData(PaymentVar);
if (paymentVar == null) return;
var data = ParseData(paymentVar);
if (data == null) return; if (data == null) return;
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject(); var curves = BillingData.GetCurves(data, BillingData.CalculationMode.Elwig);
if (auszahlungsSorten == null) {
return; foreach (var (id, curve) in curves) {
GraphEntries.Add(new GraphEntry(id, curve.Mode, curve.Normal, MinOechsle, MaxOechsle));
} }
var Graphs = auszahlungsSorten["Kurven"]?.AsArray(); var attrVariants = Context.DeliveryParts
.Where(d => d.Year == Year)
.Select(d => $"{d.SortId}{d.AttrId}")
.Distinct()
.ToList()
.Union(Context.WineVarieties.Select(v => v.SortId))
.Order()
.ToList();
ControlUtils.RenewItemsSource(AppliedInput, attrVariants, g => (g as GraphEntry)?.Id);
if (Graphs == null) { ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.Id);
return; if (GraphEntries.Count == 1) {
}
List<Graph> GraphsList = [];
int i = 1;
foreach (var graph in Graphs) {
GraphsList.Add(new Graph("Oe", i, graph?.AsObject(), ParseContracts(auszahlungsSorten, i - 1), 50, 140));
i++;
}
ControlUtils.RenewItemsSource(GraphList, GraphsList, g => (g as Graph)?.Num);
if (GraphsList.Count == 1) {
GraphList.SelectedIndex = 0; GraphList.SelectedIndex = 0;
} }
@@ -103,128 +96,46 @@ namespace Elwig.Windows {
} }
private string ParseContracts(JsonObject auszahlungsSorten, int num) { private string ParseContracts(JsonObject auszahlungsSorten, int num) {
List<string> contracts = []; return "";
foreach (var sorte in auszahlungsSorten) {
if (sorte.Key == "Kurven") continue;
foreach (var attribut in sorte.Value.AsObject()) {
foreach (var bindung in attribut.Value.AsObject()) {
if ((int)bindung.Value.AsValue() == num) {
contracts.Add($"{sorte.Key}/{attribut.Key}/{bindung.Key}");
}
}
}
}
return string.Join("\n", contracts);
} }
private async Task<bool> RemoveGraph(int num) {
var paymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr);
if (paymentVar == null) return false;
var data = ParseData(paymentVar);
if (data == null) return false;
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) {
return false;
}
var Graphs = auszahlungsSorten["Kurven"]?.AsObject();
if (Graphs == null) {
return false;
}
int i = 1;
foreach (var graph in Graphs) {
if (i == num) {
Graphs.Remove(graph.Key);
break;
}
i++;
}
foreach (var sorte in auszahlungsSorten) {
if (sorte.Key == "Kurven") continue;
foreach (var attribut in sorte.Value.AsObject()) {
var bindungen = attribut.Value.AsObject();
foreach (var bindung in bindungen) {
int v = (int)bindung.Value;
if (v == num - 1) {
bindungen.Remove(bindung.Key);
} else if (v > num - 1) {
bindungen[bindung.Key] = v - 1;
}
}
}
}
EntityEntry<PaymentVar>? tr = null;
try {
paymentVar.Data = data.ToString();
tr = Context.Update(paymentVar);
await Context.SaveChangesAsync();
} catch (Exception exc) {
if (tr != null) await tr.ReloadAsync();
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
return true;
}
private void RefreshInputs(bool validate = false) { private void RefreshInputs(bool validate = false) {
ResetPlot(); ResetPlot();
ClearInputStates(); if (!PaymentVar.TestVariant) {
if (GraphList.SelectedItem is Graph g) { AddButton.IsEnabled = false;
EditButton.IsEnabled = true; CopyButton.IsEnabled = false;
DeleteButton.IsEnabled = true;
EnableOptionButtons();
FillInputs(g);
} else {
EditButton.IsEnabled = false;
DeleteButton.IsEnabled = false; DeleteButton.IsEnabled = false;
OechsleInput.IsReadOnly = true;
PriceInput.IsReadOnly = true;
} else if (SelectedGraphEntry != null) {
CopyButton.IsEnabled = true;
DeleteButton.IsEnabled = true;
OechsleInput.IsReadOnly = false;
EnableOptionButtons();
FillInputs();
} else {
CopyButton.IsEnabled = false;
DeleteButton.IsEnabled = false;
OechsleInput.IsReadOnly = true;
DisableOptionButtons(); DisableOptionButtons();
ClearOriginalValues();
ClearInputs(validate);
ClearInputStates();
} }
GC.Collect(); GC.Collect();
} }
private void FillInputs(Graph g) { private void FillInputs() {
ClearOriginalValues(); GraphNum.Text = SelectedGraphEntry.Id.ToString();
Graph = (Graph)g.Clone();
GraphNumberInput.Text = Graph.Num.ToString();
if (Graph.Type == "oe") {
OechsleGraphType_Input.IsChecked = true;
} else if (Graph.Type == "kmw") {
KmwGraphType_Input.IsChecked = true;
}
InitPlot(); InitPlot();
OechslePricePlot.IsEnabled = true; OechslePricePlot.IsEnabled = true;
FinishInputFilling();
}
private void InitInputs() {
GraphNumberInput.Text = (GraphList.Items.Count + 1).ToString();
OechsleGraphType_Input.IsChecked = true;
FinishInputFilling();
} }
protected override async Task OnRenewContext() { protected override async Task OnRenewContext() {
await base.OnRenewContext();
await RefreshGraphList(); await RefreshGraphList();
} }
private void InitPlot() { private void InitPlot() {
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY); OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(SelectedGraphEntry.DataGraph.DataX, SelectedGraphEntry.DataGraph.DataY);
OechslePricePlot.Configuration.DoubleClickBenchmark = false; OechslePricePlot.Configuration.DoubleClickBenchmark = false;
OechslePricePlotScatter.LineColor = Color.Blue; OechslePricePlotScatter.LineColor = Color.Blue;
@@ -265,7 +176,6 @@ namespace Elwig.Windows {
} }
private void ResetPlot() { private void ResetPlot() {
Graph = null;
PrimaryMarkedPointIndex = -1; PrimaryMarkedPointIndex = -1;
OechslePricePlot.Plot.Remove(OechslePricePlotScatter); OechslePricePlot.Plot.Remove(OechslePricePlotScatter);
OechslePricePlot.Plot.Clear(); OechslePricePlot.Plot.Clear();
@@ -280,16 +190,12 @@ namespace Elwig.Windows {
} }
private void FlattenGraph(int begin, int end, double value) { private void FlattenGraph(int begin, int end, double value) {
for (int i = begin; i <= end; i++) { SelectedGraphEntry.DataGraph.FlattenGraph(begin, end, value);
Graph.DataY[i] = value;
}
OechslePricePlot.Render(); OechslePricePlot.Render();
} }
private void LinearIncreaseGraph(int begin, int end, double inc) { private void LinearIncreaseGraph(int begin, int end, double inc) {
for (int i = begin; i < end; i++) { SelectedGraphEntry.DataGraph.LinearIncreaseGraph(begin, end, inc);
Graph.DataY[i + 1] = Graph.DataY[i] + inc;
}
OechslePricePlot.Render(); OechslePricePlot.Render();
} }
@@ -380,8 +286,6 @@ namespace Elwig.Windows {
} }
private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) { private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) {
IntegerInput_TextChanged(sender, evt);
bool success = int.TryParse(OechsleInput.Text, out int oechsle); bool success = int.TryParse(OechsleInput.Text, out int oechsle);
SecondaryMarkedPointIndex = -1; SecondaryMarkedPointIndex = -1;
@@ -390,12 +294,13 @@ namespace Elwig.Windows {
if (success) { if (success) {
if (oechsle >= MinOechsle && oechsle <= MaxOechsle) { if (oechsle >= MinOechsle && oechsle <= MaxOechsle) {
PrimaryMarkedPointIndex = oechsle - MinOechsle; PrimaryMarkedPointIndex = oechsle - MinOechsle;
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]); ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString(); PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex].ToString();
if (IsEditing || IsCreating) EnableActionButtons(); EnableActionButtons();
OechslePricePlot.Render(); OechslePricePlot.Render();
PriceInput.IsReadOnly = false;
return; return;
} }
} }
@@ -405,6 +310,7 @@ namespace Elwig.Windows {
DisableActionButtons(); DisableActionButtons();
PriceInput.Text = ""; PriceInput.Text = "";
OechslePricePlot.Render(); OechslePricePlot.Render();
PriceInput.IsReadOnly = true;
} }
private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) { private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) {
@@ -412,10 +318,8 @@ namespace Elwig.Windows {
bool success = Double.TryParse(PriceInput.Text, out double price); bool success = Double.TryParse(PriceInput.Text, out double price);
if (success) { if (success) {
Graph.DataY[PrimaryMarkedPointIndex] = price; SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex] = price;
PrimaryMarkedPoint.Y = price; PrimaryMarkedPoint.Y = price;
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
OechslePricePlot.Refresh(); OechslePricePlot.Refresh();
} }
} }
@@ -425,18 +329,14 @@ namespace Elwig.Windows {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(0, PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) { private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) { private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
@@ -446,13 +346,11 @@ namespace Elwig.Windows {
} }
var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex); var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex);
double step = (Graph.DataY[highIndex] - Graph.DataY[lowIndex]) / steps; double step = (SelectedGraphEntry.DataGraph.DataY[highIndex] - SelectedGraphEntry.DataGraph.DataY[lowIndex]) / steps;
for (int i = lowIndex; i < highIndex - 1; i++) { for (int i = lowIndex; i < highIndex - 1; i++) {
Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden SelectedGraphEntry.DataGraph.DataY[i + 1] = Math.Round(SelectedGraphEntry.DataGraph.DataY[i] + step, 4); // TODO richtig runden
} }
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) { private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
@@ -463,23 +361,21 @@ namespace Elwig.Windows {
if (priceIncrease == null) { if (priceIncrease == null) {
return; return;
} }
LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value); LinearIncreaseGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, priceIncrease.Value);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
if (!IsCreating && GraphList.SelectedItem == null) { if (GraphList.SelectedItem == null) {
return; return;
} }
if (HoverActive) { if (HoverActive) {
if ((IsEditing || IsCreating) && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (PaymentVar.TestVariant && Keyboard.IsKeyDown(Key.LeftCtrl)) {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
SecondaryMarkedPointIndex = HighlightedIndex; SecondaryMarkedPointIndex = HighlightedIndex;
ChangeMarker(SecondaryMarkedPoint, true, Graph.DataX[SecondaryMarkedPointIndex], Graph.DataY[SecondaryMarkedPointIndex]); ChangeMarker(SecondaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[SecondaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[SecondaryMarkedPointIndex]);
InterpolateButton.IsEnabled = true; InterpolateButton.IsEnabled = true;
@@ -487,12 +383,12 @@ namespace Elwig.Windows {
} }
PrimaryMarkedPointIndex = HighlightedIndex; PrimaryMarkedPointIndex = HighlightedIndex;
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]); ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
OechsleInput.Text = Graph.DataX[HighlightedIndex].ToString(); OechsleInput.Text = SelectedGraphEntry.DataGraph.DataX[HighlightedIndex].ToString();
PriceInput.Text = Graph.DataY[HighlightedIndex].ToString(); PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[HighlightedIndex].ToString();
if (IsEditing || IsCreating) { if (PaymentVar.TestVariant) {
EnableActionButtons(); EnableActionButtons();
} }
} else { } else {
@@ -509,7 +405,7 @@ namespace Elwig.Windows {
} }
private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) {
if (!IsCreating && GraphList.SelectedItem == null) { if (GraphList.SelectedItem == null) {
return; return;
} }
@@ -546,188 +442,45 @@ namespace Elwig.Windows {
} }
} }
override protected void UpdateButtons() { private int getMaxGraphId() {
if (!IsEditing && !IsCreating) return; return GraphEntries.Count == 0 ? 0 : GraphEntries.Select(g => g.Id).Max();
bool ch = HasChanged, v = IsValid;
} }
private void DisableNewEditDeleteButtons() { private void AddButton_Click(object sender, RoutedEventArgs e) {
NewButton.IsEnabled = false; GraphEntry newGraphEntry = new(getMaxGraphId() + 1, BillingData.CurveMode.Oe, MinOechsle, MaxOechsle);
EditButton.IsEnabled = false; GraphEntries.Add(newGraphEntry);
DeleteButton.IsEnabled = false; GraphList.Items.Refresh();
GraphList.SelectedItem = newGraphEntry;
} }
private void EnableNewEditDeleteButtons() { private void CopyButton_Click(object sender, RoutedEventArgs e) {
NewButton.IsEnabled = true; if (SelectedGraphEntry == null) return;
EditButton.IsEnabled = GraphList.SelectedItem != null;
DeleteButton.IsEnabled = GraphList.SelectedItem != null; GraphEntry newGraphEntry = SelectedGraphEntry.Copy(getMaxGraphId() + 1);
GraphEntries.Add(newGraphEntry);
GraphList.Items.Refresh();
GraphList.SelectedItem = newGraphEntry;
} }
private void ShowSaveResetCancelButtons() { private void DeleteButton_Click(object sender, RoutedEventArgs e) {
SaveButton.IsEnabled = false; if (SelectedGraphEntry == null) return;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
SaveButton.Visibility = Visibility.Visible;
ResetButton.Visibility = Visibility.Visible;
CancelButton.Visibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
SaveButton.Visibility = Visibility.Hidden;
ResetButton.Visibility = Visibility.Hidden;
CancelButton.Visibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
EnableNewEditDeleteButtons();
NewButton.Visibility = Visibility.Visible;
EditButton.Visibility = Visibility.Visible;
DeleteButton.Visibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
DisableNewEditDeleteButtons();
NewButton.Visibility = Visibility.Hidden;
EditButton.Visibility = Visibility.Hidden;
DeleteButton.Visibility = Visibility.Hidden;
}
private void NewButton_Click(object sender, RoutedEventArgs e) {
IsCreating = true;
GraphList.IsEnabled = false;
GraphList.SelectedItem = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
PriceInput.IsReadOnly = false;
OechsleInput.IsReadOnly = false;
InitInputs();
FillInputs(new Graph(GraphList.Items.Count + 1, MinOechsle, MaxOechsle));
EnableOptionButtons();
}
private void EditButton_Click(object sender, RoutedEventArgs e) {
if (GraphList.SelectedItem == null) {
return;
}
IsEditing = true;
GraphList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
PriceInput.IsReadOnly = false;
OechsleInput.IsReadOnly = false;
if (PrimaryMarkedPointIndex != -1) EnableActionButtons();
}
private async void DeleteButton_Click(object sender, RoutedEventArgs e) {
Graph g = (Graph)GraphList.SelectedItem;
if (g == null) return;
var r = MessageBox.Show( var r = MessageBox.Show(
$"Soll der Graph {g.Num} (verwendet in folgenden Verträgen: {g.Contracts}) wirklich unwiderruflich gelöscht werden?", $"Soll der Graph {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.Contracts}) wirklich gelöscht werden?",
"Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
bool success = await RemoveGraph(g.Num); GraphEntries.Remove(SelectedGraphEntry);
if (!success) { GraphList.Items.Refresh();
MessageBox.Show("Der Graph konnte nicht gelöscht werden", "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
await RefreshGraphList();
} }
} }
private void SaveButton_Click(object sender, RoutedEventArgs e) {
private async void SaveButton_Click(object sender, RoutedEventArgs e) { //TODO SAVE
int? index = await UpdateGraph(Graph);
if (index == null) {
MessageBox.Show("Der Graph konnte nicht gespeichert werden", "Graph speichern", MessageBoxButton.OK, MessageBoxImage.Error);
}
IsEditing = false;
IsCreating = false;
GraphList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
PriceInput.IsReadOnly = true;
OechsleInput.IsReadOnly = true;
await RefreshGraphList();
GraphList.SelectedIndex = index.Value;
} }
private async Task<int?> UpdateGraph(Graph g) { private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync(); SelectedGraphEntry = (GraphEntry)GraphList.SelectedItem;
if (paymentVars.Count != 1) {
return null;
}
PaymentVar paymentVar = paymentVars[0];
var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"];
if (auszahlungsSorten == null) {
return null;
}
var Graphs = auszahlungsSorten["Kurven"].AsArray();
if (Graphs == null) {
return null;
}
if (IsEditing) {
Graphs[g.Num - 1] = g.ToJson();
} else if(IsCreating) {
Graphs.Add(g.ToJson());
} else {
return null;
}
EntityEntry<PaymentVar>? tr = null;
try {
paymentVar.Data = data.ToString();
tr = Context.Update(paymentVar);
await Context.SaveChangesAsync();
} catch (Exception exc) {
if (tr != null) await tr.ReloadAsync();
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
return g.Num - 1;
}
private void ResetButton_Click(object sender, RoutedEventArgs e) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs e) {
IsEditing = false;
IsCreating = false;
GraphList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
DisableActionButtons();
RefreshInputs();
PriceInput.Text = "";
OechsleInput.Text = "";
ClearInputStates();
LockInputs();
PriceInput.IsReadOnly = true;
OechsleInput.IsReadOnly = true;
}
private void GraphList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
RefreshInputs(); RefreshInputs();
//var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>(); //var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>();
@@ -742,11 +495,7 @@ namespace Elwig.Windows {
} }
private void GraphNumberInput_TextChanged(object sender, TextChangedEventArgs e) { private void GebundenBonus_TextChanged(object sender, TextChangedEventArgs evt) {
}
private void GraphNumberInput_LostFocus(object sender, RoutedEventArgs e) {
} }
} }