/// <summary> /// Called when the group collection is changed. Used to add/remove Totals rows. /// </summary> /// <param name="sender">The modified collection</param> /// <param name="e">The arguments</param> private void GroupsCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) { //Adds Totals rows if (e.NewItems != null && e.Action != NotifyCollectionChangedAction.Move) { foreach (Group newGroup in e.NewItems) { Category newCategory = new Category(newGroup.Name); try { Group totalGroup; //Gets the income state if (newGroup.IsIncome) { totalGroup = columnIncomeTotalsGroup; } else { totalGroup = columnExpendituresTotalsGroup; } this.BudgetTotals.Add(new MoneyGridRow(totalGroup, newCategory)); this.SpendingTotals.Add(new MoneyGridRow(totalGroup, newCategory)); this.ComparisonTotals.Add(new MoneyGridRow(totalGroup, newCategory)); } catch (ArgumentException ex) { this._errorhandler.DisplayError($"Could not match group to category {newCategory.Name}").Wait(); throw new ArgumentException("Could not match group to category " + newGroup.Name, ex); } } } //Removes totals rows if (e.OldItems != null && e.Action != NotifyCollectionChangedAction.Move) { foreach (Group oldGroup in e.OldItems) { MoneyGridRow oldRow = this.BudgetTotals.Where(row => row.Category.Name.Equals(oldGroup.Name)).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.BudgetTotals.Remove(oldRow); oldRow = this.SpendingTotals.Where(row => row.Category.Name.Equals(oldGroup.Name)).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.SpendingTotals.Remove(oldRow); oldRow = this.ComparisonTotals.Where(row => row.Category.Name.Equals(oldGroup.Name)).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.ComparisonTotals.Remove(oldRow); } } }
/// <summary> /// Move a category closer to the back (N-index) of its group's collection /// </summary> /// <param name="category">The category to move up</param> private void MoveCategoryDown(Category category) { if (category == null) { return; } Group group = GetCategoryGroup(category); if (group == null) { throw new ArgumentException("Category " + category.Name + " does not belong to a group"); } int index = group.Categories.IndexOf(category); if (index < 0) { throw new ArgumentException("Category " + category.Name + " does not exist"); } if (index < group.Categories.Count - 1) { //Move all the Values rows down group.Categories.Move(index, index + 1); MoneyGridRow budgetRow = this.sessionService.BudgetValues.Single(x => x.Category == category); int rowIndex = this.sessionService.BudgetValues.IndexOf(budgetRow); this.sessionService.MoveValueRows(rowIndex, rowIndex + 1); } }
/// <summary> /// Called when the category collection is changed. Used to add/remove Values rows. /// </summary> /// <param name="sender">The modified collection</param> /// <param name="e">The arguments</param> private void CategoryCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) { //Adds Values rows if (e.NewItems != null && e.Action != NotifyCollectionChangedAction.Move) { foreach (Category newCategory in e.NewItems) { Group group = GetCategoryGroup(newCategory); if (group == null) { throw new ArgumentException("Could not match group to category " + newCategory.Name); } this.BudgetValues.Add(new MoneyGridRow(group, newCategory)); this.SpendingValues.Add(new MoneyGridRow(group, newCategory)); this.ComparisonValues.Add(new MoneyGridRow(group, newCategory)); } } //Removes Values rows. Order is important to avoid triggering data that doesn't exist. (1/4/2017: May be fixed now) if (e.OldItems != null && e.Action != NotifyCollectionChangedAction.Move) { foreach (Category oldCategory in e.OldItems) { MoneyGridRow oldRow = this.ComparisonValues.Where(row => row.Category == oldCategory).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.ComparisonValues.Remove(oldRow); oldRow = this.SpendingValues.Where(row => row.Category == oldCategory).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.SpendingValues.Remove(oldRow); oldRow = this.BudgetValues.Where(row => row.Category == oldCategory).ElementAt(0); if (oldRow == null) { throw new ArgumentException("Cannot locate deleted row"); } this.BudgetValues.Remove(oldRow); } } }
/// <summary> /// Fires when a cell is edited in the values grid. /// Allows the user to apply the same value to an entire row with Ctrl+Enter when editing /// </summary> /// <param name="sender">The sending object</param> /// <param name="e">The arguments</param> private void ValuesGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) { //Check if either Control key is pressed if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { //Get the current MoneyGridRow object DataGridCellInfo cellInfo = ValuesGrid.SelectedCells[0]; MoneyGridRow row = cellInfo.Item as MoneyGridRow; //Parse the new value into a decimal decimal currentVal = decimal.Parse((e.EditingElement as TextBox).Text, System.Globalization.NumberStyles.Currency); //Change the other values in the MoneyGridRow //Note: A new array is used to avoid multiple PropertyChanged notifications decimal[] values = new decimal[12]; for (int i = 0; i < row.Values.Count; i++) { values[i] = currentVal; } row.Values.Values = values; } }
private void CalculateColumnTotals(ObservableCollection <MoneyGridRow> columnValues, ObservableCollection <MoneyGridRow> columnTotals, String propertyName) { //Don't do anything if not all the rows have been loaded yet if (columnValues.Count < this.Categories.Count || columnTotals.Count < this.Groups.Count) { return; } double totalGridIncomeTotal = 0.0; double totalGridExpenditureTotal = 0.0; foreach (Group group in this.Groups) //For each group, find its total row and then sum all the category rows that are part of the group { double groupTotal = 0.0; decimal[] groupSum = new decimal[12]; MoneyGridRow total; try { total = columnTotals.Single(x => x.Category.Name.Equals(group.Name)); } catch (Exception ex) { this._errorhandler.DisplayError($"Could not find corresponding total row: {group.Name}").Wait(); throw new ArgumentException("Could not find corresponding total row: " + group.Name, ex); } foreach (Category category in group.Categories) { try { MoneyGridRow row = columnValues.Single(x => x.Group == group && x.Category == category); for (int i = 0; i < row.Values.Count; i++) { groupSum[i] += row.Values[i]; } groupTotal += (double)row.Sum; } catch (Exception ex) { this._errorhandler.DisplayError($"{propertyName} does not contain row for group {group} and category {category}").Wait(); continue; throw new ArgumentException("Could not find corresponding row", ex); } } total.Values.Values = groupSum; groupTotal = (double)total.Sum; if (group.IsIncome) { totalGridIncomeTotal += (double)total.Sum; } else { totalGridExpenditureTotal += (double)total.Sum; } foreach (Category category in group.Categories) { try { MoneyGridRow row = columnValues.Single(x => x.Group == group && x.Category == category); row.Percentage = (double)row.Sum / groupTotal; } catch (Exception ex) { this._errorhandler.DisplayError($"{propertyName} does not contain row for group {group} and category {category}").Wait(); continue; throw new ArgumentException("Could not find corresponding row", ex); } } //RaisePropertyChanged(propertyName); } foreach (MoneyGridRow row in columnTotals) { if (row.Group.IsIncome) { row.Percentage = (double)row.Sum / totalGridIncomeTotal; } else { row.Percentage = (double)row.Sum / totalGridExpenditureTotal; } } }
public async Task LoadDataFromFile(string filePath) { //Clears all existing data this.Groups.Clear(); this.Categories.Clear(); this.Transactions.Clear(); this.PaymentMethods.Clear(); this.BudgetValues.Clear(); this.BudgetTotals.Clear(); this.SpendingValues.Clear(); this.SpendingTotals.Clear(); this.ComparisonValues.Clear(); this.ComparisonTotals.Clear(); Group testGroup = new Model.Group(); Category testCategory = new Category(); MoneyGridRow testRow = new MoneyGridRow(testGroup, testCategory); //this.Groups.Add(testGroup); //this.Categories.Add(testCategory); //this.BudgetValues.Add(testRow); //return; //Retrieves the data using the serialize attributes DataWrapper data = new DataWrapper(); try { using (FileStream file = new FileStream(filePath, FileMode.Open)) { XmlSerializer dataSerializer = new XmlSerializer(typeof(DataWrapper)); data = (DataWrapper)dataSerializer.Deserialize(file); } } catch (IOException ex) //File does not exist; set everything to defaults { await CreateNewFile(Properties.Settings.Default.DefaultDirectory + "\\data_new.xml"); } catch (InvalidOperationException ex) { await this._errorhandler.DisplayError($"Error reading XML from file {filePath}\n{ex.Message}"); } catch (System.Xml.XmlException ex) { await this._errorhandler.DisplayError($"Error in XML file\n{ex.Message}"); } //Process the data this.CurrentYear = data.Year; int index = 0; //Add groups, categories, and budget values List <Group> tempGroups = new List <Group>(); List <Category> tempCategories = new List <Category>(); this.BudgetValues.MemberChanged -= UpdateBudgetTotals; foreach (Group group in data.Groups) { Group newGroup = new Group(group.IsIncome, group.Name); this.Groups.Add(newGroup); foreach (Category category in group.Categories) { newGroup.Categories.Add(category); this.Categories.Add(category); MoneyGridRow row = this.BudgetValues.Single(x => x.Category == category); row.Values.Values = data.BudgetValues.ElementAt(index); index++; } } this.BudgetValues.MemberChanged += UpdateBudgetTotals; RefreshBudgetTotals(); //Add payment methods foreach (PaymentMethod payment in data.PaymentMethods) { this.PaymentMethods.Add(payment); } //Adds the transactions //Transactions are stored with different instances of the category and payment method objects. These need to //be matched to the data loaded above. Matching is done using the name of each. List <Transaction> tempTransactions = new List <Transaction>(); foreach (Transaction transaction in data.Transactions) { string categoryName = transaction.Category.Name; string paymentName = transaction.PaymentMethod.Name; try { transaction.Category = this.Categories.Single(x => x.Name.Equals(categoryName)); } catch (ArgumentException ex) { throw new ArgumentException("Cannot find matching category " + transaction.Category.Name + " in categories list"); } try { transaction.PaymentMethod = this.PaymentMethods.Single(x => x.Name.Equals(paymentName)); } catch (ArgumentException ex) { throw new ArgumentException("Cannot find matching payment method " + transaction.PaymentMethod.Name + " in payment methods list"); } tempTransactions.Add(transaction); } this.Transactions.InsertRange(tempTransactions); }
private void RefreshBudgetPieChart() { List <MoneyGridRow> filteredRows = new List <MoneyGridRow>(); foreach (MoneyGridRow row in this.Session.BudgetValues) { MoneyGridRow tempRow = row.Copy(); if (this.BudgetGraphConfiguration.Filter(tempRow)) { filteredRows.Add(tempRow); } } this.Series.Clear(); this.PointLabel = this.FormatPieChartLabel; decimal sum = 0.0M; switch (this.BudgetGraphConfiguration.Grouping) { case BudgetGraphGrouping.Category: foreach (Category category in this.Session.Categories) { sum = 0.0M; sum = filteredRows.Where(x => x.Category.Equals(category)).Sum(x => x.Sum); if (sum < 0.0M) { sum = 0.0M; } this.Series.Add(new PieSeries { Title = category.Name, Values = new ChartValues <decimal> { sum }, DataLabels = true, LabelPoint = this.PointLabel, LabelPosition = PieLabelPosition.OutsideSlice, }); } break; case BudgetGraphGrouping.Month: for (int i = 0; i < 12; i++) { sum = 0.0M; sum = filteredRows.Select(x => x.Values[i]).Sum(); if (sum < 0.0M) { sum = 0.0M; } this.Series.Add(new PieSeries { Title = (new DateTime(1, i + 1, 1)).ToString("MMMM"), Values = new ChartValues <decimal> { sum }, DataLabels = true, LabelPoint = this.PointLabel, LabelPosition = PieLabelPosition.OutsideSlice }); } break; case BudgetGraphGrouping.Group: foreach (Group group in this.Session.Groups) { sum = 0.0M; sum = filteredRows.Where(x => x.Group.Equals(group)).Sum(x => x.Sum); if (sum < 0.0M) { sum = 0.0M; } this.Series.Add(new PieSeries { Title = group.Name, Values = new ChartValues <decimal> { sum }, DataLabels = true, LabelPoint = this.PointLabel, LabelPosition = PieLabelPosition.OutsideSlice }); } break; case BudgetGraphGrouping.IsIncome: sum = 0.0M; sum += filteredRows.Where(x => x.Group.IsIncome).Sum(x => x.Sum); if (sum < 0.0M) { sum = 0.0M; } this.Series.Add(new PieSeries { Title = "Income", Values = new ChartValues <decimal> { sum }, DataLabels = true, LabelPoint = this.PointLabel, LabelPosition = PieLabelPosition.OutsideSlice }); sum = 0.0M; sum += filteredRows.Where(x => !x.Group.IsIncome).Sum(x => x.Sum); if (sum < 0.0M) { sum = 0.0M; } this.Series.Add(new PieSeries { Title = "Expenditures", Values = new ChartValues <decimal> { sum }, DataLabels = true, LabelPoint = this.PointLabel, LabelPosition = PieLabelPosition.OutsideSlice }); break; default: break; } }