ViewModelBase.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Threading;
  4. using CommunityToolkit.Mvvm.ComponentModel;
  5. using CommunityToolkit.Mvvm.Input;
  6. using EventBus;
  7. using Mono.CompilerServices.SymbolWriter;
  8. using Shaker.Models;
  9. using SukiUI.Dialogs;
  10. using SukiUI.Toasts;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.ComponentModel;
  14. using System.Diagnostics.CodeAnalysis;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Reflection;
  18. using System.Runtime.CompilerServices;
  19. using System.Windows.Input;
  20. namespace ShakerApp.ViewModels;
  21. public abstract class ViewModelBase<TModel> : ObservableObject where TModel : IModel
  22. {
  23. private string Caption => (string)(App.Current?.FindResource("PromptTitle") ?? "");
  24. private string Yes => (string)(App.Current?.FindResource("PromptYes") ?? "");
  25. private string No => (string)(App.Current?.FindResource("PromptNo") ?? "");
  26. public virtual double Width => double.NaN;
  27. public virtual double Height => double.NaN;
  28. public virtual bool CanResize => true;
  29. private List<FieldInfo> AllFields = new List<FieldInfo>();
  30. private List<(string,IReadOnlyList<string>)> AllProperties = new List<(string, IReadOnlyList<string>)>();
  31. [AllowNull]
  32. public Action CloseWindowAction { get; set; }
  33. public ISukiToastManager ToastManager { get; } = new SukiToastManager();
  34. public ISukiDialogManager DialogManager { get; } = new SukiDialogManager();
  35. [AllowNull]
  36. public virtual Type Content
  37. {
  38. get => content;
  39. set => SetProperty(ref content , value);
  40. }
  41. private string title = string.Empty;
  42. private bool saveIsEnabled= false;
  43. public bool SaveIsEnabled
  44. {
  45. get { return saveIsEnabled; }
  46. set {SetProperty(ref saveIsEnabled , value); }
  47. }
  48. [AllowNull]
  49. public virtual bool CanCancel { get; } = typeof(TModel).IsAnsiClass && !typeof(TModel).IsAbstract;
  50. public ViewModelBase()
  51. {
  52. if (typeof(TModel).IsAnsiClass && !typeof(TModel).IsAbstract)
  53. {
  54. Model = Activator.CreateInstance<TModel>();
  55. AllFields = typeof(TModel).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
  56. this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
  57. .Where(x => x.GetCustomAttribute<ViewModels.PropertyAssociationAttribute>() != null)
  58. .ToList()
  59. .ForEach(x =>
  60. {
  61. var att = x.GetCustomAttribute<PropertyAssociationAttribute>();
  62. if (att == null || att.Names.Count ==0) return;
  63. AllProperties.Add((x.Name, att.Names));
  64. });
  65. }
  66. }
  67. public bool ShowError(string msg)
  68. {
  69. return Tools.DispatherInovke.Inovke(()=> DialogManager.CreateDialog()
  70. .OfType(Avalonia.Controls.Notifications.NotificationType.Error)
  71. .WithTitle(Caption)
  72. .WithContent(msg)
  73. .WithActionButton
  74. (Yes, _ => { }, true).TryShow());
  75. }
  76. public bool ShowAsk(string msg, Action? yesaction = null, Action? noaction = null)
  77. {
  78. return Tools.DispatherInovke.Inovke(()=> DialogManager.CreateDialog()
  79. .OfType(Avalonia.Controls.Notifications.NotificationType.Information)
  80. .WithTitle(Caption)
  81. .WithContent(msg)
  82. .WithActionButton(Yes, _ => yesaction?.Invoke(), true)
  83. .WithActionButton(No, _ => noaction?.Invoke(), true)
  84. .TryShow());
  85. }
  86. public bool ShowInfo(string msg)
  87. {
  88. return Tools.DispatherInovke.Inovke(()=> DialogManager.CreateDialog()
  89. .OfType(Avalonia.Controls.Notifications.NotificationType.Information)
  90. .WithTitle(Caption)
  91. .WithContent(msg)
  92. .WithActionButton
  93. (Yes, _ => { }, true).TryShow());
  94. }
  95. public bool ShowSuccess(string msg)
  96. {
  97. return Tools.DispatherInovke.Inovke(()=> DialogManager.CreateDialog()
  98. .OfType(Avalonia.Controls.Notifications.NotificationType.Success)
  99. .WithTitle(Caption)
  100. .WithContent(msg)
  101. .WithActionButton
  102. (Yes, _ => { }, true).TryShow());
  103. }
  104. public bool ShowWarning(string msg)
  105. {
  106. return Tools.DispatherInovke.Inovke(()=> DialogManager.CreateDialog()
  107. .OfType(Avalonia.Controls.Notifications.NotificationType.Warning)
  108. .WithTitle(Caption)
  109. .WithContent(msg)
  110. .WithActionButton
  111. (Yes, _ => { }, true).TryShow());
  112. }
  113. public void ShowToast(string msg,Avalonia.Controls.Notifications.NotificationType type = Avalonia.Controls.Notifications.NotificationType.Information)
  114. {
  115. if (ToastManager == null) return;
  116. Tools.DispatherInovke.Inovke(()=> ToastManager.CreateToast()
  117. .WithTitle(Caption)
  118. .WithContent(msg)
  119. .OfType(type)
  120. .Dismiss().After(TimeSpan.FromSeconds(3))
  121. .Dismiss().ByClicking()
  122. .Queue());
  123. }
  124. public ViewModelBase(TModel model):this()
  125. {
  126. UpDateModel(model);
  127. }
  128. public virtual void ReceiveServiceModel(TModel model)
  129. {
  130. if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
  131. if(lastModel!=null)
  132. {
  133. lastModel = (TModel)model.Clone();
  134. }
  135. UpDateModel(model);
  136. }
  137. public virtual void UpDateModel(TModel model)
  138. {
  139. if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
  140. List<string> changedNames = new List<string>();
  141. if (model == null || AllFields.Count==0) return;
  142. if (Model == null)
  143. {
  144. changedNames = AllFields.Select(x => x.Name).ToList();
  145. }
  146. else
  147. {
  148. AllFields.ForEach(x =>
  149. {
  150. var lastvalue = x.GetValue(Model);
  151. var fieldvalue = x.GetValue(model);
  152. if (!Object.Equals(lastvalue, fieldvalue))
  153. {
  154. changedNames.Add(x.Name);
  155. }
  156. });
  157. }
  158. Model = model;
  159. if (changedNames.Count == 0) return;
  160. List<string> nochanged = new List<string>();
  161. changedNames.ForEach(x =>
  162. {
  163. bool state = false;
  164. AllProperties.ForEach(y =>
  165. {
  166. if(y.Item2.Contains(x))
  167. {
  168. OnPropertyChanged(y.Item1);
  169. state = true;
  170. }
  171. });
  172. if (!state) nochanged.Add(x);
  173. });
  174. if (nochanged.Count == 0) return;
  175. RefreshUI(nochanged);
  176. }
  177. public virtual void Init()
  178. {
  179. }
  180. protected virtual void RefreshUI(List<string> changedNames)
  181. {
  182. }
  183. private bool cansave = false;
  184. public virtual string OKContent => "Save";
  185. public virtual string CancelContent =>"Cancel";
  186. [AllowNull]
  187. private TModel model = default;
  188. public virtual TModel Model { get => model; set => SetProperty(ref model, value); }
  189. [return: NotNull]
  190. protected EventBus.EventBroker.EventData<TData> GetEvent<TData>() => (EventBroker.EventData<TData>)EventBroker.Instance.GetEvent<TData>();
  191. [return: NotNull]
  192. protected EventBus.EventBroker.EventData<TData, T> GetEvent<TData, T>() => (EventBroker.EventData<TData, T>)EventBroker.Instance.GetEvent<TData, T>();
  193. [return: NotNull]
  194. public EventBus.EventBroker.AnonymousEventData GetEvent([NotNull] string eventName) => (EventBroker.AnonymousEventData)EventBroker.Instance.GetEvent(eventName);
  195. [return: NotNull]
  196. public EventBus.EventBroker.AnonymousEventData<T> GetEvent<T>([NotNull] string eventName) => (EventBroker.AnonymousEventData<T>)EventBroker.Instance.GetEvent<T>(eventName);
  197. public ICommand SaveCommand => new RelayCommand(()=>
  198. {
  199. if (!cansave) return;
  200. Save();
  201. cansave = false;
  202. });
  203. public ICommand CancelCommand => new RelayCommand(Cancel);
  204. public virtual string Title { get => title; set =>SetProperty(ref title , value); }
  205. public bool ButtonVisibily { get => buttonVisibily; set =>SetProperty(ref buttonVisibily , value); }
  206. protected virtual void Save()
  207. {
  208. lastModel = default;
  209. }
  210. [AllowNull]
  211. protected TModel lastModel;
  212. [AllowNull]
  213. private Type content;
  214. public virtual void InitData()
  215. {
  216. cansave = true;
  217. if (CanCancel)
  218. {
  219. lastModel = (TModel)Model.Clone();
  220. SaveIsEnabled = false;
  221. }
  222. else
  223. {
  224. //lastModel = default;
  225. }
  226. }
  227. protected virtual bool SkipProperty(string? propertyName)
  228. {
  229. return false;
  230. }
  231. protected override void OnPropertyChanged(PropertyChangedEventArgs e)
  232. {
  233. base.OnPropertyChanged(e);
  234. if (e.PropertyName == nameof(Content)
  235. || e.PropertyName == nameof(Model)
  236. || e.PropertyName == nameof(ButtonVisibily)
  237. || e.PropertyName == nameof(SaveCommand)
  238. || e.PropertyName == nameof(Title)
  239. || e.PropertyName == nameof(SaveIsEnabled)
  240. || e.PropertyName == nameof(CancelCommand))
  241. return;
  242. if (SkipProperty(e.PropertyName)) return;
  243. SaveIsEnabled = true;
  244. }
  245. private bool buttonVisibily = true;
  246. protected virtual void Cancel()
  247. {
  248. if (CanCancel && lastModel !=null)
  249. {
  250. UpDateModel(lastModel);
  251. lastModel = default;
  252. }
  253. }
  254. }