MobileDataGrid.xaml.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. using comal.timesheets.Data_Classes;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Linq;
  6. using System.Reflection;
  7. using Xamarin.Forms;
  8. using Xamarin.Forms.Xaml;
  9. namespace comal.timesheets
  10. {
  11. public enum DataGridSaveType
  12. {
  13. None,
  14. Single,
  15. Multiple
  16. }
  17. public delegate void DataGridOptionsSet(string title, DataGridSaveType savetype = DataGridSaveType.None);
  18. [XamlCompilation(XamlCompilationOptions.Compile)]
  19. public partial class MobileDataGrid : ContentView
  20. {
  21. public List<DataGridViewModelItem> Items { get; set; }
  22. DataGridSaveType SaveType { get; set; }
  23. public event DataGridOptionsSet OnOptionsSet;
  24. bool bSearching = false;
  25. ObservableCollection<DataGridFilter> Filters = new ObservableCollection<DataGridFilter>();
  26. List<DataGridViewModelItem> CurrentItems = new List<DataGridViewModelItem>();
  27. PropertyInfo[] info = typeof(DataGridViewModelItem).GetProperties();
  28. Dictionary<string, List<string>> FilterOptions = new Dictionary<string, List<string>>();
  29. Type Type;
  30. public MobileDataGrid()
  31. {
  32. InitializeComponent();
  33. Items = new List<DataGridViewModelItem>();
  34. Filters.CollectionChanged += Filters_CollectionChanged;
  35. }
  36. public void Setup(List<DataGridViewModelItem> items, Type type, DataGridSaveType savetype = DataGridSaveType.None)
  37. {
  38. Type = type;
  39. Items = items;
  40. Device.BeginInvokeOnMainThread(() =>
  41. {
  42. SetupHeadersAndDataTemplate(items.First());
  43. Refresh(Items);
  44. });
  45. OnOptionsSet?.Invoke(type.Name, savetype);
  46. SaveType = savetype;
  47. }
  48. /// <summary>
  49. /// Never pass CurrentItems into this function - create an intermediate list first
  50. /// </summary>
  51. /// <param name="items"></param>
  52. private void Refresh(List<DataGridViewModelItem> items)
  53. {
  54. itemsListView.ItemsSource = items;
  55. countLbl.Text = items.Count + " Records";
  56. CurrentItems.Clear();
  57. foreach (var item in items)
  58. {
  59. CurrentItems.Add(item);
  60. }
  61. if (Filters.Any())
  62. filterBtnRow.Height = 80;
  63. else
  64. filterBtnRow.Height = 0;
  65. int count = 0;
  66. foreach (var column in Items.First().Data)
  67. {
  68. var filter = Filters.FirstOrDefault(x => x.ColNumber == "Col" + count);
  69. if (filter != null)
  70. ChangeHeaderColour(true, count);
  71. else
  72. ChangeHeaderColour(false, count);
  73. count++;
  74. }
  75. }
  76. public void SetupHeadersAndDataTemplate(DataGridViewModelItem item)
  77. {
  78. GenerateHeaders(item);
  79. }
  80. public void GenerateHeaders(DataGridViewModelItem item)
  81. {
  82. int count = 0;
  83. foreach (var tuple in item.Data)
  84. {
  85. CreateNewHeader(tuple.Item1, count);
  86. CreateFilterOption(count);
  87. count++;
  88. }
  89. }
  90. private void CreateFilterOption(int column)
  91. {
  92. List<string> options = new List<string>();
  93. foreach (var property in info)
  94. {
  95. if (property.Name == "Col" + column)
  96. foreach (var item in Items)
  97. {
  98. string value = GetStringValue(property, item);
  99. if (!options.Contains(value) && !string.IsNullOrWhiteSpace(value))
  100. options.Add(value);
  101. }
  102. }
  103. options.Sort();
  104. FilterOptions.Add("Col" + column, options);
  105. }
  106. private void CreateNewHeader(string name, int count)
  107. {
  108. headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
  109. DataGridHeaderRow header = new DataGridHeaderRow { ColumnName = name, ColumnNumber = count };
  110. header.Setup();
  111. header.OnDataGridHeaderFilterTapped += Header_OnDataGridHeaderFilterTapped;
  112. header.OnDataGridHeaderTapped += Header_OnDataGridHeaderTapped;
  113. headerGrid.Children.Add(SetGridValues(header, 0, count));
  114. CreateSearchEntry(name, count);
  115. }
  116. private void CreateSearchEntry(string name, int count)
  117. {
  118. var searchEnt = new DataGridSearchEntry(name, count);
  119. searchEnt.OnDataGridSearchEntryChanged += SearchEnt_OnDataGridSearchEntryChanged;
  120. searchEnt.IsEnabled = name == "Image" ? false : true;
  121. headerGrid.Children.Add(SetGridValues(searchEnt, 1, count));
  122. }
  123. private View SetGridValues(View view, int row, int column)
  124. {
  125. view.SetValue(Grid.RowProperty, row);
  126. view.SetValue(Grid.ColumnProperty, column);
  127. return view;
  128. }
  129. #region Events
  130. private void Row_Tapped(object sender, EventArgs e)
  131. {
  132. var item = itemsListView.SelectedItem as DataGridViewModelItem;
  133. if (item != null && SaveType != DataGridSaveType.None)
  134. {
  135. switch (SaveType)
  136. {
  137. case DataGridSaveType.Single:
  138. AddSelectionToLists(item);
  139. UnselectOthers(item);
  140. break;
  141. case DataGridSaveType.Multiple:
  142. AddSelectionToLists(item);
  143. break;
  144. }
  145. }
  146. List<DataGridViewModelItem> list = new List<DataGridViewModelItem>();
  147. foreach (var i in CurrentItems)
  148. list.Add(i);
  149. Refresh(list);
  150. }
  151. private void UnselectOthers(DataGridViewModelItem item)
  152. {
  153. UnselectItems(item, Items);
  154. UnselectItems(item, CurrentItems);
  155. }
  156. private void UnselectItems(DataGridViewModelItem item, List<DataGridViewModelItem> selectedlist)
  157. {
  158. var list = selectedlist.Where(x => x.IsSelected == true);
  159. foreach (var foundItem in list)
  160. {
  161. if (foundItem.ID != item.ID)
  162. foundItem.IsSelected = false;
  163. }
  164. }
  165. private void AddSelectionToLists(DataGridViewModelItem item)
  166. {
  167. bool selected = item.IsSelected;
  168. AddSelectionToList(selected, Items, item.ID);
  169. AddSelectionToList(selected, CurrentItems, item.ID);
  170. }
  171. private void AddSelectionToList(bool selected, List<DataGridViewModelItem> list, Guid ID)
  172. {
  173. var foundItem = list.FirstOrDefault(x => x.ID == ID);
  174. if (foundItem != null)
  175. foundItem.IsSelected = selected ? false : true;
  176. }
  177. private void SearchEnt_OnDataGridSearchEntryChanged(int columnnumber, string value, string colname)
  178. {
  179. if (string.IsNullOrWhiteSpace(value))
  180. Filters.Remove(Filters.FirstOrDefault(x => x.ColNumber == "Col" + columnnumber && x.FilterType == FilterType.Typed));
  181. else
  182. {
  183. if (Filters.FirstOrDefault(x => x.ColNumber == "Col" + columnnumber) != null)
  184. Filters.Remove(Filters.FirstOrDefault(x => x.ColNumber == "Col" + columnnumber && x.FilterType == FilterType.Typed));
  185. Filters.Add(new DataGridFilter(colname, "Col" + columnnumber, value, FindNumber(columnnumber), FilterType.Typed));
  186. }
  187. }
  188. private void Filters_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  189. {
  190. if (bSearching)
  191. return;
  192. CheckFilterDisplay();
  193. bSearching = true;
  194. List<DataGridViewModelItem> finalList = new List<DataGridViewModelItem>();
  195. if (Filters.FirstOrDefault(x => x.FilterNumber == FilterNumber.Zero) != null)
  196. {
  197. var foundfilters = Filters.Where(x => x.FilterNumber == FilterNumber.Zero);
  198. foreach (var foundfilter in foundfilters)
  199. {
  200. foreach (DataGridViewModelItem item in RunSearch(Items, foundfilter.Value, "Col0"))
  201. finalList.Add(item);
  202. }
  203. }
  204. else
  205. {
  206. foreach (DataGridViewModelItem item in Items)
  207. finalList.Add(item);
  208. }
  209. if (Filters.FirstOrDefault(x => x.FilterNumber == FilterNumber.One) != null)
  210. {
  211. var foundfilters = Filters.Where(x => x.FilterNumber == FilterNumber.One);
  212. List<DataGridViewModelItem> intermediatelist = new List<DataGridViewModelItem>();
  213. foreach (var foundfilter in foundfilters)
  214. {
  215. foreach (DataGridViewModelItem item in RunSearch(finalList, foundfilter.Value, "Col1"))
  216. intermediatelist.Add(item);
  217. }
  218. finalList.Clear();
  219. foreach (DataGridViewModelItem item in intermediatelist)
  220. finalList.Add(item);
  221. }
  222. else if (finalList.Count == 0)
  223. {
  224. foreach (DataGridViewModelItem item in Items)
  225. finalList.Add(item);
  226. }
  227. if (Filters.FirstOrDefault(x => x.FilterNumber == FilterNumber.Two) != null)
  228. {
  229. var foundfilters = Filters.Where(x => x.FilterNumber == FilterNumber.Two);
  230. List<DataGridViewModelItem> intermediatelist = new List<DataGridViewModelItem>();
  231. foreach (var foundfilter in foundfilters)
  232. {
  233. foreach (DataGridViewModelItem item in RunSearch(finalList, foundfilter.Value, "Col2"))
  234. intermediatelist.Add(item);
  235. }
  236. finalList.Clear();
  237. foreach (DataGridViewModelItem item in intermediatelist)
  238. finalList.Add(item);
  239. }
  240. else if (finalList.Count == 0)
  241. {
  242. foreach (DataGridViewModelItem item in Items)
  243. finalList.Add(item);
  244. }
  245. if (Filters.FirstOrDefault(x => x.FilterNumber == FilterNumber.Three) != null)
  246. {
  247. var foundfilters = Filters.Where(x => x.FilterNumber == FilterNumber.Three);
  248. List<DataGridViewModelItem> intermediatelist = new List<DataGridViewModelItem>();
  249. foreach (var foundfilter in foundfilters)
  250. {
  251. foreach (DataGridViewModelItem item in RunSearch(finalList, foundfilter.Value, "Col3"))
  252. intermediatelist.Add(item);
  253. }
  254. finalList.Clear();
  255. foreach (DataGridViewModelItem item in intermediatelist)
  256. finalList.Add(item);
  257. }
  258. if (finalList.Count == 0 && Filters.Count == 0)
  259. {
  260. foreach (DataGridViewModelItem item in Items)
  261. finalList.Add(item);
  262. }
  263. else if (finalList.Count == Items.Count && Filters.Count > 0)
  264. finalList.Clear();
  265. Refresh(finalList);
  266. bSearching = false;
  267. }
  268. private void CheckFilterDisplay()
  269. {
  270. filterLayout.Children.Clear();
  271. foreach (var filter in Filters)
  272. {
  273. DataGridFilterView filterview = new DataGridFilterView(filter.DisplayValue, filter.FilterType, filter.ColNumber);
  274. filterview.OnFilterClosed += Filterview_OnFilterClosed;
  275. filterLayout.Children.Add(filterview);
  276. }
  277. }
  278. private void Filterview_OnFilterClosed(string value, FilterType type, string colnumber)
  279. {
  280. List<DataGridFilter> toRemove = new List<DataGridFilter>();
  281. foreach (var child in filterLayout.Children)
  282. {
  283. if (child.GetType() == typeof(DataGridFilterView))
  284. {
  285. if ((child as DataGridFilterView).Value == value && (child as DataGridFilterView).FilterType == type)
  286. {
  287. var filter = Filters.FirstOrDefault(x => x.DisplayValue == value && x.FilterType == type);
  288. if (filter != null)
  289. toRemove.Add(filter);
  290. }
  291. }
  292. }
  293. if (toRemove.First().FilterType == FilterType.Typed)
  294. {
  295. foreach (var child in headerGrid.Children)
  296. {
  297. if (child.GetType() == typeof(DataGridSearchEntry))
  298. {
  299. if ((child as DataGridSearchEntry).ColumnNumber == int.Parse(colnumber.Substring(3, 1)))
  300. (child as DataGridSearchEntry).Text = "";
  301. }
  302. }
  303. }
  304. Filters.Remove(toRemove.First());
  305. }
  306. private void Header_OnDataGridHeaderTapped(int columnnumber, SearchUtils.SortDirection sortdirection)
  307. {
  308. var intermediatelist = new List<DataGridViewModelItem>();
  309. foreach (var property in info)
  310. {
  311. if (property.Name == "Col" + columnnumber)
  312. {
  313. foreach (var item in SearchUtils.OrderByPropertyName(CurrentItems, "Col" + columnnumber, sortdirection))
  314. {
  315. intermediatelist.Add(item);
  316. }
  317. Refresh(intermediatelist);
  318. }
  319. }
  320. }
  321. private void Header_OnDataGridHeaderFilterTapped(int columnnumber, string columnname)
  322. {
  323. List<string> options = FilterOptions["Col" + columnnumber];
  324. List<DataGridFilter> filters = new List<DataGridFilter>();
  325. var selectedFilters = Filters.Where(x => x.FilterType == FilterType.Selected && x.ColNumber == "Col" + columnnumber);
  326. foreach (var list in selectedFilters)
  327. {
  328. filters.Add(list);
  329. }
  330. MultiSelectPage filterpage = new MultiSelectPage(options, filters, columnnumber, columnname);
  331. filterpage.OnPageSaved += FilterPage_OnPageSaved;
  332. Navigation.PushAsync(filterpage);
  333. }
  334. private void FilterPage_OnPageSaved(IEnumerable<MultiSelectPageViewItem> items, int columnnumber, string columnname)
  335. {
  336. var intermediateList = new List<DataGridFilter>();
  337. var list = Filters.Where(x => x.ColNumber == "Col" + columnnumber && x.FilterType == FilterType.Selected);
  338. foreach (var item in list)
  339. {
  340. intermediateList.Add(item);
  341. }
  342. foreach (var filter in intermediateList)
  343. Filters.Remove(filter);
  344. foreach (var item in items)
  345. Filters.Add(new DataGridFilter(columnname, "Col" + columnnumber, item.Value, FindNumber(columnnumber), FilterType.Selected));
  346. var newlist = Filters.Where(x => x.ColNumber == "Col" + columnnumber);
  347. }
  348. #endregion
  349. private IEnumerable<DataGridViewModelItem> RunSearch(IEnumerable<DataGridViewModelItem> list, string value, string propertyname)
  350. {
  351. var intermediatelist = new List<DataGridViewModelItem>();
  352. foreach (var property in info)
  353. {
  354. if (property.Name == propertyname)
  355. {
  356. foreach (var item in list)
  357. {
  358. if (GetStringValue(property, item).Contains(value)
  359. || GetStringValue(property, item).Contains(value.ToUpper())
  360. || GetStringValue(property, item).Contains(value.ToLower())
  361. || GetStringValue(property, item).Contains(SearchUtils.LowerCaseFirst(value))
  362. || GetStringValue(property, item).Contains(SearchUtils.UpperCaseFirst(value))
  363. || GetStringValue(property, item).Contains(SearchUtils.UpperCaseSecond(value))
  364. )
  365. {
  366. if (!intermediatelist.Contains(item))
  367. intermediatelist.Add(item);
  368. }
  369. }
  370. }
  371. }
  372. return intermediatelist;
  373. }
  374. private void ChangeHeaderColour(bool filterpresent, int colnumber)
  375. {
  376. foreach (var header in headerGrid.Children)
  377. {
  378. if (header.GetType() == typeof(DataGridHeaderRow))
  379. {
  380. if ((header as DataGridHeaderRow).ColumnNumber == colnumber)
  381. (header as DataGridHeaderRow).ChangeFilterColour(filterpresent);
  382. }
  383. }
  384. }
  385. private string GetStringValue(PropertyInfo property, object item)
  386. {
  387. if (property.PropertyType == typeof(string))
  388. return (string)property.GetValue(item);
  389. else
  390. return "";
  391. }
  392. private FilterNumber FindNumber(int columnnumber)
  393. {
  394. switch (columnnumber)
  395. {
  396. case 0:
  397. return FilterNumber.Zero;
  398. case 1:
  399. return FilterNumber.One;
  400. case 2:
  401. return FilterNumber.Two;
  402. case 3:
  403. return FilterNumber.Three;
  404. default:
  405. return FilterNumber.Zero;
  406. }
  407. }
  408. }
  409. public enum FilterNumber
  410. {
  411. Zero,
  412. One,
  413. Two,
  414. Three
  415. }
  416. public enum FilterType
  417. {
  418. Typed,
  419. Selected
  420. }
  421. public class DataGridFilter
  422. {
  423. public string ColNumber { get; set; }
  424. public string ColName { get; set; }
  425. public string Value { get; set; }
  426. public FilterNumber FilterNumber { get; set; }
  427. public FilterType FilterType { get; set; }
  428. public string DisplayValue
  429. {
  430. get => ColName + " = " + Value;
  431. }
  432. public DataGridFilter(string colname, string colNumber, string value, FilterNumber filterNumber, FilterType filterType)
  433. {
  434. ColName = colname;
  435. ColNumber = colNumber;
  436. Value = value;
  437. FilterNumber = filterNumber;
  438. FilterType = filterType;
  439. }
  440. }
  441. public class DataGridViewModelItem
  442. {
  443. public Guid ID { get; set; }
  444. private bool isSelected;
  445. public bool IsSelected
  446. {
  447. get
  448. {
  449. return isSelected;
  450. }
  451. set
  452. {
  453. isSelected = value;
  454. Color = isSelected == true ? Color.FromHex("#91a3b0") /*Cadet Gray*/ : Color.Default;
  455. }
  456. }
  457. public Color Color { get; set; }
  458. public List<Tuple<string, string>> Data { get; set; }
  459. public string Col0 { get; set; }
  460. public string Col1 { get; set; }
  461. public string Col2 { get; set; }
  462. public string Col3 { get; set; }
  463. public Image Image { get; set; }
  464. public ImageSource Source
  465. {
  466. get
  467. {
  468. return Image.Source;
  469. }
  470. }
  471. public GridLength ColWidth0 { get; set; }
  472. public GridLength ColWidth1 { get; set; }
  473. public GridLength ColWidth2 { get; set; }
  474. public GridLength ColWidth3 { get; set; }
  475. public DataGridViewModelItem(Guid id, List<Tuple<string, string>> data, Image image = null)
  476. {
  477. ID = id;
  478. Data = data;
  479. Image = image;
  480. Col0 = data.Count > 0 ? data[0].Item2 : "";
  481. Col1 = data.Count > 1 ? data[1].Item2 : "";
  482. Col2 = data.Count > 2 ? data[2].Item2 : "";
  483. Col3 = data.Count > 3 ? data[3].Item2 : "";
  484. ColWidth0 = data.Count > 0 ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Absolute);
  485. ColWidth1 = data.Count > 1 ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Absolute);
  486. ColWidth2 = data.Count > 2 ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Absolute);
  487. ColWidth3 = data.Count > 3 ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Absolute);
  488. IsSelected = false;
  489. }
  490. }
  491. }