From 18f05fd5672e721009ce0c6066233f7e0d253e3d Mon Sep 17 00:00:00 2001 From: clovett Date: Sun, 23 Jun 2024 17:17:48 -0700 Subject: [PATCH] Add IReportState so that we do not keep entire report on the undo stack. Fix report disposal so event handlers are removed so that reports are properly garbage collected. --- Source/WPF/MyMoney/MainWindow.xaml.cs | 255 +++++++++++++++--- Source/WPF/MyMoney/Reports/CashFlowReport.cs | 236 +++++++++------- .../WPF/MyMoney/Reports/FutureBillsReport.cs | 28 +- Source/WPF/MyMoney/Reports/IReport.cs | 32 ++- Source/WPF/MyMoney/Reports/NetWorthReport.cs | 58 +++- Source/WPF/MyMoney/Reports/PortfolioReport.cs | 132 ++++++--- Source/WPF/MyMoney/Reports/Reports.cs | 21 ++ Source/WPF/MyMoney/Reports/TaxReport.cs | 84 +++++- .../WPF/MyMoney/Reports/UnacceptedReport.cs | 25 +- Source/WPF/MyMoney/Reports/W2Report.cs | 84 ++++-- ...geInfoFormatter.cs => ChangeInfoReport.cs} | 79 +++++- .../MyMoney/Views/FlowDocumentView.xaml.cs | 22 +- .../MyMoney/Views/TransactionsView.xaml.cs | 15 +- Source/WPF/Version/VersionMaster.txt | 2 +- 14 files changed, 829 insertions(+), 244 deletions(-) rename Source/WPF/MyMoney/Setup/{ChangeInfoFormatter.cs => ChangeInfoReport.cs} (71%) diff --git a/Source/WPF/MyMoney/MainWindow.xaml.cs b/Source/WPF/MyMoney/MainWindow.xaml.cs index 1a1899c3..b8d1f9e2 100644 --- a/Source/WPF/MyMoney/MainWindow.xaml.cs +++ b/Source/WPF/MyMoney/MainWindow.xaml.cs @@ -22,6 +22,7 @@ using Walkabout.Data; using Walkabout.Dialogs; using Walkabout.Help; +using Walkabout.Interfaces.Reports; using Walkabout.Interfaces.Views; using Walkabout.Migrate; using Walkabout.Ofx; @@ -1174,6 +1175,14 @@ public IView CurrentView { MessageBoxEx.Show("Internal error"); } + + if (iView is FlowDocumentView flow) + { + flow.ReportCreated -= this.OnReportCreated; + flow.ReportCreated += this.OnReportCreated; + flow.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); + flow.Closed += new EventHandler(this.OnFlowDocumentViewClosed); + } } IView o = this.cacheViews[typeof(T)]; @@ -1181,7 +1190,6 @@ public IView CurrentView return (T)o; } - /// /// Gets or Creates a single view and makes it the current active view /// @@ -3388,6 +3396,10 @@ object IServiceProvider.GetService(Type service) { return this.TransactionView.ViewModel; } + else if (service == typeof(FlowDocumentView)) + { + return this.GetOrCreateView(); + } return null; } @@ -3517,17 +3529,27 @@ private void OnFlowDocumentViewClosed(object sender, EventArgs e) this.SetCurrentView(); } + private ReportEventHandler reportHandler; + + + private void OnReportCreated(object sender, IReport e) + { + using (this.reportHandler) + { + // dispose previous report handler. + } + + this.reportHandler = new ReportEventHandler(this, e); + } + private void OnCommandNetWorth(object sender, ExecutedRoutedEventArgs e) { this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportNetworth"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/NetworthReport/"); - NetWorthReport report = new NetWorthReport(view, this.myMoney, this.cache); - report.SecurityDrillDown += this.OnReportDrillDown; - report.CashBalanceDrillDown += this.OnReportCashDrillDown; + NetWorthReport report = new NetWorthReport() { ServiceProvider = this }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3536,12 +3558,9 @@ private void OnCommandFutureBills(object sender, ExecutedRoutedEventArgs e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "FutureBillsReport"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/FutureBillsReport/"); - FutureBillsReport report = new FutureBillsReport(this.myMoney); - report.PayeeSelected += this.OnReportPayeeSelected; - report.CategorySelected += this.OnReportCategorySelected; + FutureBillsReport report = new FutureBillsReport() { ServiceProvider = this }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3577,11 +3596,13 @@ private void ViewInvestmentPortfolio() this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportPortfolio"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/InvestmentPortfolio/"); - PortfolioReport report = new PortfolioReport(view, this.myMoney, null, this, DateTime.Now); - report.DrillDown += this.OnReportDrillDown; + PortfolioReport report = new PortfolioReport() + { + ServiceProvider = this, + ReportDate = DateTime.Now + }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3591,10 +3612,14 @@ private void OnReportDrillDown(object sender, SecurityGroup e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportPortfolio"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/InvestmentPortfolio/"); - PortfolioReport report = new PortfolioReport(view, this.myMoney, this, e.Date, e); + PortfolioReport report = new PortfolioReport() + { + ServiceProvider = this, + ReportDate = e.Date, + SelectedGroup = e + }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3604,10 +3629,14 @@ private void OnReportCashDrillDown(object sender, AccountGroup e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportPortfolio"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/InvestmentPortfolio/"); - PortfolioReport report = new PortfolioReport(view, this.myMoney, this, e.Date, e); + PortfolioReport report = new PortfolioReport() + { + ServiceProvider = this, + ReportDate = e.Date, + AccountGroup = e + }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3616,10 +3645,9 @@ private void OnTaxReport(object sender, ExecutedRoutedEventArgs e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportTaxes"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/TaxReport/"); - TaxReport report = new TaxReport(view, this.myMoney, this.databaseSettings.FiscalYearStart); + TaxReport report = new TaxReport() { FiscalYearStart = this.databaseSettings.FiscalYearStart, ServiceProvider = this }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3628,10 +3656,9 @@ private void OnCommandW2Report(object sender, ExecutedRoutedEventArgs e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportW2"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Reports/W2Report/"); - W2Report report = new W2Report(view, this.myMoney, this, this.databaseSettings.FiscalYearStart); + W2Report report = new W2Report() { ServiceProvider = this, FiscalYearStart = this.databaseSettings.FiscalYearStart }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3646,9 +3673,8 @@ private void OnCommandReportCashFlow(object sender, ExecutedRoutedEventArgs e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportCashFlow"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); - CashFlowReport report = new CashFlowReport(view, this.myMoney, this, this.databaseSettings.FiscalYearStart); + CashFlowReport report = new CashFlowReport() { ServiceProvider = this, FiscalYearStart = this.databaseSettings.FiscalYearStart }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -3657,11 +3683,10 @@ private void OnCommandReportUnaccepted(object sender, ExecutedRoutedEventArgs e) this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportUnaccepted"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); var pixelsPerDip = VisualTreeHelper.GetDpi(this).PixelsPerDip; FlowDocumentReportWriter writer = new FlowDocumentReportWriter(view.DocumentViewer.Document, pixelsPerDip); - UnacceptedReport report = new UnacceptedReport(this.myMoney); + UnacceptedReport report = new UnacceptedReport() { ServiceProvider = this }; + this.OnReportCreated(this, report); this.GenerateReport(report); } @@ -4530,11 +4555,15 @@ private void ShowChangeInfo(string previousVersion, XDocument changeList, bool i this.SaveViewStateOfCurrentView(); FlowDocumentView view = this.SetCurrentView(); view.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ReportUpdates"); - view.Closed -= new EventHandler(this.OnFlowDocumentViewClosed); - view.Closed += new EventHandler(this.OnFlowDocumentViewClosed); HelpService.SetHelpKeyword(view, "Basics/Updates/"); - ChangeInfoFormatter report = new ChangeInfoFormatter(view, installButton, previousVersion, changeList); - report.InstallButtonClick += this.OnInstallButtonClick; + ChangeInfoReport report = new ChangeInfoReport() + { + InstallButton = installButton, + PreviousVersion = previousVersion, + Document = changeList, + ServiceProvider = this + }; + this.OnReportCreated(this, report); _ = view.Generate(report); } } @@ -4704,5 +4733,157 @@ private void OnStockQuoteServiceOptions(object sender, ExecutedRoutedEventArgs e #endregion + #region Report Events + + /// + /// This class breaks the circular dependency between reports and this MainWindow by inserting + /// the window as a weak reference. This way reports will be garbage collected properly. + /// + class ReportEventHandler : IDisposable + { + WeakReference windowRef; + IReport report; + private bool disposedValue; + + internal ReportEventHandler(MainWindow window, IReport e) + { + this.windowRef = new WeakReference(window); + this.report = e; + + var view = window.GetOrCreateView(); + view.PreviewMouseLeftButtonUp -= this.OnFlowViewPreviewMouseLeftButtonUp; + view.PreviewMouseLeftButtonUp += this.OnFlowViewPreviewMouseLeftButtonUp; + view.Unloaded += (s, e) => + { + view.PreviewMouseLeftButtonUp -= this.OnFlowViewPreviewMouseLeftButtonUp; + }; + + if (e is NetWorthReport report) + { + report.SecurityDrillDown -= this.OnReportDrillDown; + report.CashBalanceDrillDown -= this.OnReportCashDrillDown; + report.SecurityDrillDown += this.OnReportDrillDown; + report.CashBalanceDrillDown += this.OnReportCashDrillDown; + } + else if (e is FutureBillsReport bills) + { + bills.PayeeSelected -= this.OnReportPayeeSelected; + bills.CategorySelected -= this.OnReportCategorySelected; + bills.PayeeSelected += this.OnReportPayeeSelected; + bills.CategorySelected += this.OnReportCategorySelected; + } + else if (e is PortfolioReport portfolio) + { + portfolio.DrillDown -= this.OnReportDrillDown; + portfolio.DrillDown += this.OnReportDrillDown; + } + else if (e is ChangeInfoReport changes) + { + changes.InstallButtonClick -= this.OnInstallButtonClick; + changes.InstallButtonClick += this.OnInstallButtonClick; + } + } + + + private void OnFlowViewPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + this.report.OnMouseLeftButtonClick(sender, e); + } + } + + private void OnReportDrillDown(object sender, SecurityGroup e) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + target.OnReportDrillDown(sender, e); + } + } + + private void OnReportCashDrillDown(object sender, AccountGroup e) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + target.OnReportCashDrillDown(sender, e); + } + } + + private void OnReportCategorySelected(object sender, Category c) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + target.OnReportCategorySelected(sender, c); + } + } + + private void OnReportPayeeSelected(object sender, Payee p) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + target.OnReportPayeeSelected(sender, p); + } + } + + private void OnInstallButtonClick(object sender, EventArgs e) + { + if (windowRef.TryGetTarget(out MainWindow target)) + { + target.OnInstallButtonClick(sender, e); + } + } + + protected virtual void Dispose(bool disposing) + { + if (this.windowRef != null && windowRef.TryGetTarget(out MainWindow window)) + { + var view = window.GetOrCreateView(); + view.PreviewMouseLeftButtonUp -= this.OnFlowViewPreviewMouseLeftButtonUp; + } + + if (this.report != null) + { + var report = this.report; + + if (report is NetWorthReport networth) + { + networth.SecurityDrillDown -= this.OnReportDrillDown; + networth.CashBalanceDrillDown -= this.OnReportCashDrillDown; + } + else if (report is FutureBillsReport bills) + { + bills.PayeeSelected -= this.OnReportPayeeSelected; + bills.CategorySelected -= this.OnReportCategorySelected; + } + else if (report is PortfolioReport portfolio) + { + portfolio.DrillDown -= this.OnReportDrillDown; + } + else if (report is ChangeInfoReport changes) + { + changes.InstallButtonClick -= this.OnInstallButtonClick; + } + } + + this.report = null; + this.windowRef = null; + } + + // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~ReportEventHandler() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + #endregion } } diff --git a/Source/WPF/MyMoney/Reports/CashFlowReport.cs b/Source/WPF/MyMoney/Reports/CashFlowReport.cs index 184408ee..ba4cffba 100644 --- a/Source/WPF/MyMoney/Reports/CashFlowReport.cs +++ b/Source/WPF/MyMoney/Reports/CashFlowReport.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -16,130 +17,96 @@ namespace Walkabout.Reports { - internal class CashFlowCell - { - internal List Data; // or splits - internal decimal Value; - } - - internal class CashFlowColumns + //========================================================================================= + public class CashFlowReport : Report { - private readonly Dictionary columns = new Dictionary(); + private MyMoney myMoney; + private bool byYear; + private int fiscalYearStart; + private DateTime startDate; + private DateTime endDate; + private Dictionary byCategory; + private Dictionary monthMap; + private List columns; - public void AddValue(string key, Transaction data, decimal amount) + public CashFlowReport() { - CashFlowCell cell; - this.columns.TryGetValue(key, out cell); - if (cell == null) - { - cell = new CashFlowCell(); - cell.Data = new List(); - this.columns[key] = cell; - } - cell.Value += amount; - if (data != null) - { - cell.Data.Add(data); - } + this.startDate = new DateTime(DateTime.Now.Year, 1, 1); + this.byYear = true; + this.endDate = this.startDate.AddYears(1); + this.startDate = this.startDate.AddYears(-4); // show 5 years by default. } - public CashFlowCell GetCell(string key) + ~CashFlowReport() { - CashFlowCell cell = null; - if (!this.columns.TryGetValue(key, out cell)) - { - cell = new CashFlowCell(); - } - return cell; + Debug.WriteLine("CashFlowReport disposed!"); } - public decimal GetValue(string key) + public int FiscalYearStart { - CashFlowCell cell; - this.columns.TryGetValue(key, out cell); - if (cell != null) + get => this.fiscalYearStart; + set { - return cell.Value; + this.fiscalYearStart = value; + this.startDate = new DateTime(DateTime.Now.Year, this.fiscalYearStart + 1, 1); + if (this.startDate > DateTime.Today) + { + this.startDate = this.startDate.AddYears(-1); + } } - return 0; } - public List GetData(string key) + public override void OnSiteChanged() { - CashFlowCell cell; - this.columns.TryGetValue(key, out cell); - if (cell != null) - { - return cell.Data; - } - return new List(); + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); } - public List GetOrderedValues(IEnumerable columnKeys) + class CashFlowReportState : IReportState { - List result = new List(); - foreach (string name in columnKeys) + public int FiscalYearStart { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + + public CashFlowReportState() { - result.Add(this.GetValue(name)); } - return result; - } - - public int Count { get { return this.columns.Count; } } - } - - //========================================================================================= - public class CashFlowReport : Report - { - private readonly FlowDocumentView view; - private readonly MyMoney myMoney; - private bool byYear; - private readonly int fiscalYearStart; - private DateTime startDate; - private DateTime endDate; - private Dictionary byCategory; - private Dictionary monthMap; - private List columns; - private readonly IServiceProvider serviceProvider; - public CashFlowReport(FlowDocumentView view, MyMoney money, IServiceProvider sp, int fiscalYearStart) - { - this.myMoney = money; - this.fiscalYearStart = fiscalYearStart; - this.startDate = new DateTime(DateTime.Now.Year, 1, 1); - this.byYear = true; - if (this.fiscalYearStart > 0) + public Type GetReportType() { - this.startDate = new DateTime(DateTime.Now.Year, this.fiscalYearStart + 1, 1); - if (this.startDate > DateTime.Today) - { - this.startDate = this.startDate.AddYears(-1); - } + return typeof(CashFlowReport); } + } - this.endDate = this.startDate.AddYears(1); - this.startDate = this.startDate.AddYears(-4); // show 5 years by default. - this.view = view; - this.serviceProvider = sp; - view.Unloaded += (s, e) => + public override IReportState GetState() + { + return new CashFlowReportState() { - this.view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; + FiscalYearStart = this.fiscalYearStart, + StartDate = this.startDate, + EndDate = this.endDate }; - view.Loaded += (s, e) => + } + + public override void ApplyState(IReportState state) + { + if (state is CashFlowReportState cashFlowReportState) { - view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; - view.PreviewMouseLeftButtonUp += this.OnPreviewMouseLeftButtonUp; - }; + this.FiscalYearStart = cashFlowReportState.FiscalYearStart; + this.startDate = cashFlowReportState.StartDate; + this.endDate = cashFlowReportState.EndDate; + } } - private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + + public override void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e) { - Point pos = e.GetPosition(this.view); + var view = (FlowDocumentView)sender; + Point pos = e.GetPosition(view); if (this.mouseDownCell != null && Math.Abs(this.downPos.X - pos.X) < 5 && Math.Abs(this.downPos.Y - pos.Y) < 5) { // navigate to show the cell.Data rows. - IViewNavigator nav = this.serviceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; + IViewNavigator nav = this.ServiceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; nav.ViewTransactions(this.mouseDownCell.Data); } } @@ -200,11 +167,6 @@ private bool IsInvestment(Category c) return c.Type == CategoryType.Investments; } - public void Regenerate() - { - _ = this.view.Generate(this); - } - public override Task Generate(IReportWriter writer) { this.byCategory = new Dictionary(); @@ -378,6 +340,12 @@ private void OnByYearMonthChanged(object sender, SelectionChangedEventArgs e) this.Regenerate(); } + public void Regenerate() + { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + _ = view.Generate(this); + } + private Button CreateExportReportButton() { Button button = this.CreateReportButton("Icons/Excel.png", "Export", "Export .csv spreadsheet file format"); @@ -591,9 +559,10 @@ private void WriteRow(IReportWriter writer, bool header, bool addExpanderCell, s private void OnReportCellMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); Paragraph p = (Paragraph)sender; this.mouseDownCell = (CashFlowCell)p.Tag; - this.downPos = e.GetPosition(this.view); + this.downPos = e.GetPosition(view); } @@ -652,6 +621,79 @@ private void GenerateCsvGroup(StreamWriter writer, Dictionary Data; // or splits + internal decimal Value; + } + + internal class CashFlowColumns + { + private readonly Dictionary columns = new Dictionary(); + + public void AddValue(string key, Transaction data, decimal amount) + { + CashFlowCell cell; + this.columns.TryGetValue(key, out cell); + if (cell == null) + { + cell = new CashFlowCell(); + cell.Data = new List(); + this.columns[key] = cell; + } + cell.Value += amount; + if (data != null) + { + cell.Data.Add(data); + } + } + + public CashFlowCell GetCell(string key) + { + CashFlowCell cell = null; + if (!this.columns.TryGetValue(key, out cell)) + { + cell = new CashFlowCell(); + } + return cell; + } + + public decimal GetValue(string key) + { + CashFlowCell cell; + this.columns.TryGetValue(key, out cell); + if (cell != null) + { + return cell.Value; + } + return 0; + } + + public List GetData(string key) + { + CashFlowCell cell; + this.columns.TryGetValue(key, out cell); + if (cell != null) + { + return cell.Data; + } + return new List(); + } + + public List GetOrderedValues(IEnumerable columnKeys) + { + List result = new List(); + foreach (string name in columnKeys) + { + result.Add(this.GetValue(name)); + } + return result; + } + + public int Count { get { return this.columns.Count; } } + } + } } diff --git a/Source/WPF/MyMoney/Reports/FutureBillsReport.cs b/Source/WPF/MyMoney/Reports/FutureBillsReport.cs index 8b3b1b39..91f12b2a 100644 --- a/Source/WPF/MyMoney/Reports/FutureBillsReport.cs +++ b/Source/WPF/MyMoney/Reports/FutureBillsReport.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using System.Windows; @@ -16,7 +17,7 @@ namespace Walkabout.Reports /// public class FutureBillsReport : Report { - private readonly MyMoney myMoney; + private MyMoney myMoney; private const int ALLOWED_MISSED_PAYMENTS = 2; internal struct PaymentKey @@ -102,8 +103,6 @@ internal class Payments internal const double AmountSensitivity = 0.1; // % stderr internal const double TimeSensitivity = 0.5; // % stderr on date - private List transactions; - public Payee Payee { get; internal set; } public Category Category { get; internal set; } @@ -285,9 +284,27 @@ public Payments() } } - public FutureBillsReport(MyMoney money) + public FutureBillsReport() + { + } + + ~FutureBillsReport() + { + Debug.WriteLine("FutureBillsReport disposed!"); + } + + public override void OnSiteChanged() + { + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + } + + public override IReportState GetState() + { + return new SimpleReportState(typeof(FutureBillsReport)); + } + + public override void ApplyState(IReportState state) { - this.myMoney = money; } public override Task Generate(IReportWriter writer) @@ -466,6 +483,7 @@ public override void Export(string filename) { throw new NotImplementedException(); } + } diff --git a/Source/WPF/MyMoney/Reports/IReport.cs b/Source/WPF/MyMoney/Reports/IReport.cs index 9694da57..37a16fcd 100644 --- a/Source/WPF/MyMoney/Reports/IReport.cs +++ b/Source/WPF/MyMoney/Reports/IReport.cs @@ -1,15 +1,45 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; +using Walkabout.Views; namespace Walkabout.Interfaces.Reports { public interface IReport { + IServiceProvider ServiceProvider { get; set; } + Task Generate(IReportWriter writer); void Export(string filename); + + IReportState GetState(); + + void ApplyState(IReportState state); + + void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e); + } + + public interface IReportState + { + Type GetReportType(); + } + + public class SimpleReportState : IReportState + { + Type type; + + public SimpleReportState(Type reportType) + { + this.type = reportType; + } + + public virtual Type GetReportType() + { + return this.type; + } } public enum ReportInterval diff --git a/Source/WPF/MyMoney/Reports/NetWorthReport.cs b/Source/WPF/MyMoney/Reports/NetWorthReport.cs index 788e8316..746fbff2 100644 --- a/Source/WPF/MyMoney/Reports/NetWorthReport.cs +++ b/Source/WPF/MyMoney/Reports/NetWorthReport.cs @@ -1,6 +1,7 @@ using LovettSoftware.Charts; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -20,28 +21,63 @@ namespace Walkabout.Reports //========================================================================================= public class NetWorthReport : Report { - private readonly FlowDocumentView view; - private readonly MyMoney myMoney; + private MyMoney myMoney; private readonly Random rand = new Random(Environment.TickCount); private readonly byte minRandColor, maxRandColor; private DateTime reportDate; - private readonly StockQuoteCache cache; + private StockQuoteCache cache; private bool generating; private bool filterOutClosedAccounts = false; public event EventHandler SecurityDrillDown; public event EventHandler CashBalanceDrillDown; - public NetWorthReport(FlowDocumentView view, MyMoney money, StockQuoteCache cache) + public NetWorthReport() { - this.view = view; - this.myMoney = money; - this.cache = cache; this.reportDate = DateTime.Now; this.minRandColor = 20; this.maxRandColor = ("" + AppTheme.Instance.GetTheme()).Contains("Dark") ? (byte)128 : (byte)200; } + ~NetWorthReport() + { + Debug.WriteLine("NetWorthReport disposed!"); + } + + public override void OnSiteChanged() + { + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + this.cache = (StockQuoteCache)this.ServiceProvider.GetService(typeof(StockQuoteCache)); + } + + class NetworthReportState : IReportState + { + public DateTime ReportDate { get; set; } + + public NetworthReportState(DateTime reportDate) + { + this.ReportDate = reportDate; + } + + public Type GetReportType() + { + return typeof(NetWorthReport); + } + } + + public override IReportState GetState() + { + return new NetworthReportState(reportDate); + } + + public override void ApplyState(IReportState state) + { + if (state is NetworthReportState networthReportState) + { + this.reportDate = networthReportState.ReportDate; + } + } + public override async Task Generate(IReportWriter writer) { this.generating = true; @@ -298,11 +334,17 @@ private void Picker_SelectedDateChanged(object sender, SelectionChangedEventArgs { this.reportDate = picker.SelectedDate.Value; this.filterOutClosedAccounts = this.reportDate >= DateTime.Today; - _ = this.view.Generate(this); + this.Regenerate(); } } } + private void Regenerate() + { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + _ = view.Generate(this); + } + private void OnPieSliceClicked(object sender, ChartDataValue e) { if (e.UserData is SecurityGroup g) diff --git a/Source/WPF/MyMoney/Reports/PortfolioReport.cs b/Source/WPF/MyMoney/Reports/PortfolioReport.cs index 6c137a40..440abeb3 100644 --- a/Source/WPF/MyMoney/Reports/PortfolioReport.cs +++ b/Source/WPF/MyMoney/Reports/PortfolioReport.cs @@ -1,6 +1,7 @@ using LovettSoftware.Charts; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -13,6 +14,7 @@ using Walkabout.Interfaces.Reports; using Walkabout.Interfaces.Views; using Walkabout.StockQuotes; +using Walkabout.Taxes; using Walkabout.Utilities; using Walkabout.Views; @@ -21,18 +23,16 @@ namespace Walkabout.Reports //========================================================================================= public class PortfolioReport : Report { - private readonly MyMoney myMoney; - private readonly Account account; + private MyMoney myMoney; + private Account account; private FlowDocumentReportWriter flowwriter; private CostBasisCalculator calc; - private readonly IServiceProvider serviceProvider; - private readonly FlowDocumentView view; private Paragraph mouseDownPara; private Point downPos; private DateTime reportDate; - private readonly StockQuoteCache cache; + private StockQuoteCache cache; private SecurityGroup selectedGroup; - private readonly AccountGroup accountGroup; + private AccountGroup accountGroup; private readonly Random rand = new Random(Environment.TickCount); private bool generating; @@ -41,62 +41,104 @@ public class PortfolioReport : Report /// /// Create new PortfolioReport /// - /// The FlowDocumentView we are generating the report in - /// The money database - /// Optional account for single account portfolio - /// Required to access additional services - /// The date to compute the portfolio balances to - public PortfolioReport(FlowDocumentView view, MyMoney money, Account account, IServiceProvider serviceProvider, DateTime asOfDate) - { - this.myMoney = money; - this.account = account; - this.serviceProvider = serviceProvider; - this.view = view; - this.reportDate = asOfDate; - this.cache = (StockQuoteCache)serviceProvider.GetService(typeof(StockQuoteCache)); - view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; - view.PreviewMouseLeftButtonUp += this.OnPreviewMouseLeftButtonUp; - view.Unloaded += (s, e) => - { - this.view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; - }; + public PortfolioReport() + { } - /// - /// Create new PortfolioReport for a given SecurityGroup we are drilling into from the Networth Report, like "Taxable Mutual Funds". - /// - public PortfolioReport(FlowDocumentView view, MyMoney money, IServiceProvider serviceProvider, DateTime asOfDate, SecurityGroup a) : - this(view, money, null, serviceProvider, asOfDate) + ~PortfolioReport() { - this.selectedGroup = a; + Debug.WriteLine("PortfolioReport disposed!"); } - /// - /// Create new PortfolioReport for a given AccountGroup, this will show just the cash balances of these accounts. - /// - public PortfolioReport(FlowDocumentView view, MyMoney money, IServiceProvider serviceProvider, DateTime asOfDate, AccountGroup a) : - this(view, money, null, serviceProvider, asOfDate) + + public Account Account { - this.accountGroup = a; + get => this.account; + set => this.account = value; } - private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + public AccountGroup AccountGroup { - Point pos = e.GetPosition(this.view); + get => this.accountGroup; + set => this.accountGroup = value; + } + + public SecurityGroup SelectedGroup + { + get => this.selectedGroup; + set => this.selectedGroup = value; + } + + public DateTime ReportDate + { + get => reportDate; + set => reportDate = value; + } + + public override void OnSiteChanged() + { + this.cache = (StockQuoteCache)this.ServiceProvider.GetService(typeof(StockQuoteCache)); + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + } + + class PortfolioReportState : IReportState + { + public DateTime ReportDate { get; set; } + public Account Account { get; set; } + public AccountGroup AccountGroup { get; set; } + public SecurityGroup SelectedGroup { get; set; } + + public PortfolioReportState() + { + } + + public Type GetReportType() + { + return typeof(PortfolioReport); + } + } + + public override IReportState GetState() + { + return new PortfolioReportState() + { + ReportDate = this.reportDate, + Account = this.account, + AccountGroup = this.accountGroup, + SelectedGroup = this.selectedGroup, + }; + } + + public override void ApplyState(IReportState state) + { + if (state is PortfolioReportState taxReportState) + { + this.reportDate = taxReportState.ReportDate; + this.account = taxReportState.Account; + this.accountGroup = taxReportState.AccountGroup; + this.selectedGroup = taxReportState.SelectedGroup; + } + } + + public override void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e) + { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + Point pos = e.GetPosition(view); if (this.mouseDownPara != null && Math.Abs(this.downPos.X - pos.X) < 5 && Math.Abs(this.downPos.Y - pos.Y) < 5) { string name = (string)this.mouseDownPara.Tag; // navigate to show the cell.Data rows. - IViewNavigator nav = this.serviceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; + IViewNavigator nav = this.ServiceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; nav.ViewTransactionsBySecurity(this.myMoney.Securities.FindSecurity(name, false)); } } private void OnReportCellMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); this.mouseDownPara = (Paragraph)sender; - this.downPos = e.GetPosition(this.view); + this.downPos = e.GetPosition(view); } private decimal totalMarketValue; @@ -356,12 +398,18 @@ private void Picker_SelectedDateChanged(object sender, SelectionChangedEventArgs { this.accountGroup.Date = newDate; } - _ = this.view.Generate(this); + this.Regenerate(); } } } } + void Regenerate() + { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + _ = view.Generate(this); + } + private string GetSecurityTypeCaption(SecurityType st) { string caption = ""; diff --git a/Source/WPF/MyMoney/Reports/Reports.cs b/Source/WPF/MyMoney/Reports/Reports.cs index 9b50f2ad..40c55796 100644 --- a/Source/WPF/MyMoney/Reports/Reports.cs +++ b/Source/WPF/MyMoney/Reports/Reports.cs @@ -8,6 +8,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Documents; +using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using Walkabout.Data; @@ -20,9 +21,29 @@ public abstract class Report : IReport { private Currency currency; private CultureInfo currencyCulture; + private IServiceProvider serviceProvider; + + public IServiceProvider ServiceProvider + { + get => serviceProvider; + set { + serviceProvider = value; + this.OnSiteChanged(); + } + } + + public virtual void OnSiteChanged() { } + + public abstract IReportState GetState(); + + public abstract void ApplyState(IReportState state); public abstract Task Generate(IReportWriter writer); + public virtual void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e) + { + } + public virtual void Export(string filename) { throw new NotImplementedException(); diff --git a/Source/WPF/MyMoney/Reports/TaxReport.cs b/Source/WPF/MyMoney/Reports/TaxReport.cs index 6184e99a..e1987fdb 100644 --- a/Source/WPF/MyMoney/Reports/TaxReport.cs +++ b/Source/WPF/MyMoney/Reports/TaxReport.cs @@ -20,21 +20,74 @@ namespace Walkabout.Reports //========================================================================================= public class TaxReport : Report { - private readonly FlowDocumentView view; - private readonly MyMoney money; + private MyMoney money; private DateTime startDate; private DateTime endDate; private bool consolidateOnDateSold; private bool capitalGainsOnly; - private readonly int fiscalYearStart; + private int fiscalYearStart; private const string FiscalPrefix = "FY "; - public TaxReport(FlowDocumentView view, MyMoney money, int fiscalYearStart) + public TaxReport() { - this.fiscalYearStart = fiscalYearStart; - this.view = view; this.SetStartDate(DateTime.Now.Year); - this.money = money; + } + + ~TaxReport() + { + Debug.WriteLine("TaxReport disposed!"); + } + + public int FiscalYearStart + { + get => this.fiscalYearStart; + set { + this.fiscalYearStart = value; + } + } + + public override void OnSiteChanged() + { + this.money = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + } + + class TaxReportState : IReportState + { + public int FiscalYearStart { get; set; } + public bool CapitalGainsOnly { get; set; } + public bool ConsolidateOnDateSold { get; set; } + public int TaxYear { get; set; } + + public TaxReportState() + { + } + + public Type GetReportType() + { + return typeof(TaxReport); + } + } + + public override IReportState GetState() + { + return new TaxReportState() + { + FiscalYearStart = this.fiscalYearStart, + CapitalGainsOnly = this.capitalGainsOnly, + ConsolidateOnDateSold = this.consolidateOnDateSold, + TaxYear = this.startDate.Year + }; + } + + public override void ApplyState(IReportState state) + { + if (state is TaxReportState taxReportState) + { + this.FiscalYearStart = taxReportState.FiscalYearStart; + this.capitalGainsOnly = taxReportState.CapitalGainsOnly; + this.consolidateOnDateSold = taxReportState.ConsolidateOnDateSold; + this.SetStartDate(taxReportState.TaxYear); + } } private void SetStartDate(int year) @@ -57,7 +110,6 @@ public override Task Generate(IReportWriter writer) { FlowDocumentReportWriter fwriter = (FlowDocumentReportWriter)writer; - writer.WriteHeading("Tax Report For "); var (firstYear, lastYear) = this.money.Transactions.GetTaxYearRange(this.fiscalYearStart); @@ -137,7 +189,8 @@ public override Task Generate(IReportWriter writer) } this.GenerateCapitalGains(writer); - FlowDocument document = this.view.DocumentViewer.Document; + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + FlowDocument document = view.DocumentViewer.Document; document.Blocks.InsertAfter(document.Blocks.FirstBlock, new BlockUIContainer(this.CreateExportTxfButton())); return Task.CompletedTask; } @@ -183,11 +236,18 @@ private void WriteHeaders(IReportWriter writer) writer.EndRow(); } + + private void Renerate() + { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + _ = view.Generate(this); + } + private void OnCapitalGainsOnlyChanged(object sender, RoutedEventArgs e) { CheckBox checkBox = (CheckBox)sender; this.capitalGainsOnly = checkBox.IsChecked == true; - _ = this.view.Generate(this); + this.Renerate(); } private void OnConsolidateComboSelectionChanged(object sender, SelectionChangedEventArgs e) @@ -195,7 +255,7 @@ private void OnConsolidateComboSelectionChanged(object sender, SelectionChangedE ComboBox box = (ComboBox)sender; int index = box.SelectedIndex; this.consolidateOnDateSold = index == 1; - _ = this.view.Generate(this); + this.Renerate(); } private void OnYearChanged(object sender, SelectionChangedEventArgs e) @@ -209,7 +269,7 @@ private void OnYearChanged(object sender, SelectionChangedEventArgs e) if (int.TryParse(label, out int year)) { this.SetStartDate(year); - _ = this.view.Generate(this); + this.Renerate(); } } diff --git a/Source/WPF/MyMoney/Reports/UnacceptedReport.cs b/Source/WPF/MyMoney/Reports/UnacceptedReport.cs index 32726252..27945f04 100644 --- a/Source/WPF/MyMoney/Reports/UnacceptedReport.cs +++ b/Source/WPF/MyMoney/Reports/UnacceptedReport.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading.Tasks; using Walkabout.Data; using Walkabout.Interfaces.Reports; @@ -10,11 +11,29 @@ namespace Walkabout.Reports /// public class UnacceptedReport : Report { - private readonly MyMoney myMoney; + private MyMoney myMoney; - public UnacceptedReport(MyMoney money) + public UnacceptedReport() + { + } + + ~UnacceptedReport() + { + Debug.WriteLine("UnacceptedReport disposed!"); + } + + public override void OnSiteChanged() + { + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + } + + public override IReportState GetState() + { + return new SimpleReportState(typeof(UnacceptedReport)); + } + + public override void ApplyState(IReportState state) { - this.myMoney = money; } public override Task Generate(IReportWriter writer) diff --git a/Source/WPF/MyMoney/Reports/W2Report.cs b/Source/WPF/MyMoney/Reports/W2Report.cs index 25522cbf..70e8da8c 100644 --- a/Source/WPF/MyMoney/Reports/W2Report.cs +++ b/Source/WPF/MyMoney/Reports/W2Report.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -17,33 +18,73 @@ namespace Walkabout.Taxes // This class prepares an estimated W2 from the splits found in paycheck deposits. public class W2Report : Report { - private readonly FlowDocumentView view; - private readonly MyMoney myMoney; + private MyMoney myMoney; private DateTime startDate; private DateTime endDate; - private readonly IServiceProvider serviceProvider; private Point downPos; - private readonly int fiscalYearStart; + private int fiscalYearStart; private Category selectedCategory; - private readonly TaxCategoryCollection taxCategories; + private TaxCategoryCollection taxCategories; private Dictionary> transactionsByCategory; private const string FiscalPrefix = "FY "; - public W2Report(FlowDocumentView view, MyMoney money, IServiceProvider sp, int fiscalYearStart) + public W2Report() { - this.myMoney = money; - this.fiscalYearStart = fiscalYearStart; - this.view = view; - this.serviceProvider = sp; - view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; - view.PreviewMouseLeftButtonUp += this.OnPreviewMouseLeftButtonUp; - view.Unloaded += (s, e) => + } + + ~W2Report() + { + Debug.WriteLine("W2Report disposed!"); + } + + public int FiscalYearStart + { + get => this.fiscalYearStart; + set { + this.fiscalYearStart = value; + + } + } + + public override void OnSiteChanged() + { + this.taxCategories = new TaxCategoryCollection(); + this.transactionsByCategory = null; + this.myMoney = (MyMoney)this.ServiceProvider.GetService(typeof(MyMoney)); + } + + class W2ReportState : IReportState + { + public int FiscalYearStart { get; set; } + public DateTime StartDate { get; set; } + + public W2ReportState() + { + } + + public Type GetReportType() + { + return typeof(W2Report); + } + } + + public override IReportState GetState() + { + return new W2ReportState() { - view.PreviewMouseLeftButtonUp -= this.OnPreviewMouseLeftButtonUp; + FiscalYearStart = this.fiscalYearStart, + StartDate = this.startDate, }; - this.taxCategories = new TaxCategoryCollection(); } + public override void ApplyState(IReportState state) + { + if (state is W2ReportState taxReportState) + { + this.FiscalYearStart = taxReportState.FiscalYearStart; + this.SetStartDate(taxReportState.StartDate); + } + } private void SetStartDate(DateTime date) { @@ -65,14 +106,15 @@ private void SetStartDate(DateTime date) this.endDate = this.startDate.AddYears(1); } - private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + public override void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e) { - Point pos = e.GetPosition(this.view); + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + Point pos = e.GetPosition(view); if (Math.Abs(this.downPos.X - pos.X) < 5 && Math.Abs(this.downPos.Y - pos.Y) < 5) { // navigate to show the cell.Data rows. - IViewNavigator nav = this.serviceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; + IViewNavigator nav = this.ServiceProvider.GetService(typeof(IViewNavigator)) as IViewNavigator; List transactions = null; if (this.selectedCategory != null && this.transactionsByCategory.TryGetValue(this.selectedCategory, out transactions)) { @@ -83,7 +125,8 @@ private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) public void Regenerate() { - _ = this.view.Generate(this); + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + _ = view.Generate(this); } private bool Summarize(Dictionary byCategory, Transaction t) @@ -374,9 +417,10 @@ private string GetCategoryCaption(Category c) private void OnReportCellMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { + var view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); Paragraph p = (Paragraph)sender; this.selectedCategory = p.Tag as Category; - this.downPos = e.GetPosition(this.view); + this.downPos = e.GetPosition(view); } diff --git a/Source/WPF/MyMoney/Setup/ChangeInfoFormatter.cs b/Source/WPF/MyMoney/Setup/ChangeInfoReport.cs similarity index 71% rename from Source/WPF/MyMoney/Setup/ChangeInfoFormatter.cs rename to Source/WPF/MyMoney/Setup/ChangeInfoReport.cs index 9c939bb7..94197d64 100644 --- a/Source/WPF/MyMoney/Setup/ChangeInfoFormatter.cs +++ b/Source/WPF/MyMoney/Setup/ChangeInfoReport.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media.Imaging; using System.Xml.Linq; +using Walkabout.Data; using Walkabout.Interfaces.Reports; using Walkabout.Reports; using Walkabout.Utilities; @@ -12,12 +14,13 @@ namespace Walkabout.Setup { - internal class ChangeInfoFormatter : Report + internal class ChangeInfoReport + : Report { - private readonly string previousVersion; - private readonly XDocument doc; - private readonly FlowDocumentView view; - private readonly bool installButton; + private string previousVersion; + private XDocument doc; + private FlowDocumentView view; + private bool installButton; public event EventHandler InstallButtonClick; @@ -29,12 +32,68 @@ private void OnInstallButtonClick() } } - public ChangeInfoFormatter(FlowDocumentView view, bool addInstallButton, string previousVersion, XDocument doc) + public ChangeInfoReport() { - this.previousVersion = previousVersion; - this.doc = doc; - this.view = view; - this.installButton = addInstallButton; + } + + ~ChangeInfoReport() + { + Debug.WriteLine("ChangeInfoReport disposed!"); + } + + + public string PreviousVersion + { + get => previousVersion; + set => previousVersion = value; + } + + public bool InstallButton + { + get => installButton; + set => installButton = value; + } + public XDocument Document + { + get => doc; + set => doc = value; + } + + public override void OnSiteChanged() + { + this.view = (FlowDocumentView)this.ServiceProvider.GetService(typeof(FlowDocumentView)); + } + + public class ChangeInfoReportState : IReportState + { + public string PreviousVersion { get; set; } + public bool InstallButton { get; set; } + public XDocument Document { get; set; } + public ChangeInfoReportState() { } + + public Type GetReportType() { + return typeof(ChangeInfoReport); + } + } + + public override IReportState GetState() + { + return new ChangeInfoReportState() + { + PreviousVersion = this.previousVersion, + InstallButton = this.installButton, + Document = this.doc + }; + } + + public override void ApplyState(IReportState state) + { + if (state is ChangeInfoReportState cs) + { + this.previousVersion = cs.PreviousVersion; + this.installButton = cs.InstallButton; + this.doc = cs.Document; + } } /// diff --git a/Source/WPF/MyMoney/Views/FlowDocumentView.xaml.cs b/Source/WPF/MyMoney/Views/FlowDocumentView.xaml.cs index 8e6f9113..8e74a431 100644 --- a/Source/WPF/MyMoney/Views/FlowDocumentView.xaml.cs +++ b/Source/WPF/MyMoney/Views/FlowDocumentView.xaml.cs @@ -154,16 +154,26 @@ public object SelectedRow public ViewState ViewState { - get { return new ReportViewState(this.report); } + get { return new ReportViewState(this.report.GetState()); } set { - if (value is ReportViewState r) + if (value is ReportViewState s) { - _ = this.Generate(r.report); + var type = s.ReportState.GetReportType(); + var report = (IReport)Activator.CreateInstance(type); + report.ServiceProvider = this.serviceProvider; + report.ApplyState(s.ReportState); + if (ReportCreated != null) + { + ReportCreated(this, report); + } + _ = this.Generate(report); } } } + public event EventHandler ReportCreated; + public ViewState DeserializeViewState(System.Xml.XmlReader reader) { return new ViewState(); @@ -302,11 +312,11 @@ private void ResetExpandAllToggleButton() /// internal class ReportViewState : ViewState { - public IReport report; + public IReportState ReportState { get; private set; } - public ReportViewState(IReport report) + public ReportViewState(IReportState reportState) { - this.report = report; + this.ReportState = reportState; } } diff --git a/Source/WPF/MyMoney/Views/TransactionsView.xaml.cs b/Source/WPF/MyMoney/Views/TransactionsView.xaml.cs index c4a18613..00309999 100644 --- a/Source/WPF/MyMoney/Views/TransactionsView.xaml.cs +++ b/Source/WPF/MyMoney/Views/TransactionsView.xaml.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Security.Principal; using System.Text; using System.Windows; using System.Windows.Automation; @@ -2298,7 +2299,12 @@ private void UpdatePortfolio(Account account) this.SetActiveAccount(account, null, null, null, null); // if we are reconciling then show the positions held at statement date so the stock balances can be reconciled also. DateTime reportDate = this.IsReconciling ? this.GetReconiledExclusiveEndDate() : DateTime.Now; - PortfolioReport report = new PortfolioReport(view, this.myMoney, account, this.ServiceProvider, reportDate); + PortfolioReport report = new PortfolioReport() + { + ServiceProvider = this.ServiceProvider, + ReportDate = reportDate, + Account = account + }; report.DrillDown += this.OnReportDrillDown; _ = view.Generate(report); this.portfolioReport = report; @@ -2315,7 +2321,12 @@ private void OnReportDrillDown(object sender, SecurityGroup e) FlowDocumentView view = this.InvestmentPortfolioView; HelpService.SetHelpKeyword(view, "Reports/InvestmentPortfolio/"); DateTime reportDate = this.IsReconciling ? this.GetReconiledExclusiveEndDate() : DateTime.Now; - PortfolioReport report = new PortfolioReport(view, this.myMoney, this.ServiceProvider, reportDate, e); + PortfolioReport report = new PortfolioReport() + { + ServiceProvider = this.ServiceProvider, + ReportDate = reportDate, + SelectedGroup = e + }; _ = view.Generate(report); this.FireAfterViewStateChanged(this.SelectedRowId); } diff --git a/Source/WPF/Version/VersionMaster.txt b/Source/WPF/Version/VersionMaster.txt index 95f92f23..80cbfbc9 100644 --- a/Source/WPF/Version/VersionMaster.txt +++ b/Source/WPF/Version/VersionMaster.txt @@ -1 +1 @@ -2.1.0.20 \ No newline at end of file +2.1.0.21 \ No newline at end of file