From 9929eee056787363a849ffee5310d4e4e44ecc60 Mon Sep 17 00:00:00 2001 From: gaoyuan Date: Wed, 2 Mar 2022 22:29:13 +0800 Subject: [PATCH] first step --- Billing.Shared/Languages/Resource.cs | 6 + Billing.Shared/Languages/en.xml | 8 ++ Billing.Shared/Languages/zh-CN.xml | 8 ++ Billing.Shared/Models/Bill.cs | 6 + Billing.Shared/Store/StoreHelper.cs | 19 ++- Billing.Shared/Themes/Light.cs | 2 +- Billing.Shared/UI/Definition.cs | 7 + Billing.Shared/UI/GroupStackLayout.cs | 3 +- Billing.Shared/UI/OptionsCells.cs | 6 +- Billing.Shared/Views/AccountPage.xaml | 16 ++- Billing.Shared/Views/AccountPage.xaml.cs | 23 +++- Billing.Shared/Views/AddAccountPage.xaml | 1 - Billing.Shared/Views/AddAccountPage.xaml.cs | 49 ++++--- Billing.Shared/Views/AddBillPage.xaml | 27 ++-- Billing.Shared/Views/AddBillPage.xaml.cs | 134 +++++++++++++++++++- Billing.Shared/Views/BillPage.xaml | 15 ++- Billing.Shared/Views/BillPage.xaml.cs | 73 ++++++++++- 17 files changed, 343 insertions(+), 60 deletions(-) diff --git a/Billing.Shared/Languages/Resource.cs b/Billing.Shared/Languages/Resource.cs index 261f260..4ce5e2e 100644 --- a/Billing.Shared/Languages/Resource.cs +++ b/Billing.Shared/Languages/Resource.cs @@ -9,6 +9,7 @@ namespace Billing.Languages { internal class Resource { + public static string Ok => Text(nameof(Ok)); public static string TitleDateFormat => Text(nameof(TitleDateFormat)); public static string Cash => Text(nameof(Cash)); public static string CreditCard => Text(nameof(CreditCard)); @@ -16,6 +17,11 @@ namespace Billing.Languages public static string ElecAccount => Text(nameof(ElecAccount)); public static string AddBill => Text(nameof(AddBill)); public static string EditBill => Text(nameof(EditBill)); + public static string AddAccount => Text(nameof(AddAccount)); + public static string EditAccount => Text(nameof(EditAccount)); + public static string AccountRequired => Text(nameof(AccountRequired)); + public static string NeedAccount => Text(nameof(NeedAccount)); + public static string AmountRequired => Text(nameof(AmountRequired)); static readonly Dictionary dict = new(); diff --git a/Billing.Shared/Languages/en.xml b/Billing.Shared/Languages/en.xml index dd6340f..a5e2626 100644 --- a/Billing.Shared/Languages/en.xml +++ b/Billing.Shared/Languages/en.xml @@ -1,5 +1,6 @@ + OK Accounts Bills Settings @@ -17,8 +18,10 @@ Assets Liability Add Account + Edit Account Account Name Please enter account name + The account name is required. Icon Category Balance @@ -34,6 +37,11 @@ Icon Selection Add Billing Edit Billing + Name + Please enter the name Account Created Time + Store + Please create an account first. + Please enter the amount. \ No newline at end of file diff --git a/Billing.Shared/Languages/zh-CN.xml b/Billing.Shared/Languages/zh-CN.xml index 1933fd2..f3f2c7a 100644 --- a/Billing.Shared/Languages/zh-CN.xml +++ b/Billing.Shared/Languages/zh-CN.xml @@ -1,5 +1,6 @@ + 确定 账户 账单 设置 @@ -17,8 +18,10 @@ 资产 负债 新建账户 + 编辑账户 账户名称 请输入账户名称 + 账户名称不可为空。 图标 种类 余额 @@ -34,6 +37,11 @@ 图标选择 增加账单 编辑账单 + 名称 + 请输入名称 账户 创建时间 + 店铺 + 请先创建一个资金账户。 + 请输入金额。 \ No newline at end of file diff --git a/Billing.Shared/Models/Bill.cs b/Billing.Shared/Models/Bill.cs index 5e5242e..4ed804f 100644 --- a/Billing.Shared/Models/Bill.cs +++ b/Billing.Shared/Models/Bill.cs @@ -5,31 +5,37 @@ namespace Billing.Models { public class Bill : BaseModel { + public int Id { get; set; } public decimal Amount { get; set; } public string Name { get; set; } public int CategoryId { get; set; } public int WalletId { get; set; } public string Store { get; set; } public DateTime CreateTime { get; set; } + public string Note { get; set; } public override void OnXmlDeserialize(XElement node) { + Id = Read(node, nameof(Id), 0); Amount = Read(node, nameof(Amount), 0m); Name = Read(node, nameof(Name), string.Empty); CategoryId = Read(node, nameof(CategoryId), -1); WalletId = Read(node, nameof(WalletId), -1); Store = Read(node, nameof(Store), string.Empty); CreateTime = Read(node, nameof(CreateTime), default(DateTime)); + Note = Read(node, nameof(Note), string.Empty); } public override void OnXmlSerialize(XElement node) { + Write(node, nameof(Id), Id); Write(node, nameof(Amount), Amount); Write(node, nameof(Name), Name); Write(node, nameof(CategoryId), CategoryId); Write(node, nameof(WalletId), WalletId); Write(node, nameof(Store), Store); Write(node, nameof(CreateTime), CreateTime); + Write(node, nameof(Note), Note); } } } \ No newline at end of file diff --git a/Billing.Shared/Store/StoreHelper.cs b/Billing.Shared/Store/StoreHelper.cs index a533a92..4c161fd 100644 --- a/Billing.Shared/Store/StoreHelper.cs +++ b/Billing.Shared/Store/StoreHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using Billing.Models; using Billing.UI; using Xamarin.Essentials; @@ -52,7 +53,21 @@ namespace Billing.Store private List GetCategoriesInternal() { - return GetList(Path.Combine(PersonalFolder, categoryFile)); + var list = GetList(Path.Combine(PersonalFolder, categoryFile)); + if (list == null || list.Count == 0) + { + list = new List + { + // TODO: sample categories + new() { Id = 0, Name = "早餐", Icon = "face" }, + new() { Id = 1, Name = "轻轨", Icon = "" }, + new() { Id = 2, Name = "公交车", Icon = "" }, + new() { Id = 3, Name = "出租车", Icon = "" }, + new() { Id = 4, Type = CategoryType.Income, Name = "投资", Icon = "#brand#btc" } + }; + Task.Run(() => WriteCategoriesInternal(list)); + } + return list; } private void WriteCategoriesInternal(IEnumerable categories) @@ -71,7 +86,7 @@ namespace Billing.Store } try { - using var stream = File.OpenWrite(filename); + using var stream = File.Open(filename, FileMode.Create); list.ToStream(stream); } catch (Exception ex) diff --git a/Billing.Shared/Themes/Light.cs b/Billing.Shared/Themes/Light.cs index 5511bd2..e9a3409 100644 --- a/Billing.Shared/Themes/Light.cs +++ b/Billing.Shared/Themes/Light.cs @@ -28,7 +28,7 @@ namespace Billing.Themes Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33)); Add(SecondaryTextColor, Color.DimGray); Add(RedColor, Color.FromRgb(211, 64, 85)); - Add(RedColor, Color.FromRgb(64, 211, 85)); + Add(GreenColor, Color.FromRgb(64, 211, 85)); Add(new Style(typeof(TabBar)) { diff --git a/Billing.Shared/UI/Definition.cs b/Billing.Shared/UI/Definition.cs index e63c46f..2157510 100644 --- a/Billing.Shared/UI/Definition.cs +++ b/Billing.Shared/UI/Definition.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using System.Xml.Linq; +using Billing.Languages; using Billing.Models; using Xamarin.Forms; @@ -77,6 +79,11 @@ namespace Billing.UI Grid.SetRowSpan(view, rowSpan); return view; } + + public static async Task ShowMessage(this Page page, string message, string title = null) + { + await page.DisplayAlert(title ?? page.Title, message, Resource.Ok); + } } public static class ModelExtensionHelper diff --git a/Billing.Shared/UI/GroupStackLayout.cs b/Billing.Shared/UI/GroupStackLayout.cs index 053d57e..a61f7c9 100644 --- a/Billing.Shared/UI/GroupStackLayout.cs +++ b/Billing.Shared/UI/GroupStackLayout.cs @@ -155,7 +155,8 @@ namespace Billing.UI itemHeight = rowHeight; } var rect = new Rectangle(0, lastHeight, width, itemHeight); - item.Layout(rect); + //item.Layout(rect); + LayoutChildIntoBoundingRegion(item, rect); lastHeight += itemHeight + spacing; } } diff --git a/Billing.Shared/UI/OptionsCells.cs b/Billing.Shared/UI/OptionsCells.cs index b9f1a1a..ea973d8 100644 --- a/Billing.Shared/UI/OptionsCells.cs +++ b/Billing.Shared/UI/OptionsCells.cs @@ -245,7 +245,7 @@ namespace Billing.UI HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center } - .Binding(Switch.IsToggledProperty, nameof(IsToggled), BindingMode.TwoWay); + .Binding(Switch.IsToggledProperty, nameof(IsToggled), mode: BindingMode.TwoWay); } public class OptionEntryCell : OptionCell @@ -283,7 +283,7 @@ namespace Billing.UI VerticalOptions = LayoutOptions.Center, ReturnType = ReturnType.Next } - .Binding(Entry.TextProperty, nameof(Text), BindingMode.TwoWay) + .Binding(Entry.TextProperty, nameof(Text), mode: BindingMode.TwoWay) .Binding(InputView.KeyboardProperty, nameof(Keyboard)) .Binding(Entry.PlaceholderProperty, nameof(Placeholder)) .DynamicResource(Entry.TextColorProperty, BaseTheme.TextColor) @@ -334,7 +334,7 @@ namespace Billing.UI HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill } - .Binding(Editor.TextProperty, nameof(Text), BindingMode.TwoWay) + .Binding(Editor.TextProperty, nameof(Text), mode: BindingMode.TwoWay) .Binding(Editor.FontSizeProperty, nameof(FontSize)) .Binding(InputView.KeyboardProperty, nameof(Keyboard)) .Binding(Editor.PlaceholderProperty, nameof(Placeholder)) diff --git a/Billing.Shared/Views/AccountPage.xaml b/Billing.Shared/Views/AccountPage.xaml index be2bcd5..118af0c 100644 --- a/Billing.Shared/Views/AccountPage.xaml +++ b/Billing.Shared/Views/AccountPage.xaml @@ -19,7 +19,7 @@ - + @@ -57,17 +57,21 @@ - + + + + - + + diff --git a/Billing.Shared/Views/AccountPage.xaml.cs b/Billing.Shared/Views/AccountPage.xaml.cs index c089ff8..a531815 100644 --- a/Billing.Shared/Views/AccountPage.xaml.cs +++ b/Billing.Shared/Views/AccountPage.xaml.cs @@ -19,14 +19,14 @@ namespace Billing.Views public decimal Liability => (decimal)GetValue(LiabilityProperty); public List Accounts => (List)GetValue(AccountsProperty); - public Command AddAccount { get; } + public Command EditAccount { get; } private readonly List accounts = new(); private bool initialized; public AccountPage() { - AddAccount = new Command(OnAddAccount); + EditAccount = new Command(OnEditAccount); SetValue(AccountsProperty, accounts); InitializeComponent(); @@ -40,9 +40,17 @@ namespace Billing.Views accounts.Clear(); foreach (var account in App.Accounts) { + account.Balance = account.Initial + App.Bills.Where(b => b.WalletId == account.Id).Sum(b => b.Amount); AddToAccountGroup(account); } } + else + { + foreach (var account in App.Accounts) + { + account.Balance = account.Initial + App.Bills.Where(b => b.WalletId == account.Id).Sum(b => b.Amount); + } + } groupLayout.Refresh(accounts); } @@ -72,7 +80,7 @@ namespace Billing.Views } } - private async void OnAddAccount() + private async void OnEditAccount(object o) { if (Tap.IsBusy) { @@ -80,7 +88,7 @@ namespace Billing.Views } using (Tap.Start()) { - var page = new AddAccountPage(); + var page = new AddAccountPage(o as Account); page.AccountChecked += AccountChecked; await Navigation.PushAsync(page); } @@ -88,8 +96,11 @@ namespace Billing.Views private void AccountChecked(object sender, AccountEventArgs e) { - App.Accounts.Add(e.Account); - AddToAccountGroup(e.Account); + if (e.Account.Id < 0) + { + App.Accounts.Add(e.Account); + AddToAccountGroup(e.Account); + } groupLayout.Refresh(accounts); Task.Run(App.WriteAccounts); diff --git a/Billing.Shared/Views/AddAccountPage.xaml b/Billing.Shared/Views/AddAccountPage.xaml index 72c92fb..735ef92 100644 --- a/Billing.Shared/Views/AddAccountPage.xaml +++ b/Billing.Shared/Views/AddAccountPage.xaml @@ -7,7 +7,6 @@ x:Class="Billing.Views.AddAccountPage" x:Name="addAccountPage" x:DataType="v:AddAccountPage" - Title="{r:Text AddAccount}" BindingContext="{x:Reference addAccountPage}"> diff --git a/Billing.Shared/Views/AddAccountPage.xaml.cs b/Billing.Shared/Views/AddAccountPage.xaml.cs index edd15c3..bac3616 100644 --- a/Billing.Shared/Views/AddAccountPage.xaml.cs +++ b/Billing.Shared/Views/AddAccountPage.xaml.cs @@ -49,29 +49,27 @@ namespace Billing.Views public event EventHandler AccountChecked; - public AddAccountPage() - { - CheckAccount = new Command(OnCheckAccount); - SelectIcon = new Command(OnSelectIcon); - SelectCategory = new Command(OnSelectCategory); - - AccountIcon = BaseModel.ICON_DEFAULT; - Category = AccountCategory.Cash; - InitializeComponent(); - } - - public AddAccountPage(Account account) + public AddAccountPage(Account account = null) { CheckAccount = new Command(OnCheckAccount); SelectIcon = new Command(OnSelectIcon); SelectCategory = new Command(OnSelectCategory); + Title = Resource.EditAccount; this.account = account; - AccountName = account.Name; - AccountIcon = account.Icon; - Category = account.Category; - Initial = account.Initial.ToString(); - Memo = account.Memo; + if (account == null) + { + AccountIcon = BaseModel.ICON_DEFAULT; + Category = AccountCategory.Cash; + } + else + { + AccountName = account.Name; + AccountIcon = account.Icon; + Category = account.Category; + Initial = account.Initial.ToString(); + Memo = account.Memo; + } InitializeComponent(); } @@ -83,13 +81,26 @@ namespace Billing.Views } using (Tap.Start()) { + if (string.IsNullOrWhiteSpace(AccountName)) + { + await this.ShowMessage(Resource.AccountRequired); + return; + } await Navigation.PopAsync(); _ = decimal.TryParse(Initial, out decimal initial); + if (account != null) + { + account.Name = AccountName; + account.Icon = AccountIcon; + account.Category = Category; + account.Initial = initial; + account.Memo = Memo; + } AccountChecked?.Invoke(this, new AccountEventArgs { - Account = new Account + Account = account ?? new Account { - Id = account?.Id ?? -1, + Id = -1, Name = AccountName, Icon = AccountIcon, Category = Category, diff --git a/Billing.Shared/Views/AddBillPage.xaml b/Billing.Shared/Views/AddBillPage.xaml index 3bb68f4..fb85d3c 100644 --- a/Billing.Shared/Views/AddBillPage.xaml +++ b/Billing.Shared/Views/AddBillPage.xaml @@ -16,27 +16,32 @@ - + - - + + + Detail="{Binding CreatedTime}"/> @@ -44,7 +49,7 @@ diff --git a/Billing.Shared/Views/AddBillPage.xaml.cs b/Billing.Shared/Views/AddBillPage.xaml.cs index ca2e72b..9934f42 100644 --- a/Billing.Shared/Views/AddBillPage.xaml.cs +++ b/Billing.Shared/Views/AddBillPage.xaml.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Linq; using Billing.Languages; using Billing.Models; using Billing.UI; @@ -8,28 +10,158 @@ namespace Billing.Views { public partial class AddBillPage : BillingPage { + private static readonly BindableProperty AmountProperty = BindableProperty.Create(nameof(Amount), typeof(string), typeof(AddBillPage)); + private static readonly BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(AddBillPage)); + private static readonly BindableProperty CategoryNameProperty = BindableProperty.Create(nameof(CategoryName), typeof(string), typeof(AddBillPage)); + private static readonly BindableProperty WalletNameProperty = BindableProperty.Create(nameof(WalletName), typeof(string), typeof(AddBillPage)); + private static readonly BindableProperty StoreProperty = BindableProperty.Create(nameof(Store), typeof(string), typeof(AddBillPage)); + private static readonly BindableProperty CreatedTimeProperty = BindableProperty.Create(nameof(CreatedTime), typeof(DateTime), typeof(AddBillPage)); + private static readonly BindableProperty NoteProperty = BindableProperty.Create(nameof(Note), typeof(string), typeof(AddBillPage)); + + public string Amount + { + get => (string)GetValue(AmountProperty); + set => SetValue(AmountProperty, value); + } + public string Name + { + get => (string)GetValue(NameProperty); + set => SetValue(NameProperty, value); + } + public string CategoryName => (string)GetValue(CategoryNameProperty); + public string WalletName => (string)GetValue(WalletNameProperty); + public string Store + { + get => (string)GetValue(StoreProperty); + set => SetValue(StoreProperty, value); + } + public DateTime CreatedTime + { + get => (DateTime)GetValue(CreatedTimeProperty); + set => SetValue(CreatedTimeProperty, value); + } + public string Note + { + get => (string)GetValue(NoteProperty); + set => SetValue(NoteProperty, value); + } + public Command CheckBill { get; } + public Command SelectCategory { get; } + public Command SelectWallet { get; } + + public event EventHandler BillChecked; private readonly Bill bill; private readonly DateTime createDate; + private int walletId; + private int categoryId; + public AddBillPage(DateTime date) { createDate = date; CheckBill = new Command(OnCheckBill); + SelectCategory = new Command(OnSelectCategory); + SelectWallet = new Command(OnSelectWallet); InitializeComponent(); Title = Resource.AddBill; + + Initial(); } public AddBillPage(Bill bill) { this.bill = bill; CheckBill = new Command(OnCheckBill); + SelectCategory = new Command(OnSelectCategory); + SelectWallet = new Command(OnSelectWallet); InitializeComponent(); Title = Resource.EditBill; + + Initial(); } - private void OnCheckBill() + private void Initial() + { + if (bill != null) + { + Amount = bill.Amount.ToString(CultureInfo.InvariantCulture); + Name = bill.Name; + walletId = bill.WalletId; + categoryId = bill.CategoryId; + SetValue(WalletNameProperty, App.Accounts.FirstOrDefault(a => a.Id == walletId)?.Name); + SetValue(CategoryNameProperty, App.Categories.FirstOrDefault(c => c.Id == categoryId)?.Name); + Store = bill.Store; + CreatedTime = bill.CreateTime; + Note = bill.Note; + } + else + { + var first = App.Accounts.First(); + walletId = first.Id; + SetValue(WalletNameProperty, first.Name); + var firstCategory = App.Categories.First(); + categoryId = firstCategory.Id; + SetValue(CategoryNameProperty, firstCategory.Name); + CreatedTime = createDate.Date.Add(DateTime.Now.TimeOfDay); + } + } + + private async void OnCheckBill() + { + if (Tap.IsBusy) + { + return; + } + using (Tap.Start()) + { + if (!decimal.TryParse(Amount, out decimal amount)) + { + return; + } + if (amount == 0) + { + await this.ShowMessage(Resource.AmountRequired); + return; + } + amount = Math.Abs(amount); + var category = App.Categories.FirstOrDefault(c => c.Id == categoryId); + if (category.Type == CategoryType.Spending) + { + amount *= -1; + } + await Navigation.PopAsync(); + if (bill != null) + { + bill.Amount = amount; + bill.Name = Name; + bill.CategoryId = categoryId; + bill.WalletId = walletId; + bill.CreateTime = CreatedTime; + bill.Store = Store; + bill.Note = Note; + } + BillChecked?.Invoke(this, bill ?? new Bill + { + Id = -1, + Amount = amount, + Name = Name, + CategoryId = categoryId, + WalletId = walletId, + CreateTime = CreatedTime, + Store = Store, + Note = Note + }); + } + } + + private void OnSelectCategory() + { + + } + + private void OnSelectWallet() { } diff --git a/Billing.Shared/Views/BillPage.xaml b/Billing.Shared/Views/BillPage.xaml index 354c4f5..daa1970 100644 --- a/Billing.Shared/Views/BillPage.xaml +++ b/Billing.Shared/Views/BillPage.xaml @@ -30,6 +30,10 @@ + + + + - + - + + + + diff --git a/Billing.Shared/Views/BillPage.xaml.cs b/Billing.Shared/Views/BillPage.xaml.cs index c8cb0b0..2e6afac 100644 --- a/Billing.Shared/Views/BillPage.xaml.cs +++ b/Billing.Shared/Views/BillPage.xaml.cs @@ -1,8 +1,10 @@ +using Billing.Languages; using Billing.Models; using Billing.UI; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Xamarin.Forms; namespace Billing.Views @@ -23,11 +25,11 @@ namespace Billing.Views set => SetValue(BillsProperty, value); } - public Command AddBilling { get; } + public Command EditBilling { get; } public BillPage() { - AddBilling = new Command(OnAddBilling); + EditBilling = new Command(OnEditBilling); InitializeComponent(); @@ -42,14 +44,28 @@ namespace Billing.Views b.CreateTime.Year == e.Date.Year && b.CreateTime.Month == e.Date.Month && b.CreateTime.Day == e.Date.Day); - Bills = new List(bills.Select(b => new UIBill(b) + Bills = new List(bills.Select(b => WrapBill(b))); + } + + private UIBill WrapBill(Bill b) + { + return new UIBill(b) { Icon = App.Categories.FirstOrDefault(c => c.Id == b.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT, Name = b.Name, DateCreation = b.CreateTime, Amount = b.Amount, Wallet = App.Accounts.FirstOrDefault(a => a.Id == b.WalletId)?.Name - })); + }; + } + + private void UpdateBill(UIBill bill) + { + bill.Icon = App.Categories.FirstOrDefault(c => c.Id == bill.Bill.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT; + bill.Name = bill.Bill.Name; + bill.DateCreation = bill.Bill.CreateTime; + bill.Amount = bill.Bill.Amount; + bill.Wallet = App.Accounts.FirstOrDefault(a => a.Id == bill.Bill.WalletId)?.Name; } private void OnTitleDateLongPressed(object sender, EventArgs e) @@ -57,7 +73,7 @@ namespace Billing.Views billingDate.SetDateTime(DateTime.Now); } - private async void OnAddBilling() + private async void OnEditBilling(object o) { if (Tap.IsBusy) { @@ -65,10 +81,55 @@ namespace Billing.Views } using (Tap.Start()) { - var page = new AddBillPage(SelectedDate); + AddBillPage page; + if (o is UIBill bill) + { + page = new AddBillPage(bill.Bill); + } + else + { + if (App.Accounts.Count == 0) + { + await this.ShowMessage(Resource.NeedAccount); + await Shell.Current.GoToAsync("//Accounts"); + return; + } + page = new AddBillPage(SelectedDate); + } + page.BillChecked += OnBillChecked; await Navigation.PushAsync(page); } } + + private void OnBillChecked(object sender, Bill e) + { + if (e.Id < 0) + { + int maxId; + if (App.Bills.Count > 0) + { + maxId = App.Bills.Max(b => b.Id); + } + else + { + maxId = -1; + } + e.Id = maxId + 1; + App.Bills.Add(e); + Bills.Add(WrapBill(e)); + billsLayout.Refresh(Bills); + } + else + { + var bill = Bills.FirstOrDefault(b => b.Bill == e); + if (bill != null) + { + UpdateBill(bill); + } + } + + Task.Run(App.WriteBills); + } } public class UIBill : BindableObject