language version degraded to 9.0
This commit is contained in:
parent
9a6011c3d8
commit
e012110b00
@ -3,59 +3,60 @@ using Billing.Themes;
|
|||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing;
|
namespace Billing
|
||||||
|
|
||||||
public class App : Application
|
|
||||||
{
|
{
|
||||||
public static AppTheme CurrentTheme { get; private set; }
|
public class App : Application
|
||||||
public static PlatformCulture CurrentCulture { get; private set; }
|
|
||||||
|
|
||||||
public App()
|
|
||||||
{
|
{
|
||||||
CurrentCulture = new PlatformCulture();
|
public static AppTheme CurrentTheme { get; private set; }
|
||||||
InitResources();
|
public static PlatformCulture CurrentCulture { get; private set; }
|
||||||
|
|
||||||
MainPage = new MainShell();
|
public App()
|
||||||
Shell.Current.GoToAsync("//Bills");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStart()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnResume()
|
|
||||||
{
|
|
||||||
SetTheme(AppInfo.RequestedTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitResources()
|
|
||||||
{
|
|
||||||
var theme = AppInfo.RequestedTheme;
|
|
||||||
SetTheme(theme, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTheme(AppTheme theme, bool force = false)
|
|
||||||
{
|
|
||||||
if (force || theme != CurrentTheme)
|
|
||||||
{
|
{
|
||||||
CurrentTheme = theme;
|
CurrentCulture = new PlatformCulture();
|
||||||
}
|
InitResources();
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Helper.Debug($"application theme: {theme}");
|
|
||||||
|
|
||||||
BaseTheme instance;
|
MainPage = new MainShell();
|
||||||
if (theme == AppTheme.Dark)
|
Shell.Current.GoToAsync("//Bills");
|
||||||
{
|
|
||||||
instance = Dark.Instance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
instance = Light.Instance;
|
|
||||||
}
|
}
|
||||||
// TODO: status bar
|
|
||||||
Resources = instance;
|
protected override void OnResume()
|
||||||
|
{
|
||||||
|
SetTheme(AppInfo.RequestedTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitResources()
|
||||||
|
{
|
||||||
|
var theme = AppInfo.RequestedTheme;
|
||||||
|
SetTheme(theme, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTheme(AppTheme theme, bool force = false)
|
||||||
|
{
|
||||||
|
if (force || theme != CurrentTheme)
|
||||||
|
{
|
||||||
|
CurrentTheme = theme;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Helper.Debug($"application theme: {theme}");
|
||||||
|
|
||||||
|
BaseTheme instance;
|
||||||
|
if (theme == AppTheme.Dark)
|
||||||
|
{
|
||||||
|
instance = Dark.Instance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instance = Light.Instance;
|
||||||
|
}
|
||||||
|
// TODO: status bar
|
||||||
|
Resources = instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,23 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Billing;
|
namespace Billing
|
||||||
|
|
||||||
internal static class Helper
|
|
||||||
{
|
{
|
||||||
public static void Debug(string message)
|
internal static class Helper
|
||||||
{
|
{
|
||||||
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
public static void Debug(string message)
|
||||||
System.Diagnostics.Debug.WriteLine($"[{time}] - {message}");
|
{
|
||||||
}
|
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[{time}] - {message}");
|
||||||
|
}
|
||||||
|
|
||||||
public static void Error(string category, Exception ex)
|
public static void Error(string category, Exception ex)
|
||||||
{
|
{
|
||||||
Error(category, ex?.Message ?? "unknown error");
|
Error(category, ex?.Message ?? "unknown error");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Error(string category, string message)
|
public static void Error(string category, string message)
|
||||||
{
|
{
|
||||||
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||||
System.Diagnostics.Debug.Fail($"[{time}] - {category}", message);
|
System.Diagnostics.Debug.Fail($"[{time}] - {category}", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,7 +36,7 @@ namespace Billing.Languages
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (strings?.TryGetValue(key, out string val) == true)
|
if (strings != null && strings.TryGetValue(key, out string val))
|
||||||
{
|
{
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing;
|
namespace Billing
|
||||||
|
|
||||||
public partial class MainShell : Shell
|
|
||||||
{
|
{
|
||||||
public MainShell()
|
public partial class MainShell : Shell
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
public MainShell()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +1,42 @@
|
|||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Billing.Models;
|
namespace Billing.Models
|
||||||
|
|
||||||
public class Account : BaseModel
|
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public class Account : BaseModel
|
||||||
public string Icon { get; set; } = ICON_DEFAULT;
|
|
||||||
public AccountCategory Category { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public decimal Balance { get; set; }
|
|
||||||
public string Memo { get; set; }
|
|
||||||
|
|
||||||
public override void OnXmlDeserialize(XElement node)
|
|
||||||
{
|
{
|
||||||
Id = Read(node, nameof(Id), 0);
|
public int Id { get; set; }
|
||||||
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
public string Icon { get; set; } = ICON_DEFAULT;
|
||||||
Category = (AccountCategory)Read(node, nameof(Category), 0);
|
public AccountCategory Category { get; set; }
|
||||||
Name = Read(node, nameof(Name), string.Empty);
|
public string Name { get; set; }
|
||||||
Balance = Read(node, nameof(Balance), 0m);
|
public decimal Balance { get; set; }
|
||||||
Memo = Read(node, nameof(Memo), null);
|
public string Memo { get; set; }
|
||||||
|
|
||||||
|
public override void OnXmlDeserialize(XElement node)
|
||||||
|
{
|
||||||
|
Id = Read(node, nameof(Id), 0);
|
||||||
|
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
||||||
|
Category = (AccountCategory)Read(node, nameof(Category), 0);
|
||||||
|
Name = Read(node, nameof(Name), string.Empty);
|
||||||
|
Balance = Read(node, nameof(Balance), 0m);
|
||||||
|
Memo = Read(node, nameof(Memo), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnXmlSerialize(XElement node)
|
||||||
|
{
|
||||||
|
Write(node, nameof(Id), Id);
|
||||||
|
Write(node, nameof(Icon), Icon);
|
||||||
|
Write(node, nameof(Category), (int)Category);
|
||||||
|
Write(node, nameof(Name), Name);
|
||||||
|
Write(node, nameof(Balance), Balance);
|
||||||
|
Write(node, nameof(Memo), Memo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnXmlSerialize(XElement node)
|
public enum AccountCategory
|
||||||
{
|
{
|
||||||
Write(node, nameof(Id), Id);
|
Cash = 0,
|
||||||
Write(node, nameof(Icon), Icon);
|
CreditCard,
|
||||||
Write(node, nameof(Category), (int)Category);
|
DebitCard,
|
||||||
Write(node, nameof(Name), Name);
|
ElecAccount
|
||||||
Write(node, nameof(Balance), Balance);
|
|
||||||
Write(node, nameof(Memo), Memo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AccountCategory
|
|
||||||
{
|
|
||||||
Cash = 0,
|
|
||||||
CreditCard,
|
|
||||||
DebitCard,
|
|
||||||
ElecAccount
|
|
||||||
}
|
|
@ -5,180 +5,181 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Billing.Models;
|
namespace Billing.Models
|
||||||
|
|
||||||
public interface IModel
|
|
||||||
{
|
{
|
||||||
void OnXmlSerialize(XElement node);
|
public interface IModel
|
||||||
|
|
||||||
void OnXmlDeserialize(XElement node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class BaseModel : IModel, IDisposable
|
|
||||||
{
|
|
||||||
protected const string ICON_DEFAULT = "ic_default";
|
|
||||||
|
|
||||||
private bool disposed = false;
|
|
||||||
|
|
||||||
public static T ParseXml<T>(string xml) where T : BaseModel, new()
|
|
||||||
{
|
{
|
||||||
XDocument doc = XDocument.Parse(xml);
|
void OnXmlSerialize(XElement node);
|
||||||
T model = new();
|
|
||||||
model.OnXmlDeserialize(doc.Root);
|
void OnXmlDeserialize(XElement node);
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static string ToString(IFormattable v) => v.ToString(null, CultureInfo.InvariantCulture);
|
public abstract class BaseModel : IModel, IDisposable
|
||||||
protected static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
|
|
||||||
protected static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture);
|
|
||||||
protected static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture);
|
|
||||||
protected static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
protected static bool IsTrue(string s) =>
|
|
||||||
string.Equals(s, "true", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(s, "yes", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
s == "1";
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private static XElement WriteString(XElement parent, string name, string val, Action<XElement> action = null)
|
|
||||||
{
|
{
|
||||||
XElement ele;
|
protected const string ICON_DEFAULT = "ic_default";
|
||||||
if (val == null)
|
|
||||||
|
private bool disposed = false;
|
||||||
|
|
||||||
|
public static T ParseXml<T>(string xml) where T : BaseModel, new()
|
||||||
{
|
{
|
||||||
ele = new XElement(name);
|
XDocument doc = XDocument.Parse(xml);
|
||||||
|
T model = new();
|
||||||
|
model.OnXmlDeserialize(doc.Root);
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
protected static string ToString(IFormattable v) => v.ToString(null, CultureInfo.InvariantCulture);
|
||||||
|
protected static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
|
||||||
|
protected static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture);
|
||||||
|
protected static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture);
|
||||||
|
protected static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
protected static bool IsTrue(string s) =>
|
||||||
|
string.Equals(s, "true", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(s, "yes", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
s == "1";
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private static XElement WriteString(XElement parent, string name, string val, Action<XElement> action = null)
|
||||||
{
|
{
|
||||||
ele = new XElement(name, val);
|
XElement ele;
|
||||||
}
|
if (val == null)
|
||||||
action?.Invoke(ele);
|
|
||||||
parent.Add(ele);
|
|
||||||
return ele;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T ReadSubnode<T>(XElement node, string subname, Func<XElement, T> func)
|
|
||||||
{
|
|
||||||
var ele = node.Elements().FirstOrDefault(e => string.Equals(e.Name.ToString(), subname, StringComparison.OrdinalIgnoreCase));
|
|
||||||
return func(ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T ReadObject<T>(XElement node) where T : IModel, new()
|
|
||||||
{
|
|
||||||
if (IsTrue(node.Attribute("null")?.Value))
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
T value = new();
|
|
||||||
value.OnXmlDeserialize(node);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T[] ReadArray<T>(XElement node) where T : IModel, new()
|
|
||||||
{
|
|
||||||
if (IsTrue(node.Attribute("null")?.Value))
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
int count = ParseInt(node.Attribute("count").Value);
|
|
||||||
T[] array = new T[count];
|
|
||||||
foreach (XElement ele in node.Elements("item"))
|
|
||||||
{
|
|
||||||
int index = ParseInt(ele.Attribute("index").Value);
|
|
||||||
array[index] = ReadObject<T>(ele);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Read/Write Helper
|
|
||||||
|
|
||||||
protected static XElement Write(XElement parent, string name, string val) => WriteString(parent, name, val);
|
|
||||||
protected static XElement Write(XElement parent, string name, DateTime date) => WriteString(parent, name, ToString(date.Ticks));
|
|
||||||
protected static XElement Write<T>(XElement parent, string name, T value) where T : IFormattable => WriteString(parent, name, ToString(value));
|
|
||||||
protected static XElement WriteObject<T>(XElement parent, string name, T value) where T : IModel => WriteString(parent, name, null,
|
|
||||||
action: ele =>
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
{
|
||||||
ele.Add(new XAttribute("null", 1));
|
ele = new XElement(name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value.OnXmlSerialize(ele);
|
ele = new XElement(name, val);
|
||||||
}
|
}
|
||||||
});
|
action?.Invoke(ele);
|
||||||
protected static XElement WriteArray<T>(XElement parent, string name, T[] value) where T : IModel => WriteString(parent, name, null,
|
parent.Add(ele);
|
||||||
action: ele =>
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T ReadSubnode<T>(XElement node, string subname, Func<XElement, T> func)
|
||||||
{
|
{
|
||||||
if (value == null)
|
var ele = node.Elements().FirstOrDefault(e => string.Equals(e.Name.ToString(), subname, StringComparison.OrdinalIgnoreCase));
|
||||||
|
return func(ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T ReadObject<T>(XElement node) where T : IModel, new()
|
||||||
|
{
|
||||||
|
if (IsTrue(node.Attribute("null")?.Value))
|
||||||
{
|
{
|
||||||
ele.Add(new XAttribute("null", 1));
|
return default;
|
||||||
}
|
}
|
||||||
else
|
T value = new();
|
||||||
|
value.OnXmlDeserialize(node);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T[] ReadArray<T>(XElement node) where T : IModel, new()
|
||||||
|
{
|
||||||
|
if (IsTrue(node.Attribute("null")?.Value))
|
||||||
{
|
{
|
||||||
ele.Add(new XAttribute("count", value.Length));
|
return default;
|
||||||
for (var i = 0; i < value.Length; i++)
|
}
|
||||||
|
int count = ParseInt(node.Attribute("count").Value);
|
||||||
|
T[] array = new T[count];
|
||||||
|
foreach (XElement ele in node.Elements("item"))
|
||||||
|
{
|
||||||
|
int index = ParseInt(ele.Attribute("index").Value);
|
||||||
|
array[index] = ReadObject<T>(ele);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read/Write Helper
|
||||||
|
|
||||||
|
protected static XElement Write(XElement parent, string name, string val) => WriteString(parent, name, val);
|
||||||
|
protected static XElement Write(XElement parent, string name, DateTime date) => WriteString(parent, name, ToString(date.Ticks));
|
||||||
|
protected static XElement Write<T>(XElement parent, string name, T value) where T : IFormattable => WriteString(parent, name, ToString(value));
|
||||||
|
protected static XElement WriteObject<T>(XElement parent, string name, T value) where T : IModel => WriteString(parent, name, null,
|
||||||
|
action: ele =>
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
{
|
{
|
||||||
XElement item = WriteObject(ele, "item", value[i]);
|
ele.Add(new XAttribute("null", 1));
|
||||||
item.Add(new XAttribute("index", i));
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
});
|
{
|
||||||
|
value.OnXmlSerialize(ele);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
protected static XElement WriteArray<T>(XElement parent, string name, T[] value) where T : IModel => WriteString(parent, name, null,
|
||||||
|
action: ele =>
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
ele.Add(new XAttribute("null", 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ele.Add(new XAttribute("count", value.Length));
|
||||||
|
for (var i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
XElement item = WriteObject(ele, "item", value[i]);
|
||||||
|
item.Add(new XAttribute("index", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
protected static string Read(XElement node, string subname, string def) => ReadSubnode(node, subname, e => e?.Value ?? def);
|
protected static string Read(XElement node, string subname, string def) => ReadSubnode(node, subname, e => e?.Value ?? def);
|
||||||
protected static int Read(XElement node, string subname, int def) => ReadSubnode(node, subname, e => e == null ? def : ParseInt(e.Value));
|
protected static int Read(XElement node, string subname, int def) => ReadSubnode(node, subname, e => e == null ? def : ParseInt(e.Value));
|
||||||
protected static long Read(XElement node, string subname, long def) => ReadSubnode(node, subname, e => e == null ? def : ParseLong(e.Value));
|
protected static long Read(XElement node, string subname, long def) => ReadSubnode(node, subname, e => e == null ? def : ParseLong(e.Value));
|
||||||
protected static double Read(XElement node, string subname, double def) => ReadSubnode(node, subname, e => e == null ? def : ParseDouble(e.Value));
|
protected static double Read(XElement node, string subname, double def) => ReadSubnode(node, subname, e => e == null ? def : ParseDouble(e.Value));
|
||||||
protected static decimal Read(XElement node, string subname, decimal def) => ReadSubnode(node, subname, e => e == null ? def : ParseDecimal(e.Value));
|
protected static decimal Read(XElement node, string subname, decimal def) => ReadSubnode(node, subname, e => e == null ? def : ParseDecimal(e.Value));
|
||||||
protected static DateTime Read(XElement node, string subname, DateTime def) => ReadSubnode(node, subname, e => e == null ? def : new DateTime(ParseLong(e.Value)));
|
protected static DateTime Read(XElement node, string subname, DateTime def) => ReadSubnode(node, subname, e => e == null ? def : new DateTime(ParseLong(e.Value)));
|
||||||
protected static T ReadObject<T>(XElement node, string subname, T def = default) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadObject<T>(e));
|
protected static T ReadObject<T>(XElement node, string subname, T def = default) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadObject<T>(e));
|
||||||
protected static T[] ReadArray<T>(XElement node, string subname, T[] def = null) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadArray<T>(e));
|
protected static T[] ReadArray<T>(XElement node, string subname, T[] def = null) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadArray<T>(e));
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public XDocument ToXml()
|
public XDocument ToXml()
|
||||||
{
|
|
||||||
XDocument xdoc = new(new XDeclaration("1.0", "utf-8", "yes"), new XElement("root"));
|
|
||||||
ToXml(xdoc.Root);
|
|
||||||
return xdoc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToXml(XElement node)
|
|
||||||
{
|
|
||||||
OnXmlSerialize(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveToStream(Stream stream)
|
|
||||||
{
|
|
||||||
ToXml().Save(stream, SaveOptions.DisableFormatting);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void OnXmlSerialize(XElement node);
|
|
||||||
public abstract void OnXmlDeserialize(XElement node);
|
|
||||||
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
XDocument xdoc = ToXml();
|
|
||||||
using MemoryStream ms = new();
|
|
||||||
using StreamWriter writer = new(ms, Encoding.UTF8);
|
|
||||||
xdoc.Save(writer, SaveOptions.DisableFormatting);
|
|
||||||
writer.Flush();
|
|
||||||
ms.Seek(0, SeekOrigin.Begin);
|
|
||||||
using StreamReader reader = new(ms, Encoding.UTF8);
|
|
||||||
return reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool dispose) { }
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!disposed)
|
|
||||||
{
|
{
|
||||||
Dispose(true);
|
XDocument xdoc = new(new XDeclaration("1.0", "utf-8", "yes"), new XElement("root"));
|
||||||
disposed = true;
|
ToXml(xdoc.Root);
|
||||||
|
return xdoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToXml(XElement node)
|
||||||
|
{
|
||||||
|
OnXmlSerialize(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveToStream(Stream stream)
|
||||||
|
{
|
||||||
|
ToXml().Save(stream, SaveOptions.DisableFormatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void OnXmlSerialize(XElement node);
|
||||||
|
public abstract void OnXmlDeserialize(XElement node);
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
XDocument xdoc = ToXml();
|
||||||
|
using MemoryStream ms = new();
|
||||||
|
using StreamWriter writer = new(ms, Encoding.UTF8);
|
||||||
|
xdoc.Save(writer, SaveOptions.DisableFormatting);
|
||||||
|
writer.Flush();
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
using StreamReader reader = new(ms, Encoding.UTF8);
|
||||||
|
return reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool dispose) { }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,34 +1,35 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Billing.Models;
|
namespace Billing.Models
|
||||||
|
|
||||||
public class Billing : BaseModel
|
|
||||||
{
|
{
|
||||||
public decimal Amount { get; set; }
|
public class Billing : BaseModel
|
||||||
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 override void OnXmlDeserialize(XElement node)
|
|
||||||
{
|
{
|
||||||
Amount = Read(node, nameof(Amount), 0m);
|
public decimal Amount { get; set; }
|
||||||
Name = Read(node, nameof(Name), string.Empty);
|
public string Name { get; set; }
|
||||||
CategoryId = Read(node, nameof(CategoryId), -1);
|
public int CategoryId { get; set; }
|
||||||
WalletId = Read(node, nameof(WalletId), -1);
|
public int WalletId { get; set; }
|
||||||
Store = Read(node, nameof(Store), string.Empty);
|
public string Store { get; set; }
|
||||||
CreateTime = Read(node, nameof(CreateTime), default(DateTime));
|
public DateTime CreateTime { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnXmlSerialize(XElement node)
|
public override void OnXmlDeserialize(XElement node)
|
||||||
{
|
{
|
||||||
Write(node, nameof(Amount), Amount);
|
Amount = Read(node, nameof(Amount), 0m);
|
||||||
Write(node, nameof(Name), Name);
|
Name = Read(node, nameof(Name), string.Empty);
|
||||||
Write(node, nameof(CategoryId), CategoryId);
|
CategoryId = Read(node, nameof(CategoryId), -1);
|
||||||
Write(node, nameof(WalletId), WalletId);
|
WalletId = Read(node, nameof(WalletId), -1);
|
||||||
Write(node, nameof(Store), Store);
|
Store = Read(node, nameof(Store), string.Empty);
|
||||||
Write(node, nameof(CreateTime), CreateTime);
|
CreateTime = Read(node, nameof(CreateTime), default(DateTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnXmlSerialize(XElement node)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,34 +1,35 @@
|
|||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Billing.Models;
|
namespace Billing.Models
|
||||||
|
|
||||||
public class Category : BaseModel
|
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public class Category : BaseModel
|
||||||
public string Icon { get; set; } = ICON_DEFAULT;
|
|
||||||
public string Name { get; set; }
|
|
||||||
public int? ParentId { get; set; }
|
|
||||||
|
|
||||||
public override void OnXmlDeserialize(XElement node)
|
|
||||||
{
|
{
|
||||||
Id = Read(node, nameof(Id), 0);
|
public int Id { get; set; }
|
||||||
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
public string Icon { get; set; } = ICON_DEFAULT;
|
||||||
Name = Read(node, nameof(Name), string.Empty);
|
public string Name { get; set; }
|
||||||
var parentId = Read(node, nameof(ParentId), -1);
|
public int? ParentId { get; set; }
|
||||||
if (parentId >= 0)
|
|
||||||
|
public override void OnXmlDeserialize(XElement node)
|
||||||
{
|
{
|
||||||
ParentId = parentId;
|
Id = Read(node, nameof(Id), 0);
|
||||||
|
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
||||||
|
Name = Read(node, nameof(Name), string.Empty);
|
||||||
|
var parentId = Read(node, nameof(ParentId), -1);
|
||||||
|
if (parentId >= 0)
|
||||||
|
{
|
||||||
|
ParentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnXmlSerialize(XElement node)
|
||||||
|
{
|
||||||
|
Write(node, nameof(Id), Id);
|
||||||
|
Write(node, nameof(Icon), Icon);
|
||||||
|
Write(node, nameof(Name), Name);
|
||||||
|
if (ParentId != null)
|
||||||
|
{
|
||||||
|
Write(node, nameof(ParentId), ParentId.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public override void OnXmlSerialize(XElement node)
|
|
||||||
{
|
|
||||||
Write(node, nameof(Id), Id);
|
|
||||||
Write(node, nameof(Icon), Icon);
|
|
||||||
Write(node, nameof(Name), Name);
|
|
||||||
if (ParentId != null)
|
|
||||||
{
|
|
||||||
Write(node, nameof(ParentId), ParentId.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +1,77 @@
|
|||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Themes;
|
namespace Billing.Themes
|
||||||
|
|
||||||
public abstract class BaseTheme : ResourceDictionary
|
|
||||||
{
|
{
|
||||||
public const string CascadiaFontRegular = nameof(CascadiaFontRegular);
|
public abstract class BaseTheme : ResourceDictionary
|
||||||
public const string CascadiaFontBold = nameof(CascadiaFontBold);
|
|
||||||
public const string RobotoCondensedFontRegular = nameof(RobotoCondensedFontRegular);
|
|
||||||
public const string RobotoCondensedFontBold = nameof(RobotoCondensedFontBold);
|
|
||||||
|
|
||||||
public const string WindowBackgroundColor = nameof(WindowBackgroundColor);
|
|
||||||
public const string OptionTintColor = nameof(OptionTintColor);
|
|
||||||
public const string PromptBackgroundColor = nameof(PromptBackgroundColor);
|
|
||||||
public const string PrimaryColor = nameof(PrimaryColor);
|
|
||||||
public const string SecondaryColor = nameof(SecondaryColor);
|
|
||||||
public const string TabBarBackgroundColor = nameof(TabBarBackgroundColor);
|
|
||||||
public const string TabBarTitleColor = nameof(TabBarTitleColor);
|
|
||||||
public const string TabBarUnselectedColor = nameof(TabBarUnselectedColor);
|
|
||||||
public const string OutRangeDayColor = nameof(OutRangeDayColor);
|
|
||||||
public const string TextColor = nameof(TextColor);
|
|
||||||
public const string SecondaryTextColor = nameof(SecondaryTextColor);
|
|
||||||
public const string RedColor = nameof(RedColor);
|
|
||||||
|
|
||||||
protected abstract Color PrimaryMauiColor { get; }
|
|
||||||
protected abstract Color SecondaryMauiColor { get; }
|
|
||||||
|
|
||||||
protected void InitResources()
|
|
||||||
{
|
{
|
||||||
var robotoRegularFontFamily = Definition.GetRobotoCondensedRegularFontFamily();
|
public const string CascadiaFontRegular = nameof(CascadiaFontRegular);
|
||||||
Add(CascadiaFontRegular, Definition.GetCascadiaRegularFontFamily());
|
public const string CascadiaFontBold = nameof(CascadiaFontBold);
|
||||||
Add(CascadiaFontBold, Definition.GetCascadiaBoldFontFamily());
|
public const string RobotoCondensedFontRegular = nameof(RobotoCondensedFontRegular);
|
||||||
Add(RobotoCondensedFontRegular, Definition.GetRobotoCondensedRegularFontFamily());
|
public const string RobotoCondensedFontBold = nameof(RobotoCondensedFontBold);
|
||||||
Add(RobotoCondensedFontBold, Definition.GetRobotoCondensedBoldFontFamily());
|
|
||||||
|
|
||||||
Add(PrimaryColor, PrimaryMauiColor);
|
public const string WindowBackgroundColor = nameof(WindowBackgroundColor);
|
||||||
Add(SecondaryColor, SecondaryMauiColor);
|
public const string OptionTintColor = nameof(OptionTintColor);
|
||||||
Add(TabBarTitleColor, PrimaryMauiColor);
|
public const string PromptBackgroundColor = nameof(PromptBackgroundColor);
|
||||||
|
public const string PrimaryColor = nameof(PrimaryColor);
|
||||||
|
public const string SecondaryColor = nameof(SecondaryColor);
|
||||||
|
public const string TabBarBackgroundColor = nameof(TabBarBackgroundColor);
|
||||||
|
public const string TabBarTitleColor = nameof(TabBarTitleColor);
|
||||||
|
public const string TabBarUnselectedColor = nameof(TabBarUnselectedColor);
|
||||||
|
public const string OutRangeDayColor = nameof(OutRangeDayColor);
|
||||||
|
public const string TextColor = nameof(TextColor);
|
||||||
|
public const string SecondaryTextColor = nameof(SecondaryTextColor);
|
||||||
|
public const string RedColor = nameof(RedColor);
|
||||||
|
|
||||||
Add(new Style(typeof(Label))
|
protected abstract Color PrimaryMauiColor { get; }
|
||||||
|
protected abstract Color SecondaryMauiColor { get; }
|
||||||
|
|
||||||
|
protected void InitResources()
|
||||||
{
|
{
|
||||||
Setters =
|
var robotoRegularFontFamily = Definition.GetRobotoCondensedRegularFontFamily();
|
||||||
|
Add(CascadiaFontRegular, Definition.GetCascadiaRegularFontFamily());
|
||||||
|
Add(CascadiaFontBold, Definition.GetCascadiaBoldFontFamily());
|
||||||
|
Add(RobotoCondensedFontRegular, Definition.GetRobotoCondensedRegularFontFamily());
|
||||||
|
Add(RobotoCondensedFontBold, Definition.GetRobotoCondensedBoldFontFamily());
|
||||||
|
|
||||||
|
Add(PrimaryColor, PrimaryMauiColor);
|
||||||
|
Add(SecondaryColor, SecondaryMauiColor);
|
||||||
|
Add(TabBarTitleColor, PrimaryMauiColor);
|
||||||
|
|
||||||
|
Add(new Style(typeof(Label))
|
||||||
{
|
{
|
||||||
new Setter { Property = Label.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Label)) },
|
Setters =
|
||||||
new Setter { Property = Label.TextColorProperty, Value = PrimaryMauiColor },
|
{
|
||||||
new Setter { Property = Label.FontFamilyProperty, Value = robotoRegularFontFamily }
|
new Setter { Property = Label.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Label)) },
|
||||||
}
|
new Setter { Property = Label.TextColorProperty, Value = PrimaryMauiColor },
|
||||||
});
|
new Setter { Property = Label.FontFamilyProperty, Value = robotoRegularFontFamily }
|
||||||
Add(new Style(typeof(OptionEntry))
|
}
|
||||||
{
|
});
|
||||||
Setters =
|
Add(new Style(typeof(OptionEntry))
|
||||||
{
|
{
|
||||||
new Setter { Property = Entry.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Entry)) },
|
Setters =
|
||||||
new Setter { Property = Entry.FontFamilyProperty, Value = robotoRegularFontFamily }
|
{
|
||||||
}
|
new Setter { Property = Entry.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Entry)) },
|
||||||
});
|
new Setter { Property = Entry.FontFamilyProperty, Value = robotoRegularFontFamily }
|
||||||
Add(new Style(typeof(Button))
|
}
|
||||||
{
|
});
|
||||||
Setters =
|
Add(new Style(typeof(Button))
|
||||||
{
|
{
|
||||||
new Setter { Property = Button.TextColorProperty, Value = SecondaryMauiColor },
|
Setters =
|
||||||
new Setter { Property = Button.FontFamilyProperty, Value = robotoRegularFontFamily },
|
{
|
||||||
new Setter { Property = VisualElement.BackgroundColorProperty, Value = PrimaryMauiColor },
|
new Setter { Property = Button.TextColorProperty, Value = SecondaryMauiColor },
|
||||||
new Setter { Property = Button.PaddingProperty, Value = new Thickness(14, 10) }
|
new Setter { Property = Button.FontFamilyProperty, Value = robotoRegularFontFamily },
|
||||||
}
|
new Setter { Property = VisualElement.BackgroundColorProperty, Value = PrimaryMauiColor },
|
||||||
});
|
new Setter { Property = Button.PaddingProperty, Value = new Thickness(14, 10) }
|
||||||
Add(new Style(typeof(TintImage))
|
}
|
||||||
{
|
});
|
||||||
Setters =
|
Add(new Style(typeof(TintImage))
|
||||||
{
|
{
|
||||||
new Setter { Property = TintImage.PrimaryColorProperty, Value = PrimaryMauiColor }
|
Setters =
|
||||||
}
|
{
|
||||||
});
|
new Setter { Property = TintImage.PrimaryColorProperty, Value = PrimaryMauiColor }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,50 +1,50 @@
|
|||||||
using Billing.UI;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Billing.Themes;
|
namespace Billing.Themes
|
||||||
|
|
||||||
public class Dark : BaseTheme
|
|
||||||
{
|
{
|
||||||
private static Dark _instance;
|
public class Dark : BaseTheme
|
||||||
|
|
||||||
public static Dark Instance => _instance ??= new Dark();
|
|
||||||
|
|
||||||
protected override Color PrimaryMauiColor => Color.White;
|
|
||||||
protected override Color SecondaryMauiColor => Color.LightGray;
|
|
||||||
|
|
||||||
public Dark()
|
|
||||||
{
|
{
|
||||||
InitColors();
|
private static Dark _instance;
|
||||||
InitResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitColors()
|
public static Dark Instance => _instance ??= new Dark();
|
||||||
{
|
|
||||||
Add(WindowBackgroundColor, Color.Black);
|
|
||||||
Add(OptionTintColor, Color.FromRgb(28, 28, 28));
|
|
||||||
Add(PromptBackgroundColor, Color.FromRgb(0x1f, 0x1f, 0x1f));
|
|
||||||
Add(TabBarBackgroundColor, Color.Black);
|
|
||||||
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
|
|
||||||
Add(OutRangeDayColor, Color.DarkGray);
|
|
||||||
Add(TextColor, Color.FromRgb(0xcc, 0xcc, 0xcc));
|
|
||||||
Add(SecondaryTextColor, Color.LightGray);
|
|
||||||
Add(RedColor, Color.FromRgb(211, 5, 5));
|
|
||||||
|
|
||||||
Add(new Style(typeof(TabBar))
|
protected override Color PrimaryMauiColor => Color.White;
|
||||||
|
protected override Color SecondaryMauiColor => Color.LightGray;
|
||||||
|
|
||||||
|
public Dark()
|
||||||
{
|
{
|
||||||
Setters =
|
InitColors();
|
||||||
{
|
InitResources();
|
||||||
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.Black },
|
}
|
||||||
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
|
|
||||||
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
|
private void InitColors()
|
||||||
}
|
|
||||||
});
|
|
||||||
Add(new Style(typeof(TableView))
|
|
||||||
{
|
{
|
||||||
Setters =
|
Add(WindowBackgroundColor, Color.Black);
|
||||||
|
Add(OptionTintColor, Color.FromRgb(28, 28, 28));
|
||||||
|
Add(PromptBackgroundColor, Color.FromRgb(0x1f, 0x1f, 0x1f));
|
||||||
|
Add(TabBarBackgroundColor, Color.Black);
|
||||||
|
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
|
||||||
|
Add(OutRangeDayColor, Color.DarkGray);
|
||||||
|
Add(TextColor, Color.FromRgb(0xcc, 0xcc, 0xcc));
|
||||||
|
Add(SecondaryTextColor, Color.LightGray);
|
||||||
|
Add(RedColor, Color.FromRgb(211, 5, 5));
|
||||||
|
|
||||||
|
Add(new Style(typeof(TabBar))
|
||||||
{
|
{
|
||||||
new Setter { Property = VisualElement.BackgroundColorProperty, Value = Color.Black }
|
Setters =
|
||||||
}
|
{
|
||||||
});
|
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.Black },
|
||||||
|
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
|
||||||
|
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Add(new Style(typeof(TableView))
|
||||||
|
{
|
||||||
|
Setters =
|
||||||
|
{
|
||||||
|
new Setter { Property = VisualElement.BackgroundColorProperty, Value = Color.Black }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,50 +1,50 @@
|
|||||||
using Billing.UI;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Billing.Themes;
|
namespace Billing.Themes
|
||||||
|
|
||||||
public class Light : BaseTheme
|
|
||||||
{
|
{
|
||||||
private static Light _instance;
|
public class Light : BaseTheme
|
||||||
|
|
||||||
public static Light Instance => _instance ??= new Light();
|
|
||||||
|
|
||||||
protected override Color PrimaryMauiColor => Color.FromRgb(0x18, 0x31, 0x53);
|
|
||||||
protected override Color SecondaryMauiColor => Color.White;
|
|
||||||
|
|
||||||
public Light()
|
|
||||||
{
|
{
|
||||||
InitColors();
|
private static Light _instance;
|
||||||
InitResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitColors()
|
public static Light Instance => _instance ??= new Light();
|
||||||
{
|
|
||||||
Add(WindowBackgroundColor, Color.White);
|
|
||||||
Add(OptionTintColor, Color.White);
|
|
||||||
Add(PromptBackgroundColor, Color.FromRgb(0xe0, 0xe0, 0xe0));
|
|
||||||
Add(TabBarBackgroundColor, Color.White);
|
|
||||||
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
|
|
||||||
Add(OutRangeDayColor, Color.LightGray);
|
|
||||||
Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33));
|
|
||||||
Add(SecondaryTextColor, Color.DimGray);
|
|
||||||
Add(RedColor, Color.FromRgb(211, 64, 85));
|
|
||||||
|
|
||||||
Add(new Style(typeof(TabBar))
|
protected override Color PrimaryMauiColor => Color.FromRgb(0x18, 0x31, 0x53);
|
||||||
|
protected override Color SecondaryMauiColor => Color.White;
|
||||||
|
|
||||||
|
public Light()
|
||||||
{
|
{
|
||||||
Setters =
|
InitColors();
|
||||||
{
|
InitResources();
|
||||||
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.White },
|
}
|
||||||
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
|
|
||||||
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
|
private void InitColors()
|
||||||
}
|
|
||||||
});
|
|
||||||
Add(new Style(typeof(TableView))
|
|
||||||
{
|
{
|
||||||
Setters =
|
Add(WindowBackgroundColor, Color.White);
|
||||||
|
Add(OptionTintColor, Color.White);
|
||||||
|
Add(PromptBackgroundColor, Color.FromRgb(0xe0, 0xe0, 0xe0));
|
||||||
|
Add(TabBarBackgroundColor, Color.White);
|
||||||
|
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
|
||||||
|
Add(OutRangeDayColor, Color.LightGray);
|
||||||
|
Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33));
|
||||||
|
Add(SecondaryTextColor, Color.DimGray);
|
||||||
|
Add(RedColor, Color.FromRgb(211, 64, 85));
|
||||||
|
|
||||||
|
Add(new Style(typeof(TabBar))
|
||||||
{
|
{
|
||||||
new Setter { Property = VisualElement.BackgroundColorProperty, Value = Color.FromRgb(242, 241, 245) }
|
Setters =
|
||||||
}
|
{
|
||||||
});
|
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.White },
|
||||||
|
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
|
||||||
|
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Add(new Style(typeof(TableView))
|
||||||
|
{
|
||||||
|
Setters =
|
||||||
|
{
|
||||||
|
new Setter { Property = VisualElement.BackgroundColorProperty, Value = Color.FromRgb(242, 241, 245) }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,289 +1,290 @@
|
|||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public partial class BillingDate : ContentView
|
|
||||||
{
|
{
|
||||||
#region UI Properties
|
public partial class BillingDate : ContentView
|
||||||
|
|
||||||
private static readonly BindableProperty SundayProperty = BindableProperty.Create(nameof(Sunday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty MondayProperty = BindableProperty.Create(nameof(Monday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty TuesdayProperty = BindableProperty.Create(nameof(Tuesday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty WednesdayProperty = BindableProperty.Create(nameof(Wednesday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty ThursdayProperty = BindableProperty.Create(nameof(Thursday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty FridayProperty = BindableProperty.Create(nameof(Friday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
private static readonly BindableProperty SaturdayProperty = BindableProperty.Create(nameof(Saturday), typeof(BillingDay), typeof(BillingDate));
|
|
||||||
|
|
||||||
public BillingDay Sunday => (BillingDay)GetValue(SundayProperty);
|
|
||||||
public BillingDay Monday => (BillingDay)GetValue(MondayProperty);
|
|
||||||
public BillingDay Tuesday => (BillingDay)GetValue(TuesdayProperty);
|
|
||||||
public BillingDay Wednesday => (BillingDay)GetValue(WednesdayProperty);
|
|
||||||
public BillingDay Thursday => (BillingDay)GetValue(ThursdayProperty);
|
|
||||||
public BillingDay Friday => (BillingDay)GetValue(FridayProperty);
|
|
||||||
public BillingDay Saturday => (BillingDay)GetValue(SaturdayProperty);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private static BindableProperty GetWeekProperty(int week)
|
|
||||||
{
|
{
|
||||||
return (DayOfWeek)week switch
|
#region UI Properties
|
||||||
{
|
|
||||||
DayOfWeek.Monday => MondayProperty,
|
|
||||||
DayOfWeek.Tuesday => TuesdayProperty,
|
|
||||||
DayOfWeek.Wednesday => WednesdayProperty,
|
|
||||||
DayOfWeek.Thursday => ThursdayProperty,
|
|
||||||
DayOfWeek.Friday => FridayProperty,
|
|
||||||
DayOfWeek.Saturday => SaturdayProperty,
|
|
||||||
_ => SundayProperty
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly BindableProperty LocatedDateProperty = BindableProperty.Create(nameof(LocatedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnLocatedDatePropertyChanged);
|
private static readonly BindableProperty SundayProperty = BindableProperty.Create(nameof(Sunday), typeof(BillingDay), typeof(BillingDate));
|
||||||
public static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnSelectedDatePropertyChanged);
|
private static readonly BindableProperty MondayProperty = BindableProperty.Create(nameof(Monday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
private static readonly BindableProperty TuesdayProperty = BindableProperty.Create(nameof(Tuesday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
private static readonly BindableProperty WednesdayProperty = BindableProperty.Create(nameof(Wednesday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
private static readonly BindableProperty ThursdayProperty = BindableProperty.Create(nameof(Thursday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
private static readonly BindableProperty FridayProperty = BindableProperty.Create(nameof(Friday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
private static readonly BindableProperty SaturdayProperty = BindableProperty.Create(nameof(Saturday), typeof(BillingDay), typeof(BillingDate));
|
||||||
|
|
||||||
private static void OnLocatedDatePropertyChanged(BindableObject obj, object old, object @new)
|
public BillingDay Sunday => (BillingDay)GetValue(SundayProperty);
|
||||||
{
|
public BillingDay Monday => (BillingDay)GetValue(MondayProperty);
|
||||||
if (obj is BillingDate billingDate && @new is DateTime date)
|
public BillingDay Tuesday => (BillingDay)GetValue(TuesdayProperty);
|
||||||
|
public BillingDay Wednesday => (BillingDay)GetValue(WednesdayProperty);
|
||||||
|
public BillingDay Thursday => (BillingDay)GetValue(ThursdayProperty);
|
||||||
|
public BillingDay Friday => (BillingDay)GetValue(FridayProperty);
|
||||||
|
public BillingDay Saturday => (BillingDay)GetValue(SaturdayProperty);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static BindableProperty GetWeekProperty(int week)
|
||||||
{
|
{
|
||||||
var week = (int)date.DayOfWeek;
|
return (DayOfWeek)week switch
|
||||||
var tmpDate = date.AddDays(-week);
|
|
||||||
for (var i = 0; i < 7; i++)
|
|
||||||
{
|
{
|
||||||
var prop = GetWeekProperty(i);
|
DayOfWeek.Monday => MondayProperty,
|
||||||
var day = new BillingDay(tmpDate);
|
DayOfWeek.Tuesday => TuesdayProperty,
|
||||||
billingDate.SetValue(prop, day);
|
DayOfWeek.Wednesday => WednesdayProperty,
|
||||||
tmpDate = tmpDate.AddDays(1);
|
DayOfWeek.Thursday => ThursdayProperty,
|
||||||
}
|
DayOfWeek.Friday => FridayProperty,
|
||||||
|
DayOfWeek.Saturday => SaturdayProperty,
|
||||||
|
_ => SundayProperty
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnSelectedDatePropertyChanged(BindableObject obj, object old, object @new)
|
public static readonly BindableProperty LocatedDateProperty = BindableProperty.Create(nameof(LocatedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnLocatedDatePropertyChanged);
|
||||||
{
|
public static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnSelectedDatePropertyChanged);
|
||||||
if (obj is BillingDate billingDate && @new is DateTime selected)
|
|
||||||
|
private static void OnLocatedDatePropertyChanged(BindableObject obj, object old, object @new)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < 7; i++)
|
if (obj is BillingDate billingDate && @new is DateTime date)
|
||||||
{
|
{
|
||||||
var prop = GetWeekProperty(i);
|
var week = (int)date.DayOfWeek;
|
||||||
var day = (BillingDay)billingDate.GetValue(prop);
|
var tmpDate = date.AddDays(-week);
|
||||||
day.Refresh(selected);
|
for (var i = 0; i < 7; i++)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime LocatedDate
|
|
||||||
{
|
|
||||||
get => (DateTime)GetValue(LocatedDateProperty);
|
|
||||||
set => SetValue(LocatedDateProperty, value);
|
|
||||||
}
|
|
||||||
public DateTime SelectedDate
|
|
||||||
{
|
|
||||||
get => (DateTime)GetValue(SelectedDateProperty);
|
|
||||||
set => SetValue(SelectedDateProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Command OnDayTapped { get; }
|
|
||||||
|
|
||||||
public event EventHandler<DateEventArgs> DateSelected;
|
|
||||||
|
|
||||||
private DateTime lastDate;
|
|
||||||
private double? x1;
|
|
||||||
private double x2;
|
|
||||||
|
|
||||||
public BillingDate()
|
|
||||||
{
|
|
||||||
OnDayTapped = new Command(OnDayChanged);
|
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDateTime(DateTime selectedDate, DateTime? locatedDate = null)
|
|
||||||
{
|
|
||||||
if (locatedDate != null)
|
|
||||||
{
|
|
||||||
LocatedDate = locatedDate.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LocatedDate = selectedDate;
|
|
||||||
}
|
|
||||||
SelectedDate = selectedDate;
|
|
||||||
DateSelected?.Invoke(this, new DateEventArgs(selectedDate));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDayChanged(object o)
|
|
||||||
{
|
|
||||||
var selected = SelectedDate;
|
|
||||||
if (o is BillingDay day && (selected.Year != day.Date.Year || selected.DayOfYear != day.Date.DayOfYear))
|
|
||||||
{
|
|
||||||
for (var i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
var d = (BillingDay)GetValue(GetWeekProperty(i));
|
|
||||||
if (d.IsSelected)
|
|
||||||
{
|
{
|
||||||
this.AbortAnimation("unselected");
|
var prop = GetWeekProperty(i);
|
||||||
this.Animate("unselected", v =>
|
var day = new BillingDay(tmpDate);
|
||||||
{
|
billingDate.SetValue(prop, day);
|
||||||
d.Opacity = v;
|
tmpDate = tmpDate.AddDays(1);
|
||||||
},
|
|
||||||
start: 1, end: 0,
|
|
||||||
easing: Easing.CubicOut,
|
|
||||||
finished: (v, b) =>
|
|
||||||
{
|
|
||||||
d.Opacity = 0;
|
|
||||||
d.IsSelected = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelectedDate = day.Date;
|
|
||||||
this.AbortAnimation("selected");
|
|
||||||
this.Animate("selected", v =>
|
|
||||||
{
|
|
||||||
day.Opacity = v;
|
|
||||||
},
|
|
||||||
start: 0, end: 1,
|
|
||||||
easing: Easing.CubicOut,
|
|
||||||
finished: (v, b) =>
|
|
||||||
{
|
|
||||||
day.Opacity = 1;
|
|
||||||
});
|
|
||||||
DateSelected?.Invoke(this, new DateEventArgs(day.Date));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
|
private static void OnSelectedDatePropertyChanged(BindableObject obj, object old, object @new)
|
||||||
{
|
|
||||||
if (e.StatusType == GestureStatus.Started)
|
|
||||||
{
|
{
|
||||||
lastDate = DateTime.Now;
|
if (obj is BillingDate billingDate && @new is DateTime selected)
|
||||||
x1 = null;
|
{
|
||||||
|
for (var i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
var prop = GetWeekProperty(i);
|
||||||
|
var day = (BillingDay)billingDate.GetValue(prop);
|
||||||
|
day.Refresh(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (e.StatusType == GestureStatus.Running)
|
|
||||||
|
public DateTime LocatedDate
|
||||||
{
|
{
|
||||||
if (x1 == null)
|
get => (DateTime)GetValue(LocatedDateProperty);
|
||||||
{
|
set => SetValue(LocatedDateProperty, value);
|
||||||
x1 = e.TotalX;
|
|
||||||
}
|
|
||||||
x2 = e.TotalX;
|
|
||||||
}
|
}
|
||||||
else if (e.StatusType == GestureStatus.Completed)
|
public DateTime SelectedDate
|
||||||
{
|
{
|
||||||
if (x1 == null)
|
get => (DateTime)GetValue(SelectedDateProperty);
|
||||||
{
|
set => SetValue(SelectedDateProperty, value);
|
||||||
return;
|
|
||||||
}
|
|
||||||
var totalX = x2 - x1.Value;
|
|
||||||
x1 = null;
|
|
||||||
var ms = (DateTime.Now - lastDate).TotalMilliseconds;
|
|
||||||
var speed = totalX / ms;
|
|
||||||
Helper.Debug($"completed, speed: {speed}");
|
|
||||||
if (speed < -0.7)
|
|
||||||
{
|
|
||||||
LocatedDate = LocatedDate.AddDays(7);
|
|
||||||
}
|
|
||||||
else if (speed > 0.7)
|
|
||||||
{
|
|
||||||
LocatedDate = LocatedDate.AddDays(-7);
|
|
||||||
}
|
|
||||||
OnSelectedDatePropertyChanged(this, null, SelectedDate);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DateEventArgs : EventArgs
|
public Command OnDayTapped { get; }
|
||||||
{
|
|
||||||
public DateTime Date { get; }
|
|
||||||
|
|
||||||
public DateEventArgs(DateTime date)
|
public event EventHandler<DateEventArgs> DateSelected;
|
||||||
{
|
|
||||||
Date = date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BillingDayView : ContentView
|
private DateTime lastDate;
|
||||||
{
|
private double? x1;
|
||||||
public static readonly BindableProperty BillingDayProperty = BindableProperty.Create(nameof(BillingDay), typeof(BillingDay), typeof(BillingDayView));
|
private double x2;
|
||||||
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(Command), typeof(BillingDayView));
|
|
||||||
|
|
||||||
public BillingDay BillingDay
|
public BillingDate()
|
||||||
{
|
|
||||||
get => (BillingDay)GetValue(BillingDayProperty);
|
|
||||||
set => SetValue(BillingDayProperty, value);
|
|
||||||
}
|
|
||||||
public Command Command
|
|
||||||
{
|
|
||||||
get => (Command)GetValue(CommandProperty);
|
|
||||||
set => SetValue(CommandProperty, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BillingDay : BindableObject
|
|
||||||
{
|
|
||||||
private static readonly BindableProperty DateProperty = BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(BillingDay), propertyChanged: OnDatePropertyChanged);
|
|
||||||
private static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(BillingDay));
|
|
||||||
private static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(BillingDay), defaultValue: Definition.GetCascadiaRegularFontFamily());
|
|
||||||
private static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(BillingDay));
|
|
||||||
private static readonly BindableProperty OpacityProperty = BindableProperty.Create(nameof(Opacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
|
|
||||||
private static readonly BindableProperty TextOpacityProperty = BindableProperty.Create(nameof(TextOpacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
|
|
||||||
|
|
||||||
private static void OnDatePropertyChanged(BindableObject obj, object old, object @new)
|
|
||||||
{
|
|
||||||
if (obj is BillingDay day && @new is DateTime date)
|
|
||||||
{
|
{
|
||||||
if (date.Day == 1)
|
OnDayTapped = new Command(OnDayChanged);
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDateTime(DateTime selectedDate, DateTime? locatedDate = null)
|
||||||
|
{
|
||||||
|
if (locatedDate != null)
|
||||||
{
|
{
|
||||||
day.SetValue(TextProperty, date.ToString("MMM"));
|
LocatedDate = locatedDate.Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
day.SetValue(TextProperty, date.Day.ToString());
|
LocatedDate = selectedDate;
|
||||||
|
}
|
||||||
|
SelectedDate = selectedDate;
|
||||||
|
DateSelected?.Invoke(this, new DateEventArgs(selectedDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDayChanged(object o)
|
||||||
|
{
|
||||||
|
var selected = SelectedDate;
|
||||||
|
if (o is BillingDay day && (selected.Year != day.Date.Year || selected.DayOfYear != day.Date.DayOfYear))
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
var d = (BillingDay)GetValue(GetWeekProperty(i));
|
||||||
|
if (d.IsSelected)
|
||||||
|
{
|
||||||
|
this.AbortAnimation("unselected");
|
||||||
|
this.Animate("unselected", v =>
|
||||||
|
{
|
||||||
|
d.Opacity = v;
|
||||||
|
},
|
||||||
|
start: 1, end: 0,
|
||||||
|
easing: Easing.CubicOut,
|
||||||
|
finished: (v, b) =>
|
||||||
|
{
|
||||||
|
d.Opacity = 0;
|
||||||
|
d.IsSelected = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SelectedDate = day.Date;
|
||||||
|
this.AbortAnimation("selected");
|
||||||
|
this.Animate("selected", v =>
|
||||||
|
{
|
||||||
|
day.Opacity = v;
|
||||||
|
},
|
||||||
|
start: 0, end: 1,
|
||||||
|
easing: Easing.CubicOut,
|
||||||
|
finished: (v, b) =>
|
||||||
|
{
|
||||||
|
day.Opacity = 1;
|
||||||
|
});
|
||||||
|
DateSelected?.Invoke(this, new DateEventArgs(day.Date));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.StatusType == GestureStatus.Started)
|
||||||
|
{
|
||||||
|
lastDate = DateTime.Now;
|
||||||
|
x1 = null;
|
||||||
|
}
|
||||||
|
else if (e.StatusType == GestureStatus.Running)
|
||||||
|
{
|
||||||
|
if (x1 == null)
|
||||||
|
{
|
||||||
|
x1 = e.TotalX;
|
||||||
|
}
|
||||||
|
x2 = e.TotalX;
|
||||||
|
}
|
||||||
|
else if (e.StatusType == GestureStatus.Completed)
|
||||||
|
{
|
||||||
|
if (x1 == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var totalX = x2 - x1.Value;
|
||||||
|
x1 = null;
|
||||||
|
var ms = (DateTime.Now - lastDate).TotalMilliseconds;
|
||||||
|
var speed = totalX / ms;
|
||||||
|
Helper.Debug($"completed, speed: {speed}");
|
||||||
|
if (speed < -0.7)
|
||||||
|
{
|
||||||
|
LocatedDate = LocatedDate.AddDays(7);
|
||||||
|
}
|
||||||
|
else if (speed > 0.7)
|
||||||
|
{
|
||||||
|
LocatedDate = LocatedDate.AddDays(-7);
|
||||||
|
}
|
||||||
|
OnSelectedDatePropertyChanged(this, null, SelectedDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime Date
|
public class DateEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
get => (DateTime)GetValue(DateProperty);
|
public DateTime Date { get; }
|
||||||
set => SetValue(DateProperty, value);
|
|
||||||
}
|
|
||||||
public string Text => (string)GetValue(TextProperty);
|
|
||||||
public string FontFamily => (string)GetValue(FontFamilyProperty);
|
|
||||||
public bool IsSelected
|
|
||||||
{
|
|
||||||
get => (bool)GetValue(IsSelectedProperty);
|
|
||||||
set => SetValue(IsSelectedProperty, value);
|
|
||||||
}
|
|
||||||
public double Opacity
|
|
||||||
{
|
|
||||||
get => (double)GetValue(OpacityProperty);
|
|
||||||
set => SetValue(OpacityProperty, value);
|
|
||||||
}
|
|
||||||
public double TextOpacity => (double)GetValue(TextOpacityProperty);
|
|
||||||
|
|
||||||
public BillingDay(DateTime date)
|
public DateEventArgs(DateTime date)
|
||||||
{
|
{
|
||||||
Date = date;
|
Date = date;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh(DateTime selected)
|
public class BillingDayView : ContentView
|
||||||
{
|
{
|
||||||
var date = Date;
|
public static readonly BindableProperty BillingDayProperty = BindableProperty.Create(nameof(BillingDay), typeof(BillingDay), typeof(BillingDayView));
|
||||||
if (date.Year == selected.Year && date.DayOfYear == selected.DayOfYear)
|
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(Command), typeof(BillingDayView));
|
||||||
|
|
||||||
|
public BillingDay BillingDay
|
||||||
{
|
{
|
||||||
SetValue(IsSelectedProperty, true);
|
get => (BillingDay)GetValue(BillingDayProperty);
|
||||||
SetValue(FontFamilyProperty, Definition.GetCascadiaBoldFontFamily());
|
set => SetValue(BillingDayProperty, value);
|
||||||
}
|
}
|
||||||
else
|
public Command Command
|
||||||
{
|
{
|
||||||
SetValue(FontFamilyProperty, Definition.GetCascadiaRegularFontFamily());
|
get => (Command)GetValue(CommandProperty);
|
||||||
}
|
set => SetValue(CommandProperty, value);
|
||||||
if (date.Year == selected.Year && date.Month == selected.Month)
|
|
||||||
{
|
|
||||||
SetValue(TextOpacityProperty, 1.0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetValue(TextOpacityProperty, .4);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public class BillingDay : BindableObject
|
||||||
|
{
|
||||||
|
private static readonly BindableProperty DateProperty = BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(BillingDay), propertyChanged: OnDatePropertyChanged);
|
||||||
|
private static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(BillingDay));
|
||||||
|
private static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(BillingDay), defaultValue: Definition.GetCascadiaRegularFontFamily());
|
||||||
|
private static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(BillingDay));
|
||||||
|
private static readonly BindableProperty OpacityProperty = BindableProperty.Create(nameof(Opacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
|
||||||
|
private static readonly BindableProperty TextOpacityProperty = BindableProperty.Create(nameof(TextOpacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
|
||||||
|
|
||||||
|
private static void OnDatePropertyChanged(BindableObject obj, object old, object @new)
|
||||||
|
{
|
||||||
|
if (obj is BillingDay day && @new is DateTime date)
|
||||||
|
{
|
||||||
|
if (date.Day == 1)
|
||||||
|
{
|
||||||
|
day.SetValue(TextProperty, date.ToString("MMM"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
day.SetValue(TextProperty, date.Day.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime Date
|
||||||
|
{
|
||||||
|
get => (DateTime)GetValue(DateProperty);
|
||||||
|
set => SetValue(DateProperty, value);
|
||||||
|
}
|
||||||
|
public string Text => (string)GetValue(TextProperty);
|
||||||
|
public string FontFamily => (string)GetValue(FontFamilyProperty);
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => (bool)GetValue(IsSelectedProperty);
|
||||||
|
set => SetValue(IsSelectedProperty, value);
|
||||||
|
}
|
||||||
|
public double Opacity
|
||||||
|
{
|
||||||
|
get => (double)GetValue(OpacityProperty);
|
||||||
|
set => SetValue(OpacityProperty, value);
|
||||||
|
}
|
||||||
|
public double TextOpacity => (double)GetValue(TextOpacityProperty);
|
||||||
|
|
||||||
|
public BillingDay(DateTime date)
|
||||||
|
{
|
||||||
|
Date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh(DateTime selected)
|
||||||
|
{
|
||||||
|
var date = Date;
|
||||||
|
if (date.Year == selected.Year && date.DayOfYear == selected.DayOfYear)
|
||||||
|
{
|
||||||
|
SetValue(IsSelectedProperty, true);
|
||||||
|
SetValue(FontFamilyProperty, Definition.GetCascadiaBoldFontFamily());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetValue(FontFamilyProperty, Definition.GetCascadiaRegularFontFamily());
|
||||||
|
}
|
||||||
|
if (date.Year == selected.Year && date.Month == selected.Month)
|
||||||
|
{
|
||||||
|
SetValue(TextOpacityProperty, 1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetValue(TextOpacityProperty, .4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
using Billing.Themes;
|
using Billing.Themes;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public abstract class BillingPage : ContentPage
|
|
||||||
{
|
{
|
||||||
public BillingPage()
|
public abstract class BillingPage : ContentPage
|
||||||
{
|
{
|
||||||
SetDynamicResource(BackgroundColorProperty, BaseTheme.WindowBackgroundColor);
|
public BillingPage()
|
||||||
|
{
|
||||||
|
SetDynamicResource(BackgroundColorProperty, BaseTheme.WindowBackgroundColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,38 +3,39 @@ using System;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public class TitleDateConverter : IValueConverter
|
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public class TitleDateConverter : IValueConverter
|
||||||
{
|
{
|
||||||
if (value is DateTime date)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return date.ToString(Resource.TitleDateFormat);
|
if (value is DateTime date)
|
||||||
|
{
|
||||||
|
return date.ToString(Resource.TitleDateFormat);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MoneyConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
if (value is decimal d)
|
|
||||||
{
|
{
|
||||||
return "¥ " + d.ToString("n2", CultureInfo.InvariantCulture);
|
return value;
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public class MoneyConverter : IValueConverter
|
||||||
{
|
{
|
||||||
return value;
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is decimal d)
|
||||||
|
{
|
||||||
|
return "¥ " + d.ToString("n2", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,153 +2,154 @@
|
|||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public class TintImage : Image
|
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty PrimaryColorProperty = BindableProperty.Create(nameof(PrimaryColor), typeof(Color?), typeof(TintImage));
|
public class TintImage : Image
|
||||||
|
|
||||||
public Color? PrimaryColor
|
|
||||||
{
|
{
|
||||||
get => (Color?)GetValue(PrimaryColorProperty);
|
public static readonly BindableProperty PrimaryColorProperty = BindableProperty.Create(nameof(PrimaryColor), typeof(Color?), typeof(TintImage));
|
||||||
set => SetValue(PrimaryColorProperty, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LongPressButton : Button
|
public Color? PrimaryColor
|
||||||
{
|
|
||||||
public event EventHandler LongPressed;
|
|
||||||
|
|
||||||
public LongPressButton()
|
|
||||||
{
|
|
||||||
Padding = 0;
|
|
||||||
BackgroundColor = Color.Transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TriggerLongPress()
|
|
||||||
{
|
|
||||||
LongPressed?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OptionEntry : Entry { }
|
|
||||||
|
|
||||||
public abstract class OptionCell : ViewCell
|
|
||||||
{
|
|
||||||
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(OptionCell));
|
|
||||||
public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(OptionCell));
|
|
||||||
|
|
||||||
public string Title
|
|
||||||
{
|
|
||||||
get => (string)GetValue(TitleProperty);
|
|
||||||
set => SetValue(TitleProperty, value);
|
|
||||||
}
|
|
||||||
public Color BackgroundColor
|
|
||||||
{
|
|
||||||
get => (Color)GetValue(BackgroundColorProperty);
|
|
||||||
set => SetValue(BackgroundColorProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract View Content { get; }
|
|
||||||
|
|
||||||
public OptionCell()
|
|
||||||
{
|
|
||||||
View = new Grid
|
|
||||||
{
|
{
|
||||||
BindingContext = this,
|
get => (Color?)GetValue(PrimaryColorProperty);
|
||||||
Padding = new Thickness(20, 0),
|
set => SetValue(PrimaryColorProperty, value);
|
||||||
ColumnDefinitions =
|
|
||||||
{
|
|
||||||
new ColumnDefinition { Width = new GridLength(.3, GridUnitType.Star) },
|
|
||||||
new ColumnDefinition { Width = new GridLength(.7, GridUnitType.Star) }
|
|
||||||
},
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
LineBreakMode = LineBreakMode.TailTruncation,
|
|
||||||
VerticalOptions = LayoutOptions.Center
|
|
||||||
}
|
|
||||||
.Binding(Label.TextProperty, nameof(Title))
|
|
||||||
.DynamicResource(Label.TextColorProperty, BaseTheme.TextColor),
|
|
||||||
|
|
||||||
Content.GridColumn(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LongPressButton : Button
|
||||||
|
{
|
||||||
|
public event EventHandler LongPressed;
|
||||||
|
|
||||||
|
public LongPressButton()
|
||||||
|
{
|
||||||
|
Padding = 0;
|
||||||
|
BackgroundColor = Color.Transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerLongPress()
|
||||||
|
{
|
||||||
|
LongPressed?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OptionEntry : Entry { }
|
||||||
|
|
||||||
|
public abstract class OptionCell : ViewCell
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(OptionCell));
|
||||||
|
public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(OptionCell));
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => (string)GetValue(TitleProperty);
|
||||||
|
set => SetValue(TitleProperty, value);
|
||||||
|
}
|
||||||
|
public Color BackgroundColor
|
||||||
|
{
|
||||||
|
get => (Color)GetValue(BackgroundColorProperty);
|
||||||
|
set => SetValue(BackgroundColorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract View Content { get; }
|
||||||
|
|
||||||
|
public OptionCell()
|
||||||
|
{
|
||||||
|
View = new Grid
|
||||||
|
{
|
||||||
|
BindingContext = this,
|
||||||
|
Padding = new Thickness(20, 0),
|
||||||
|
ColumnDefinitions =
|
||||||
|
{
|
||||||
|
new ColumnDefinition { Width = new GridLength(.3, GridUnitType.Star) },
|
||||||
|
new ColumnDefinition { Width = new GridLength(.7, GridUnitType.Star) }
|
||||||
|
},
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
LineBreakMode = LineBreakMode.TailTruncation,
|
||||||
|
VerticalOptions = LayoutOptions.Center
|
||||||
|
}
|
||||||
|
.Binding(Label.TextProperty, nameof(Title))
|
||||||
|
.DynamicResource(Label.TextColorProperty, BaseTheme.TextColor),
|
||||||
|
|
||||||
|
Content.GridColumn(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OptionTextCell : OptionCell
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty DetailProperty = BindableProperty.Create(nameof(Detail), typeof(string), typeof(OptionTextCell));
|
||||||
|
|
||||||
|
public string Detail
|
||||||
|
{
|
||||||
|
get => (string)GetValue(DetailProperty);
|
||||||
|
set => SetValue(DetailProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override View Content => new Label
|
||||||
|
{
|
||||||
|
HorizontalOptions = LayoutOptions.End,
|
||||||
|
VerticalOptions = LayoutOptions.Center
|
||||||
|
}
|
||||||
|
.Binding(Label.TextProperty, nameof(Detail))
|
||||||
|
.DynamicResource(Label.TextColorProperty, BaseTheme.SecondaryTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OptionSwitchCell : OptionCell
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty IsToggledProperty = BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(OptionSwitchCell));
|
||||||
|
|
||||||
|
public bool IsToggled
|
||||||
|
{
|
||||||
|
get => (bool)GetValue(IsToggledProperty);
|
||||||
|
set => SetValue(IsToggledProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override View Content => new Switch
|
||||||
|
{
|
||||||
|
HorizontalOptions = LayoutOptions.End,
|
||||||
|
VerticalOptions = LayoutOptions.Center
|
||||||
|
}
|
||||||
|
.Binding(Switch.IsToggledProperty, nameof(IsToggled), BindingMode.TwoWay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OptionEntryCell : OptionCell
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(OptionEntryCell));
|
||||||
|
public static readonly BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(OptionEntryCell));
|
||||||
|
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(OptionEntryCell));
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get => (string)GetValue(TextProperty);
|
||||||
|
set => SetValue(TextProperty, value);
|
||||||
|
}
|
||||||
|
public Keyboard Keyboard
|
||||||
|
{
|
||||||
|
get => (Keyboard)GetValue(KeyboardProperty);
|
||||||
|
set => SetValue(KeyboardProperty, value);
|
||||||
|
}
|
||||||
|
public string Placeholder
|
||||||
|
{
|
||||||
|
get => (string)GetValue(PlaceholderProperty);
|
||||||
|
set => SetValue(PlaceholderProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override View Content => new OptionEntry
|
||||||
|
{
|
||||||
|
HorizontalOptions = LayoutOptions.Fill,
|
||||||
|
HorizontalTextAlignment = TextAlignment.End,
|
||||||
|
VerticalOptions = LayoutOptions.Center,
|
||||||
|
ReturnType = ReturnType.Next
|
||||||
|
}
|
||||||
|
.Binding(Entry.TextProperty, nameof(Text), BindingMode.TwoWay)
|
||||||
|
.Binding(InputView.KeyboardProperty, nameof(Keyboard))
|
||||||
|
.Binding(Entry.PlaceholderProperty, nameof(Placeholder))
|
||||||
|
.DynamicResource(Entry.TextColorProperty, BaseTheme.TextColor)
|
||||||
|
.DynamicResource(Entry.PlaceholderColorProperty, BaseTheme.SecondaryTextColor)
|
||||||
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
|
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public class OptionTextCell : OptionCell
|
|
||||||
{
|
|
||||||
public static readonly BindableProperty DetailProperty = BindableProperty.Create(nameof(Detail), typeof(string), typeof(OptionTextCell));
|
|
||||||
|
|
||||||
public string Detail
|
|
||||||
{
|
|
||||||
get => (string)GetValue(DetailProperty);
|
|
||||||
set => SetValue(DetailProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override View Content => new Label
|
|
||||||
{
|
|
||||||
HorizontalOptions = LayoutOptions.End,
|
|
||||||
VerticalOptions = LayoutOptions.Center
|
|
||||||
}
|
|
||||||
.Binding(Label.TextProperty, nameof(Detail))
|
|
||||||
.DynamicResource(Label.TextColorProperty, BaseTheme.SecondaryTextColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OptionSwitchCell : OptionCell
|
|
||||||
{
|
|
||||||
public static readonly BindableProperty IsToggledProperty = BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(OptionSwitchCell));
|
|
||||||
|
|
||||||
public bool IsToggled
|
|
||||||
{
|
|
||||||
get => (bool)GetValue(IsToggledProperty);
|
|
||||||
set => SetValue(IsToggledProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override View Content => new Switch
|
|
||||||
{
|
|
||||||
HorizontalOptions = LayoutOptions.End,
|
|
||||||
VerticalOptions = LayoutOptions.Center
|
|
||||||
}
|
|
||||||
.Binding(Switch.IsToggledProperty, nameof(IsToggled), BindingMode.TwoWay);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OptionEntryCell : OptionCell
|
|
||||||
{
|
|
||||||
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(OptionEntryCell));
|
|
||||||
public static readonly BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(OptionEntryCell));
|
|
||||||
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(OptionEntryCell));
|
|
||||||
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
get => (string)GetValue(TextProperty);
|
|
||||||
set => SetValue(TextProperty, value);
|
|
||||||
}
|
|
||||||
public Keyboard Keyboard
|
|
||||||
{
|
|
||||||
get => (Keyboard)GetValue(KeyboardProperty);
|
|
||||||
set => SetValue(KeyboardProperty, value);
|
|
||||||
}
|
|
||||||
public string Placeholder
|
|
||||||
{
|
|
||||||
get => (string)GetValue(PlaceholderProperty);
|
|
||||||
set => SetValue(PlaceholderProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override View Content => new OptionEntry
|
|
||||||
{
|
|
||||||
HorizontalOptions = LayoutOptions.Fill,
|
|
||||||
HorizontalTextAlignment = TextAlignment.End,
|
|
||||||
VerticalOptions = LayoutOptions.Center,
|
|
||||||
ReturnType = ReturnType.Next
|
|
||||||
}
|
|
||||||
.Binding(Entry.TextProperty, nameof(Text), BindingMode.TwoWay)
|
|
||||||
.Binding(InputView.KeyboardProperty, nameof(Keyboard))
|
|
||||||
.Binding(Entry.PlaceholderProperty, nameof(Placeholder))
|
|
||||||
.DynamicResource(Entry.TextColorProperty, BaseTheme.TextColor)
|
|
||||||
.DynamicResource(Entry.PlaceholderColorProperty, BaseTheme.SecondaryTextColor)
|
|
||||||
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
|
|
||||||
}
|
}
|
@ -1,15 +1,16 @@
|
|||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public class ShadowEffect : RoutingEffect
|
|
||||||
{
|
{
|
||||||
public float Radius { get; set; }
|
public class ShadowEffect : RoutingEffect
|
||||||
public Color Color { get; set; }
|
|
||||||
public Size Offect { get; set; }
|
|
||||||
public float Opacity { get; set; }
|
|
||||||
|
|
||||||
public ShadowEffect() : base($"Org.Tsanie.{nameof(ShadowEffect)}")
|
|
||||||
{
|
{
|
||||||
|
public float Radius { get; set; }
|
||||||
|
public Color Color { get; set; }
|
||||||
|
public Size Offect { get; set; }
|
||||||
|
public float Opacity { get; set; }
|
||||||
|
|
||||||
|
public ShadowEffect() : base($"Org.Tsanie.{nameof(ShadowEffect)}")
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,68 +1,69 @@
|
|||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public static partial class Definition
|
|
||||||
{
|
{
|
||||||
public static partial string GetCascadiaRegularFontFamily();
|
public static partial class Definition
|
||||||
public static partial string GetCascadiaBoldFontFamily();
|
|
||||||
public static partial string GetRobotoCondensedRegularFontFamily();
|
|
||||||
public static partial string GetRobotoCondensedBoldFontFamily();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ExtensionHelper
|
|
||||||
{
|
|
||||||
public static View Binding(this View view, BindableProperty property, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null)
|
|
||||||
{
|
{
|
||||||
view.SetBinding(property, path, mode, converter);
|
public static partial string GetCascadiaRegularFontFamily();
|
||||||
return view;
|
public static partial string GetCascadiaBoldFontFamily();
|
||||||
|
public static partial string GetRobotoCondensedRegularFontFamily();
|
||||||
|
public static partial string GetRobotoCondensedBoldFontFamily();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static View DynamicResource(this View view, BindableProperty property, string key)
|
public static class ExtensionHelper
|
||||||
{
|
{
|
||||||
view.SetDynamicResource(property, key);
|
public static View Binding(this View view, BindableProperty property, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null)
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static View GridColumn(this View view, int column)
|
|
||||||
{
|
|
||||||
Grid.SetColumn(view, column);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static View GridRow(this View view, int row)
|
|
||||||
{
|
|
||||||
Grid.SetRow(view, row);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Tap : IDisposable
|
|
||||||
{
|
|
||||||
private readonly static object sync = new();
|
|
||||||
private static Tap instance;
|
|
||||||
|
|
||||||
private bool busy = false;
|
|
||||||
|
|
||||||
private Tap() { }
|
|
||||||
|
|
||||||
public static bool IsBusy => instance?.busy ?? false;
|
|
||||||
|
|
||||||
public static Tap Start()
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
{
|
||||||
(instance ??= new Tap()).busy = true;
|
view.SetBinding(property, path, mode, converter);
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public static View DynamicResource(this View view, BindableProperty property, string key)
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
{
|
||||||
busy = false;
|
view.SetDynamicResource(property, key);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static View GridColumn(this View view, int column)
|
||||||
|
{
|
||||||
|
Grid.SetColumn(view, column);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static View GridRow(this View view, int row)
|
||||||
|
{
|
||||||
|
Grid.SetRow(view, row);
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public class Tap : IDisposable
|
||||||
|
{
|
||||||
|
private readonly static object sync = new();
|
||||||
|
private static Tap instance;
|
||||||
|
|
||||||
|
private bool busy = false;
|
||||||
|
|
||||||
|
private Tap() { }
|
||||||
|
|
||||||
|
public static bool IsBusy => instance?.busy ?? false;
|
||||||
|
|
||||||
|
public static Tap Start()
|
||||||
|
{
|
||||||
|
lock (sync)
|
||||||
|
{
|
||||||
|
(instance ??= new Tap()).busy = true;
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (sync)
|
||||||
|
{
|
||||||
|
busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +1,44 @@
|
|||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
using System;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Views;
|
namespace Billing.Views
|
||||||
|
|
||||||
public partial class AccountPage : BillingPage
|
|
||||||
{
|
{
|
||||||
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AccountPage));
|
public partial class AccountPage : BillingPage
|
||||||
private static readonly BindableProperty AssetProperty = BindableProperty.Create(nameof(Asset), typeof(decimal), typeof(AccountPage));
|
|
||||||
private static readonly BindableProperty LiabilityProperty = BindableProperty.Create(nameof(Liability), typeof(decimal), typeof(AccountPage));
|
|
||||||
|
|
||||||
public decimal Balance => (decimal)GetValue(BalanceProperty);
|
|
||||||
public decimal Asset => (decimal)GetValue(AssetProperty);
|
|
||||||
public decimal Liability => (decimal)GetValue(LiabilityProperty);
|
|
||||||
|
|
||||||
public Command AddAccount { get; }
|
|
||||||
|
|
||||||
public AccountPage()
|
|
||||||
{
|
{
|
||||||
AddAccount = new Command(OnAddAccount);
|
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AccountPage));
|
||||||
|
private static readonly BindableProperty AssetProperty = BindableProperty.Create(nameof(Asset), typeof(decimal), typeof(AccountPage));
|
||||||
|
private static readonly BindableProperty LiabilityProperty = BindableProperty.Create(nameof(Liability), typeof(decimal), typeof(AccountPage));
|
||||||
|
|
||||||
InitializeComponent();
|
public decimal Balance => (decimal)GetValue(BalanceProperty);
|
||||||
}
|
public decimal Asset => (decimal)GetValue(AssetProperty);
|
||||||
|
public decimal Liability => (decimal)GetValue(LiabilityProperty);
|
||||||
|
|
||||||
private async void OnAddAccount()
|
public Command AddAccount { get; }
|
||||||
{
|
|
||||||
if (Tap.IsBusy)
|
public AccountPage()
|
||||||
{
|
{
|
||||||
return;
|
AddAccount = new Command(OnAddAccount);
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
using (Tap.Start())
|
|
||||||
|
private async void OnAddAccount()
|
||||||
{
|
{
|
||||||
var page = new AddAccountPage();
|
if (Tap.IsBusy)
|
||||||
page.AccountChecked += AccountChecked;
|
{
|
||||||
await Navigation.PushAsync(page);
|
return;
|
||||||
|
}
|
||||||
|
using (Tap.Start())
|
||||||
|
{
|
||||||
|
var page = new AddAccountPage();
|
||||||
|
page.AccountChecked += AccountChecked;
|
||||||
|
await Navigation.PushAsync(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AccountChecked(object sender, AccountEventArgs e)
|
||||||
|
{
|
||||||
|
Helper.Debug(e.Account.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void AccountChecked(object sender, AccountEventArgs e)
|
|
||||||
{
|
|
||||||
Helper.Debug(e.Account.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,84 +3,85 @@ using Billing.UI;
|
|||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Views;
|
namespace Billing.Views
|
||||||
|
|
||||||
public partial class AddAccountPage : BillingPage
|
|
||||||
{
|
{
|
||||||
private static readonly BindableProperty AccountNameProperty = BindableProperty.Create(nameof(AccountName), typeof(string), typeof(AddAccountPage));
|
public partial class AddAccountPage : BillingPage
|
||||||
private static readonly BindableProperty AccountIconProperty = BindableProperty.Create(nameof(AccountIcon), typeof(string), typeof(AddAccountPage));
|
{
|
||||||
private static readonly BindableProperty CategoryProperty = BindableProperty.Create(nameof(Category), typeof(string), typeof(AddAccountPage));
|
private static readonly BindableProperty AccountNameProperty = BindableProperty.Create(nameof(AccountName), typeof(string), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AddAccountPage));
|
private static readonly BindableProperty AccountIconProperty = BindableProperty.Create(nameof(AccountIcon), typeof(string), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty MemoProperty = BindableProperty.Create(nameof(Memo), typeof(string), typeof(AddAccountPage));
|
private static readonly BindableProperty CategoryProperty = BindableProperty.Create(nameof(Category), typeof(string), typeof(AddAccountPage));
|
||||||
|
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AddAccountPage));
|
||||||
|
private static readonly BindableProperty MemoProperty = BindableProperty.Create(nameof(Memo), typeof(string), typeof(AddAccountPage));
|
||||||
|
|
||||||
public string AccountName
|
public string AccountName
|
||||||
{
|
|
||||||
get => (string)GetValue(AccountNameProperty);
|
|
||||||
set => SetValue(AccountNameProperty, value);
|
|
||||||
}
|
|
||||||
public string AccountIcon
|
|
||||||
{
|
|
||||||
get => (string)GetValue(AccountIconProperty);
|
|
||||||
set => SetValue(AccountIconProperty, value);
|
|
||||||
}
|
|
||||||
public string Category
|
|
||||||
{
|
|
||||||
get => (string)GetValue(CategoryProperty);
|
|
||||||
set => SetValue(CategoryProperty, value);
|
|
||||||
}
|
|
||||||
public decimal Balance
|
|
||||||
{
|
|
||||||
get => (decimal)GetValue(BalanceProperty);
|
|
||||||
set => SetValue(BalanceProperty, value);
|
|
||||||
}
|
|
||||||
public string Memo
|
|
||||||
{
|
|
||||||
get => (string)GetValue(MemoProperty);
|
|
||||||
set => SetValue(MemoProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Account account;
|
|
||||||
|
|
||||||
public Command CheckAccount { get; }
|
|
||||||
|
|
||||||
public event EventHandler<AccountEventArgs> AccountChecked;
|
|
||||||
|
|
||||||
public AddAccountPage()
|
|
||||||
{
|
|
||||||
CheckAccount = new Command(OnCheckAccount);
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddAccountPage(Account account)
|
|
||||||
{
|
|
||||||
this.account = account;
|
|
||||||
AccountName = account.Name;
|
|
||||||
AccountIcon = account.Icon;
|
|
||||||
Category = account.Category.ToString();
|
|
||||||
Balance = account.Balance;
|
|
||||||
Memo = account.Memo;
|
|
||||||
CheckAccount = new Command(OnCheckAccount);
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCheckAccount()
|
|
||||||
{
|
|
||||||
AccountChecked?.Invoke(this, new AccountEventArgs
|
|
||||||
{
|
{
|
||||||
Account = new Account
|
get => (string)GetValue(AccountNameProperty);
|
||||||
{
|
set => SetValue(AccountNameProperty, value);
|
||||||
Id = account?.Id ?? -1,
|
}
|
||||||
Name = AccountName,
|
public string AccountIcon
|
||||||
Icon = AccountIcon,
|
{
|
||||||
//Category = Category,
|
get => (string)GetValue(AccountIconProperty);
|
||||||
Balance = Balance,
|
set => SetValue(AccountIconProperty, value);
|
||||||
Memo = Memo
|
}
|
||||||
}
|
public string Category
|
||||||
});
|
{
|
||||||
}
|
get => (string)GetValue(CategoryProperty);
|
||||||
}
|
set => SetValue(CategoryProperty, value);
|
||||||
|
}
|
||||||
|
public decimal Balance
|
||||||
|
{
|
||||||
|
get => (decimal)GetValue(BalanceProperty);
|
||||||
|
set => SetValue(BalanceProperty, value);
|
||||||
|
}
|
||||||
|
public string Memo
|
||||||
|
{
|
||||||
|
get => (string)GetValue(MemoProperty);
|
||||||
|
set => SetValue(MemoProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public class AccountEventArgs : EventArgs
|
private readonly Account account;
|
||||||
{
|
|
||||||
public Account Account { get; set; }
|
public Command CheckAccount { get; }
|
||||||
}
|
|
||||||
|
public event EventHandler<AccountEventArgs> AccountChecked;
|
||||||
|
|
||||||
|
public AddAccountPage()
|
||||||
|
{
|
||||||
|
CheckAccount = new Command(OnCheckAccount);
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddAccountPage(Account account)
|
||||||
|
{
|
||||||
|
this.account = account;
|
||||||
|
AccountName = account.Name;
|
||||||
|
AccountIcon = account.Icon;
|
||||||
|
Category = account.Category.ToString();
|
||||||
|
Balance = account.Balance;
|
||||||
|
Memo = account.Memo;
|
||||||
|
CheckAccount = new Command(OnCheckAccount);
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCheckAccount()
|
||||||
|
{
|
||||||
|
AccountChecked?.Invoke(this, new AccountEventArgs
|
||||||
|
{
|
||||||
|
Account = new Account
|
||||||
|
{
|
||||||
|
Id = account?.Id ?? -1,
|
||||||
|
Name = AccountName,
|
||||||
|
Icon = AccountIcon,
|
||||||
|
//Category = Category,
|
||||||
|
Balance = Balance,
|
||||||
|
Memo = Memo
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Account Account { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
|
|
||||||
namespace Billing.Views;
|
namespace Billing.Views
|
||||||
|
|
||||||
public partial class AddBillPage : BillingPage
|
|
||||||
{
|
{
|
||||||
public AddBillPage()
|
public partial class AddBillPage : BillingPage
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
public AddBillPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,43 +2,44 @@ using Billing.UI;
|
|||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Views;
|
namespace Billing.Views
|
||||||
|
|
||||||
public partial class BillPage : BillingPage
|
|
||||||
{
|
{
|
||||||
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
|
public partial class BillPage : BillingPage
|
||||||
|
|
||||||
public DateTime SelectedDate
|
|
||||||
{
|
{
|
||||||
get => (DateTime)GetValue(SelectedDateProperty);
|
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
|
||||||
set => SetValue(SelectedDateProperty, value);
|
|
||||||
|
public DateTime SelectedDate
|
||||||
|
{
|
||||||
|
get => (DateTime)GetValue(SelectedDateProperty);
|
||||||
|
set => SetValue(SelectedDateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command AddBilling { get; }
|
||||||
|
|
||||||
|
public BillPage()
|
||||||
|
{
|
||||||
|
AddBilling = new Command(OnAddBilling);
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
billingDate.SetDateTime(DateTime.Now);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDateSelected(object sender, DateEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedDate = e.Date;
|
||||||
|
|
||||||
|
// TODO: while selecting date
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTitleDateLongPressed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
billingDate.SetDateTime(DateTime.Now);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnAddBilling()
|
||||||
|
{
|
||||||
|
await Navigation.PushAsync(new AddBillPage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public Command AddBilling { get; }
|
|
||||||
|
|
||||||
public BillPage()
|
|
||||||
{
|
|
||||||
AddBilling = new Command(OnAddBilling);
|
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
billingDate.SetDateTime(DateTime.Now);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDateSelected(object sender, DateEventArgs e)
|
|
||||||
{
|
|
||||||
SelectedDate = e.Date;
|
|
||||||
|
|
||||||
// TODO: while selecting date
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTitleDateLongPressed(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
billingDate.SetDateTime(DateTime.Now);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnAddBilling()
|
|
||||||
{
|
|
||||||
await Navigation.PushAsync(new AddBillPage());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,12 @@
|
|||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
|
|
||||||
namespace Billing.Views;
|
namespace Billing.Views
|
||||||
|
|
||||||
public partial class SettingPage : BillingPage
|
|
||||||
{
|
{
|
||||||
public SettingPage()
|
public partial class SettingPage : BillingPage
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
public SettingPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,14 +16,13 @@
|
|||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
|
||||||
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
|
||||||
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
||||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -65,8 +64,8 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Definition.cs" />
|
<Compile Include="Definition.cs" />
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
using Foundation;
|
using Foundation;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
|
|
||||||
namespace Billing.iOS;
|
namespace Billing.iOS
|
||||||
|
|
||||||
// The UIApplicationDelegate for the application. This class is responsible for launching the
|
|
||||||
// User Interface of the application, as well as listening (and optionally responding) to
|
|
||||||
// application events from iOS.
|
|
||||||
[Register(nameof(AppDelegate))]
|
|
||||||
public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
|
|
||||||
{
|
{
|
||||||
//
|
// The UIApplicationDelegate for the application. This class is responsible for launching the
|
||||||
// This method is invoked when the application has loaded and is ready to run. In this
|
// User Interface of the application, as well as listening (and optionally responding) to
|
||||||
// method you should instantiate the window, load the UI into it and then make the window
|
// application events from iOS.
|
||||||
// visible.
|
[Register(nameof(AppDelegate))]
|
||||||
//
|
public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
|
||||||
// You have 17 seconds to return from this method, or iOS will terminate your application.
|
|
||||||
//
|
|
||||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
|
||||||
{
|
{
|
||||||
Xamarin.Forms.Forms.Init();
|
//
|
||||||
LoadApplication(new App());
|
// This method is invoked when the application has loaded and is ready to run. In this
|
||||||
|
// method you should instantiate the window, load the UI into it and then make the window
|
||||||
|
// visible.
|
||||||
|
//
|
||||||
|
// You have 17 seconds to return from this method, or iOS will terminate your application.
|
||||||
|
//
|
||||||
|
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||||
|
{
|
||||||
|
Xamarin.Forms.Forms.Init();
|
||||||
|
LoadApplication(new App());
|
||||||
|
|
||||||
return base.FinishedLaunching(app, options);
|
return base.FinishedLaunching(app, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,7 @@
|
|||||||
<MtouchEnableSGenConc>true</MtouchEnableSGenConc>
|
<MtouchEnableSGenConc>true</MtouchEnableSGenConc>
|
||||||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
|
||||||
<ProvisioningType>automatic</ProvisioningType>
|
<ProvisioningType>automatic</ProvisioningType>
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -138,8 +138,8 @@
|
|||||||
<Reference Include="System.Numerics.Vectors" />
|
<Reference Include="System.Numerics.Vectors" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BundleResource Include="Resources\bars.png" />
|
<BundleResource Include="Resources\bars.png" />
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
namespace Billing.UI;
|
namespace Billing.UI
|
||||||
|
|
||||||
public static partial class Definition
|
|
||||||
{
|
{
|
||||||
public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular";
|
public static partial class Definition
|
||||||
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold";
|
{
|
||||||
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular";
|
public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular";
|
||||||
public static partial string GetRobotoCondensedBoldFontFamily() => "RobotoCondensed-Bold";
|
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold";
|
||||||
}
|
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular";
|
||||||
|
public static partial string GetRobotoCondensedBoldFontFamily() => "RobotoCondensed-Bold";
|
||||||
|
}
|
||||||
|
}
|
@ -7,37 +7,38 @@ using Xamarin.Forms.Platform.iOS;
|
|||||||
|
|
||||||
[assembly: ResolutionGroupName("Org.Tsanie")]
|
[assembly: ResolutionGroupName("Org.Tsanie")]
|
||||||
[assembly: ExportEffect(typeof(ShadowEffectPlatform), nameof(ShadowEffect))]
|
[assembly: ExportEffect(typeof(ShadowEffectPlatform), nameof(ShadowEffect))]
|
||||||
namespace Billing.iOS.Effects;
|
namespace Billing.iOS.Effects
|
||||||
|
|
||||||
public class ShadowEffectPlatform : PlatformEffect
|
|
||||||
{
|
{
|
||||||
protected override void OnAttached()
|
public class ShadowEffectPlatform : PlatformEffect
|
||||||
{
|
{
|
||||||
try
|
protected override void OnAttached()
|
||||||
{
|
{
|
||||||
var effect = (ShadowEffect)Element.Effects.FirstOrDefault(e => e is ShadowEffect);
|
try
|
||||||
if (effect != null)
|
|
||||||
{
|
{
|
||||||
var layer = Control.Layer;
|
var effect = (ShadowEffect)Element.Effects.FirstOrDefault(e => e is ShadowEffect);
|
||||||
layer.ShadowRadius = effect.Radius;
|
if (effect != null)
|
||||||
layer.ShadowColor = effect.Color.ToCGColor();
|
{
|
||||||
layer.ShadowOffset = effect.Offect.ToSizeF();
|
var layer = Control.Layer;
|
||||||
layer.ShadowOpacity = effect.Opacity;
|
layer.ShadowRadius = effect.Radius;
|
||||||
|
layer.ShadowColor = effect.Color.ToCGColor();
|
||||||
|
layer.ShadowOffset = effect.Offect.ToSizeF();
|
||||||
|
layer.ShadowOpacity = effect.Opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Helper.Error("shadow.effect.attached", $"Cannot set property on attached control, error: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Helper.Error("shadow.effect.attached", $"Cannot set property on attached control, error: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetached()
|
protected override void OnDetached()
|
||||||
{
|
|
||||||
var layer = Control?.Layer;
|
|
||||||
if (layer != null)
|
|
||||||
{
|
{
|
||||||
layer.ShadowColor = Color.Transparent.ToCGColor();
|
var layer = Control?.Layer;
|
||||||
layer.ShadowOpacity = 0;
|
if (layer != null)
|
||||||
|
{
|
||||||
|
layer.ShadowColor = Color.Transparent.ToCGColor();
|
||||||
|
layer.ShadowOpacity = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,15 @@
|
|||||||
using UIKit;
|
using UIKit;
|
||||||
|
|
||||||
namespace Billing.iOS;
|
namespace Billing.iOS
|
||||||
|
|
||||||
public class Application
|
|
||||||
{
|
{
|
||||||
// This is the main entry point of the application.
|
public class Application
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
{
|
||||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
// This is the main entry point of the application.
|
||||||
// you can specify it here.
|
static void Main(string[] args)
|
||||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
{
|
||||||
|
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||||
|
// you can specify it here.
|
||||||
|
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,72 +4,73 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Billing.Languages;
|
namespace Billing.Languages
|
||||||
|
|
||||||
public partial class PlatformCulture
|
|
||||||
{
|
{
|
||||||
public partial string GetNamespace()
|
public partial class PlatformCulture
|
||||||
{
|
{
|
||||||
return typeof(AppDelegate).Namespace;
|
public partial string GetNamespace()
|
||||||
}
|
|
||||||
|
|
||||||
public partial void Init()
|
|
||||||
{
|
|
||||||
string lang;
|
|
||||||
if (NSLocale.PreferredLanguages.Length > 0)
|
|
||||||
{
|
{
|
||||||
var pref = NSLocale.PreferredLanguages[0];
|
return typeof(AppDelegate).Namespace;
|
||||||
lang = iOSToDotnetLanguage(pref);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lang = "zh-CN";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CultureInfo ci;
|
public partial void Init()
|
||||||
Init(lang);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ci = new CultureInfo(Language);
|
|
||||||
}
|
|
||||||
catch (CultureNotFoundException e)
|
|
||||||
{
|
{
|
||||||
|
string lang;
|
||||||
|
if (NSLocale.PreferredLanguages.Length > 0)
|
||||||
|
{
|
||||||
|
var pref = NSLocale.PreferredLanguages[0];
|
||||||
|
lang = iOSToDotnetLanguage(pref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lang = "zh-CN";
|
||||||
|
}
|
||||||
|
|
||||||
|
CultureInfo ci;
|
||||||
|
Init(lang);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fallback = ToDotnetFallbackLanguage();
|
ci = new CultureInfo(Language);
|
||||||
Helper.Debug($"{lang} failed, trying {fallback} ({e.Message})");
|
|
||||||
ci = new CultureInfo(fallback);
|
|
||||||
}
|
}
|
||||||
catch (CultureNotFoundException e1)
|
catch (CultureNotFoundException e)
|
||||||
{
|
{
|
||||||
Helper.Error("culture.get", $"{lang} couldn't be set, using 'zh-CN' ({e1.Message})");
|
try
|
||||||
ci = new CultureInfo("zh-CN");
|
{
|
||||||
|
var fallback = ToDotnetFallbackLanguage();
|
||||||
|
Helper.Debug($"{lang} failed, trying {fallback} ({e.Message})");
|
||||||
|
ci = new CultureInfo(fallback);
|
||||||
|
}
|
||||||
|
catch (CultureNotFoundException e1)
|
||||||
|
{
|
||||||
|
Helper.Error("culture.get", $"{lang} couldn't be set, using 'zh-CN' ({e1.Message})");
|
||||||
|
ci = new CultureInfo("zh-CN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread.CurrentThread.CurrentCulture = ci;
|
||||||
|
Thread.CurrentThread.CurrentUICulture = ci;
|
||||||
|
|
||||||
|
Helper.Debug($"CurrentCulture set: {ci.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.CurrentThread.CurrentCulture = ci;
|
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
||||||
Thread.CurrentThread.CurrentUICulture = ci;
|
private static string iOSToDotnetLanguage(string iOSLanguage)
|
||||||
|
|
||||||
Helper.Debug($"CurrentCulture set: {ci.Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
|
|
||||||
private static string iOSToDotnetLanguage(string iOSLanguage)
|
|
||||||
{
|
|
||||||
//certain languages need to be converted to CultureInfo equivalent
|
|
||||||
string netLanguage = iOSLanguage switch
|
|
||||||
{
|
{
|
||||||
// Not supported .NET culture
|
//certain languages need to be converted to CultureInfo equivalent
|
||||||
"ms-MY" or "ms-SG" => "ms", // closest supported
|
string netLanguage = iOSLanguage switch
|
||||||
// "Schwiizertüütsch (Swiss German)" not supported .NET culture
|
{
|
||||||
"gsw-CH" => "de-CH", // closest supported
|
// Not supported .NET culture
|
||||||
|
"ms-MY" or "ms-SG" => "ms", // closest supported
|
||||||
|
// "Schwiizertüütsch (Swiss German)" not supported .NET culture
|
||||||
|
"gsw-CH" => "de-CH", // closest supported
|
||||||
|
|
||||||
// add more application-specific cases here (if required)
|
// add more application-specific cases here (if required)
|
||||||
// ONLY use cultures that have been tested and known to work
|
// ONLY use cultures that have been tested and known to work
|
||||||
|
|
||||||
_ => iOSLanguage,
|
_ => iOSLanguage,
|
||||||
};
|
};
|
||||||
Helper.Debug($"iOS Language: {iOSLanguage}, .NET Language/Locale: {netLanguage}");
|
Helper.Debug($"iOS Language: {iOSLanguage}, .NET Language/Locale: {netLanguage}");
|
||||||
return netLanguage;
|
return netLanguage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,39 +5,40 @@ using Xamarin.Forms;
|
|||||||
using Xamarin.Forms.Platform.iOS;
|
using Xamarin.Forms.Platform.iOS;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(LongPressButton), typeof(LongPressButtonRenderer))]
|
[assembly: ExportRenderer(typeof(LongPressButton), typeof(LongPressButtonRenderer))]
|
||||||
namespace Billing.iOS.Renderers;
|
namespace Billing.iOS.Renderers
|
||||||
|
|
||||||
public class LongPressButtonRenderer : ButtonRenderer
|
|
||||||
{
|
{
|
||||||
private UILongPressGestureRecognizer longGesture;
|
public class LongPressButtonRenderer : ButtonRenderer
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
private UILongPressGestureRecognizer longGesture;
|
||||||
|
|
||||||
if (Element is LongPressButton)
|
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
||||||
{
|
{
|
||||||
longGesture = new UILongPressGestureRecognizer(OnLongPressed);
|
base.OnElementChanged(e);
|
||||||
AddGestureRecognizer(longGesture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
if (Element is LongPressButton)
|
||||||
{
|
{
|
||||||
if (longGesture != null)
|
longGesture = new UILongPressGestureRecognizer(OnLongPressed);
|
||||||
{
|
AddGestureRecognizer(longGesture);
|
||||||
RemoveGestureRecognizer(longGesture);
|
}
|
||||||
longGesture = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
protected override void Dispose(bool disposing)
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLongPressed(UILongPressGestureRecognizer e)
|
|
||||||
{
|
|
||||||
if (Element is LongPressButton button)
|
|
||||||
{
|
{
|
||||||
button.TriggerLongPress();
|
if (longGesture != null)
|
||||||
|
{
|
||||||
|
RemoveGestureRecognizer(longGesture);
|
||||||
|
longGesture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLongPressed(UILongPressGestureRecognizer e)
|
||||||
|
{
|
||||||
|
if (Element is LongPressButton button)
|
||||||
|
{
|
||||||
|
button.TriggerLongPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,18 +5,19 @@ using Xamarin.Forms;
|
|||||||
using Xamarin.Forms.Platform.iOS;
|
using Xamarin.Forms.Platform.iOS;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(OptionEntry), typeof(OptionEntryRenderer))]
|
[assembly: ExportRenderer(typeof(OptionEntry), typeof(OptionEntryRenderer))]
|
||||||
namespace Billing.iOS.Renderers;
|
namespace Billing.iOS.Renderers
|
||||||
|
|
||||||
public class OptionEntryRenderer : EntryRenderer
|
|
||||||
{
|
{
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
public class OptionEntryRenderer : EntryRenderer
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
||||||
|
|
||||||
var control = Control;
|
|
||||||
if (control != null)
|
|
||||||
{
|
{
|
||||||
control.BorderStyle = UITextBorderStyle.None;
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
var control = Control;
|
||||||
|
if (control != null)
|
||||||
|
{
|
||||||
|
control.BorderStyle = UITextBorderStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,27 +5,28 @@ using Xamarin.Forms;
|
|||||||
using Xamarin.Forms.Platform.iOS;
|
using Xamarin.Forms.Platform.iOS;
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(TintImage), typeof(TintImageRenderer))]
|
[assembly: ExportRenderer(typeof(TintImage), typeof(TintImageRenderer))]
|
||||||
namespace Billing.iOS.Renderers;
|
namespace Billing.iOS.Renderers
|
||||||
|
|
||||||
public class TintImageRenderer : ImageRenderer
|
|
||||||
{
|
{
|
||||||
|
public class TintImageRenderer : ImageRenderer
|
||||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
{
|
||||||
base.OnElementPropertyChanged(sender, e);
|
|
||||||
|
|
||||||
if (e.PropertyName == nameof(TintImage.PrimaryColor) && Control != null && Element is TintImage image)
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Control.TintColor = image.PrimaryColor?.ToUIColor();
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == nameof(TintImage.PrimaryColor) && Control != null && Element is TintImage image)
|
||||||
|
{
|
||||||
|
Control.TintColor = image.PrimaryColor?.ToUIColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (Control != null && Element is TintImage image)
|
||||||
|
{
|
||||||
|
Control.TintColor = image.PrimaryColor?.ToUIColor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
|
}
|
||||||
{
|
|
||||||
base.OnElementChanged(e);
|
|
||||||
|
|
||||||
if (Control != null && Element is TintImage image)
|
|
||||||
{
|
|
||||||
Control.TintColor = image.PrimaryColor?.ToUIColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user