TasksByUserControl.xaml.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Runtime.CompilerServices;
  9. using System.Windows;
  10. using System.Windows.Controls;
  11. using System.Windows.Data;
  12. using System.Windows.Input;
  13. using System.Windows.Media.Imaging;
  14. using Comal.Classes;
  15. using InABox.Clients;
  16. using InABox.Core;
  17. using InABox.DynamicGrid;
  18. using InABox.WPF;
  19. using Syncfusion.UI.Xaml.Kanban;
  20. using Syncfusion.Windows.Tools.Controls;
  21. namespace PRSDesktop;
  22. public class TasksByUserEmployeeHeader
  23. {
  24. public Guid EmployeeID { get; set; }
  25. public string Name { get; set; }
  26. public BitmapImage Image { get; set; }
  27. public int NumTasks { get => Tasks.Count(); }
  28. public double NumHours { get => Tasks.Sum(x => x.EstimatedTime.TotalHours); }
  29. public IEnumerable<TaskModel> Tasks => Model.Categories
  30. .SelectMany(x => x.EmployeeCategoryDictionary.GetValueOrDefault(EmployeeID)?.Tasks ?? Enumerable.Empty<TaskModel>());
  31. private TasksByUserModel Model;
  32. public TasksByUserEmployeeHeader(Guid employeeID, string name, BitmapImage image, TasksByUserModel model)
  33. {
  34. EmployeeID = employeeID;
  35. Name = name;
  36. Image = image;
  37. Model = model;
  38. }
  39. }
  40. public class TasksByUserEmployeeCategory
  41. {
  42. public Guid EmployeeID { get; set; }
  43. public List<TaskModel> Tasks { get; set; } = new();
  44. public TasksByUserEmployeeCategory(Guid employeeID)
  45. {
  46. EmployeeID = employeeID;
  47. }
  48. }
  49. public class TasksByUserCategory
  50. {
  51. public string Category { get; set; }
  52. public IEnumerable<TasksByUserEmployeeCategory> EmployeeCategories => EmployeeCategoryDictionary.Values;
  53. public Dictionary<Guid, TasksByUserEmployeeCategory> EmployeeCategoryDictionary { get; set; } = new();
  54. public TasksByUserCategory(string category)
  55. {
  56. Category = category;
  57. }
  58. }
  59. public class TasksByUserModel
  60. {
  61. public List<TasksByUserEmployeeHeader> SectionHeaders { get; set; } = new();
  62. public List<TasksByUserCategory> Categories { get; set; } = new();
  63. }
  64. public partial class TasksByUserControl : UserControl, INotifyPropertyChanged, ITaskControl
  65. {
  66. private static readonly BitmapImage anonymous = PRSDesktop.Resources.anonymous.AsBitmapImage();
  67. public TasksByUserModel Model { get; set; }
  68. private ILookup<Guid, Guid> TeamEmployees;
  69. private Dictionary<Guid, EmployeeModel> Employees;
  70. private KanbanViewMode _mode;
  71. private bool bPopulating;
  72. public KanbanViewMode Mode
  73. {
  74. get => _mode;
  75. set
  76. {
  77. _mode = value;
  78. OnPropertyChanged();
  79. }
  80. }
  81. public TasksByUserControl()
  82. {
  83. InitializeComponent();
  84. }
  85. #region INotifyPropertyChanged
  86. public event PropertyChangedEventHandler? PropertyChanged;
  87. // Create the OnPropertyChanged method to raise the event
  88. // The calling member's name will be used as the parameter.
  89. protected void OnPropertyChanged([CallerMemberName] string? name = null)
  90. {
  91. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
  92. }
  93. #endregion
  94. #region Setup
  95. private void LoadEmployees()
  96. {
  97. var empfilter = LookupFactory.DefineFilter<Employee>();
  98. var results = Client.QueryMultiple(
  99. new KeyedQueryDef<Employee>(
  100. LookupFactory.DefineFilter<Employee>(),
  101. new Columns<Employee>(x => x.ID)
  102. .Add(x => x.Name)
  103. .Add(x => x.Thumbnail.ID),
  104. new SortOrder<Employee>(x => x.Name)),
  105. new KeyedQueryDef<Team>(
  106. LookupFactory.DefineFilter<Team>(),
  107. new Columns<Team>(x => x.ID)
  108. .Add(x => x.Name),
  109. new SortOrder<Team>(x => x.Name)),
  110. new KeyedQueryDef<EmployeeTeam>(
  111. LookupFactory.DefineFilter<EmployeeTeam>(),
  112. new Columns<EmployeeTeam>(x => x.EmployeeLink.ID)
  113. .Add(x => x.TeamLink.ID)));
  114. TeamEmployees = results.Get<EmployeeTeam>().ToLookup<EmployeeTeam, Guid, Guid>(x => x.TeamLink.ID, x => x.EmployeeLink.ID);
  115. Employees = results.GetObjects<Employee>().ToDictionary(
  116. x => x.ID,
  117. x => new EmployeeModel(x.ID, x.Name, x.Thumbnail.ID, null));
  118. var teams = results.GetObjects<Team>().ToDictionary(x => x.ID, x => x.Name);
  119. SelectedTeams.ItemsSource = teams;
  120. foreach (var team in Host.KanbanSettings.UserSettings.SelectedTeams)
  121. {
  122. SelectedTeams.SelectedItems.Add(teams.Where(x => x.Key == team));
  123. }
  124. }
  125. private void PopulateEmployees()
  126. {
  127. bPopulating = true;
  128. try
  129. {
  130. var availableemployees = new List<Guid>();
  131. foreach (var team in SelectedTeams.SelectedItems.Select(v => (KeyValuePair<Guid, string>)v))
  132. availableemployees.AddRange(TeamEmployees[team.Key].Where(x => !availableemployees.Contains(x)));
  133. SelectedEmployees.ItemsSource = Employees.Where(x => availableemployees.Contains(x.Key));
  134. SelectedEmployees.SelectedItems.Clear();
  135. foreach (var employee in Host.KanbanSettings.UserSettings.SelectedEmployees.Where(x => availableemployees.Contains(x)))
  136. SelectedEmployees.SelectedItems.Add(Employees.FirstOrDefault(x => Equals(x.Key, employee)));
  137. }
  138. catch (Exception e)
  139. {
  140. }
  141. bPopulating = false;
  142. }
  143. private void SetupToolbar()
  144. {
  145. IncludeCompleted.Visibility = Security.IsAllowed<CanHideTaskCompletedColumn>() ? Visibility.Visible : Visibility.Collapsed;
  146. IncludeCompleted.IsChecked = IncludeCompleted.Visibility == Visibility.Visible ? Host.KanbanSettings.UserSettings.IncludeCompleted : true;
  147. IncludeObserved.IsChecked = Host.KanbanSettings.UserSettings.IncludeObserved;
  148. IncludeManaged.IsChecked = Host.KanbanSettings.UserSettings.IncludeManaged;
  149. ViewType.SelectedIndex = Host.KanbanSettings.UserSettings.CompactView ? 1 : 0;
  150. }
  151. private void PopulateKanbanTypes()
  152. {
  153. TaskType.Items.Add("");
  154. foreach (var kanbanType in Host.KanbanTypes)
  155. {
  156. TaskType.Items.Add(kanbanType);
  157. }
  158. }
  159. public void Setup()
  160. {
  161. SetupToolbar();
  162. SplitPanel.AnchorWidth = Host.KanbanSettings.UserSettings.AnchorWidth;
  163. TeamsRow.Height = new GridLength(Host.KanbanSettings.UserSettings.TeamsHeight);
  164. LoadEmployees();
  165. PopulateEmployees();
  166. Mode = Host.KanbanSettings.StatusSettings.CompactView ? KanbanViewMode.Compact : KanbanViewMode.Full;
  167. PopulateKanbanTypes();
  168. }
  169. #endregion
  170. #region Refresh
  171. private Filter<KanbanSubscriber> GetKanbanSubscriberFilter()
  172. {
  173. var filter = new Filter<KanbanSubscriber>(c => c.Kanban.Closed).IsEqualTo(DateTime.MinValue)
  174. .And(x => x.Kanban.Locked).IsEqualTo(false);
  175. var privateFilter = new Filter<KanbanSubscriber>(x => x.Kanban.Private).IsEqualTo(false);
  176. if (App.EmployeeID != Guid.Empty)
  177. {
  178. privateFilter = privateFilter.Or(x => x.Employee.ID).IsEqualTo(App.EmployeeID);
  179. }
  180. filter.And(privateFilter);
  181. if (Host.Job != null)
  182. {
  183. if (Host.Job.ID != Guid.Empty)
  184. filter = filter.And(c => c.Kanban.JobLink.ID).IsEqualTo(Host.Job.ID);
  185. else
  186. filter = filter.And(c => c.Kanban.JobLink.ID).None();
  187. }
  188. if (!Host.KanbanSettings.UserSettings.IncludeCompleted)
  189. filter = filter.And(new Filter<KanbanSubscriber>(x => x.Kanban.Completed).IsEqualTo(DateTime.MinValue));
  190. var emps = Employees.Where(x => Host.KanbanSettings.UserSettings.SelectedEmployees.Contains(x.Key));
  191. filter = filter.And(c => c.Employee.ID).InList(emps.Select(x => x.Key).ToArray());
  192. if (!Host.KanbanSettings.UserSettings.IncludeObserved)
  193. {
  194. if (Host.KanbanSettings.UserSettings.IncludeManaged)
  195. filter = filter.And(new Filter<KanbanSubscriber>(x => x.Manager).IsEqualTo(true).Or(x => x.Assignee).IsEqualTo(true));
  196. else
  197. filter = filter.And(x => x.Assignee).IsEqualTo(true);
  198. }
  199. return filter;
  200. }
  201. private void ReloadColumns()
  202. {
  203. Model.SectionHeaders.Clear();
  204. foreach (var employeeID in Host.KanbanSettings.UserSettings.SelectedEmployees)
  205. {
  206. if (Employees.TryGetValue(employeeID, out var employee))
  207. {
  208. Model.SectionHeaders.Add(new TasksByUserEmployeeHeader(employeeID, employee.Name, employee.Image ?? anonymous, Model));
  209. }
  210. }
  211. }
  212. public void Refresh()
  213. {
  214. var categoryOrder = new Dictionary<string, int>
  215. {
  216. { Kanban.OPEN, 0 },
  217. { Kanban.INPROGRESS, 1 },
  218. { Kanban.WAITING, 2 },
  219. { Kanban.COMPLETE, 3 }
  220. };
  221. var filter = GetKanbanSubscriberFilter();
  222. using (new WaitCursor())
  223. {
  224. var kanbans = new Client<KanbanSubscriber>().Query(
  225. filter,
  226. new Columns<KanbanSubscriber>
  227. (
  228. x => x.Kanban.ID,
  229. x => x.Kanban.DueDate,
  230. x => x.Kanban.Completed,
  231. //x => x.Kanban.Description,
  232. x => x.Kanban.Summary,
  233. x => x.Kanban.Category,
  234. x => x.Kanban.EmployeeLink.ID,
  235. x => x.Kanban.EmployeeLink.Name,
  236. x => x.Kanban.ManagerLink.ID,
  237. x => x.Kanban.ManagerLink.Name,
  238. x => x.Kanban.Notes,
  239. x => x.Kanban.Title,
  240. x => x.Kanban.JobLink.ID,
  241. x => x.Kanban.JobLink.JobNumber,
  242. x => x.Kanban.JobLink.Name,
  243. x => x.Kanban.Type.ID,
  244. x => x.Kanban.Type.Code,
  245. x => x.Kanban.Number,
  246. x => x.Kanban.Attachments,
  247. x => x.Kanban.Locked,
  248. x => x.Employee.ID,
  249. x => x.Kanban.EstimatedTime
  250. ),
  251. new SortOrder<KanbanSubscriber>(x => x.Kanban.DueDate) { Direction = SortDirection.Ascending }
  252. );
  253. var models = CreateModels(kanbans.ToObjects<KanbanSubscriber>()).ToList();
  254. ReloadColumns();
  255. AllTasks = models.OrderBy(x => categoryOrder[x.Category]).ThenBy(x => x.DueDate).ToList();
  256. FilterKanbans();
  257. }
  258. }
  259. private void FilterKanbans()
  260. {
  261. var categoryOrder = new Dictionary<string, int>
  262. {
  263. { Kanban.OPEN, 0 },
  264. { Kanban.INPROGRESS, 1 },
  265. { Kanban.WAITING, 2 },
  266. { Kanban.COMPLETE, 3 }
  267. };
  268. IEnumerable<TaskModel> filtered = AllTasks;
  269. if (TaskType.SelectedItem is KanbanType kanbanType)
  270. {
  271. filtered = filtered.Where(x => x.Type.ID == kanbanType.ID);
  272. }
  273. if (!string.IsNullOrWhiteSpace(Search.Text))
  274. {
  275. var searches = Search.Text.Split();
  276. filtered = filtered.Where(x => x.Search(searches));
  277. }
  278. var categories = new Dictionary<string, TasksByUserCategory>();
  279. foreach (var task in filtered)
  280. {
  281. if(!categories.TryGetValue(task.Category, out var category))
  282. {
  283. category = new TasksByUserCategory(task.Category);
  284. categories.Add(task.Category, category);
  285. }
  286. if(!category.EmployeeCategoryDictionary.TryGetValue(task.EmployeeCategory, out var employeeCategory))
  287. {
  288. employeeCategory = new TasksByUserEmployeeCategory(task.EmployeeCategory);
  289. category.EmployeeCategoryDictionary.Add(task.EmployeeCategory, employeeCategory);
  290. }
  291. employeeCategory.Tasks.Add(task);
  292. if (task.Checked)
  293. {
  294. SelectedTasks.Add(task);
  295. }
  296. }
  297. Model.Categories.AddRange(categories.Values.OrderBy(x => categoryOrder[x.Category]));
  298. }
  299. private IEnumerable<TaskModel> CreateModels(IEnumerable<KanbanSubscriber> subscribers)
  300. {
  301. foreach(var subscriber in subscribers)
  302. {
  303. var kanban = subscriber.Kanban;
  304. var empValid = Entity.IsEntityLinkValid<KanbanSubscriber, EmployeeLink>(x => x.Kanban.EmployeeLink, row);
  305. var mgrValid = Entity.IsEntityLinkValid<KanbanSubscriber, EmployeeLink>(x => x.Kanban.ManagerLink, row);
  306. var completed = row.Get<KanbanSubscriber, DateTime>(e => e.Kanban.Completed);
  307. var locked = row.Get<KanbanSubscriber, bool>(e => e.Kanban.Locked);
  308. var typeID = row.Get<KanbanSubscriber, Guid>(e => e.Kanban.Type.ID);
  309. var typeCode = row.Get<KanbanSubscriber, string>(e => e.Kanban.Type.Code);
  310. var job = row.Get<KanbanSubscriber, string>(x => x.Kanban.JobLink.JobNumber);
  311. var model = new TaskModel();
  312. model.Title = kanban.Title;
  313. model.ID = kanban.ID;
  314. model.Description = kanban.Summary ?? "";
  315. model.EmployeeCategory = subscriber.Employee.ID;
  316. model.Category = kanban.Category;
  317. if (model.Category.IsNullOrWhiteSpace())
  318. model.Category = "Open";
  319. var colour = subscriber.Employee.ID == kanban.EmployeeLink.ID
  320. ? TaskModel.KanbanColor(
  321. kanban.DueDate,
  322. kanban.Completed)
  323. : subscriber.Employee.ID == kanban.ManagerLink.ID
  324. ? Color.Silver
  325. : Color.Plum;
  326. if (kanban.Locked)
  327. {
  328. colour = colour.MixColors(0.5F, Color.White);
  329. }
  330. model.Color = System.Windows.Media.Color.FromArgb(colour.A, colour.R, colour.G, colour.B);
  331. model.Attachments = kanban.Attachments > 0;
  332. model.DueDate = kanban.DueDate;
  333. model.CompletedDate = kanban.Completed;
  334. model.Locked = kanban.Locked;
  335. model.EstimatedTime = kanban.EstimatedTime;
  336. var notes = new List<List<string>> { new() };
  337. var kanbanNotes = kanban.Notes;
  338. if (kanbanNotes != null)
  339. {
  340. foreach (var line in kanbanNotes)
  341. {
  342. if (line == "===================================")
  343. {
  344. notes.Add(new());
  345. }
  346. else
  347. {
  348. notes.Last().Add(line);
  349. }
  350. }
  351. }
  352. model.Notes = string.Join("\n===================================\n", notes.Reverse<List<string>>().Select(x => string.Join('\n', x)));
  353. model.EmployeeID = kanban.EmployeeLink.ID;
  354. model.ManagerID = kanban.ManagerLink.ID;
  355. var employeeString = kanban.EmployeeLink.ID == subscriber.Employee.ID
  356. ? ""
  357. : kanban.EmployeeLink.ID == Guid.Empty
  358. ? " to (Unallocated)"
  359. : " to " + kanban.EmployeeLink.Name;
  360. var managerString = kanban.ManagerLink.ID == Guid.Empty || kanban.ManagerLink.ID == subscriber.Employee.ID
  361. ? ""
  362. : " by " + kanban.ManagerLink.Name;
  363. model.AssignedTo = !string.IsNullOrEmpty(employeeString) || !managerString.IsNullOrWhiteSpace()
  364. ? $"Assigned{employeeString}{managerString}"
  365. : "";
  366. model.JobID = kanban.JobLink.ID;
  367. model.JobNumber = kanban.JobLink.JobNumber?.Trim() ?? "";
  368. model.JobName = kanban.JobLink.Name;
  369. model.Checked = SelectedTasks.Any(x => x.ID == model.ID);
  370. model.Type = new KanbanType
  371. {
  372. ID = typeID,
  373. Code = typeCode
  374. };
  375. model.Number = kanban.Number;
  376. yield return model;
  377. }
  378. }
  379. #endregion
  380. #region Kanban
  381. private readonly List<TaskModel> SelectedTasks = new();
  382. private List<TaskModel> AllTasks { get; set; } = new();
  383. private void DoEdit(TaskModel task)
  384. {
  385. var result = Host.EditReferences(new[] { task });
  386. if (result)
  387. {
  388. Refresh();
  389. }
  390. }
  391. private void EditTask_Executed(object sender, ExecutedRoutedEventArgs e)
  392. {
  393. if (e.Parameter is not TaskModel model) return;
  394. DoEdit(model);
  395. }
  396. private void OpenTaskMenu_Executed(object sender, ExecutedRoutedEventArgs e)
  397. {
  398. if (e.Parameter is not KanbanResources.OpenTaskMenuCommandArgs args) return;
  399. Host.PopulateMenu(this, args.Model, args.Menu);
  400. }
  401. private void SelectTask_Executed(object sender, ExecutedRoutedEventArgs e)
  402. {
  403. if (e.Parameter is not TaskModel model) return;
  404. if (!SelectedTasks.Remove(model))
  405. {
  406. SelectedTasks.Add(model);
  407. }
  408. }
  409. private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  410. {
  411. e.CanExecute = true;
  412. }
  413. #endregion
  414. #region ITaskControl
  415. public ITaskHost Host { get; set; }
  416. public KanbanViewType KanbanViewType => KanbanViewType.User;
  417. public bool IsReady { get; set; }
  418. public string SectionName => "Tasks By User";
  419. public DataModel DataModel(Selection selection)
  420. {
  421. var ids = SelectedModels().Select(x => x.ID).ToArray();
  422. return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).InList(ids));
  423. }
  424. public IEnumerable<TaskModel> SelectedModels(TaskModel? sender = null)
  425. {
  426. if (sender is null)
  427. {
  428. return SelectedTasks;
  429. }
  430. else
  431. {
  432. var result = SelectedTasks.ToList();
  433. if (!result.Contains(sender))
  434. {
  435. result.Add(sender);
  436. }
  437. return result;
  438. }
  439. }
  440. #endregion
  441. #region Settings
  442. private void SaveSettings()
  443. {
  444. Host.KanbanSettings.UserSettings.AnchorWidth = SplitPanel.AnchorWidth;
  445. Host.KanbanSettings.UserSettings.TeamsHeight = SelectedTeams.ActualHeight;
  446. var teams = SelectedTeams.SelectedItems.Select(x => ((KeyValuePair<Guid, string>)x).Key);
  447. Host.KanbanSettings.UserSettings.SelectedTeams = teams.ToArray();
  448. var emps = SelectedEmployees.SelectedItems.Select(x => ((KeyValuePair<Guid, string>)x).Key);
  449. emps = emps.Where(e => TeamEmployees.Any(t => t.Contains(e)));
  450. Host.KanbanSettings.UserSettings.SelectedEmployees = emps.ToArray();
  451. Host.KanbanSettings.UserSettings.IncludeCompleted = IncludeCompleted.IsChecked == true;
  452. Host.KanbanSettings.UserSettings.IncludeObserved = IncludeObserved.IsChecked == true;
  453. Host.KanbanSettings.UserSettings.IncludeManaged = IncludeManaged.IsChecked == true;
  454. Host.SaveSettings();
  455. }
  456. private void SplitPanel_OnChanged(object sender, DynamicSplitPanelSettings e)
  457. {
  458. if (!IsReady || Equals(Host.KanbanSettings.UserSettings.AnchorWidth, e.AnchorWidth))
  459. return;
  460. SaveSettings();
  461. }
  462. private void SelectedTeams_ItemChecked(object sender, ItemCheckedEventArgs e)
  463. {
  464. if (!IsReady)
  465. return;
  466. PopulateEmployees();
  467. SaveSettings();
  468. Refresh();
  469. }
  470. private void SelectedTeams_SizeChanged(object sender, SizeChangedEventArgs e)
  471. {
  472. if (!IsReady || Equals(Host.KanbanSettings.UserSettings.TeamsHeight, SelectedTeams.ActualHeight))
  473. return;
  474. SaveSettings();
  475. }
  476. private void EmployeesSelectionChanged(object sender, SelectionChangedEventArgs e)
  477. {
  478. if (!IsReady || bPopulating)
  479. return;
  480. SaveSettings();
  481. Refresh();
  482. }
  483. #endregion
  484. private void Export_Click(object sender, RoutedEventArgs e)
  485. {
  486. }
  487. private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
  488. {
  489. }
  490. private void IncludeObserved_Checked(object sender, RoutedEventArgs e)
  491. {
  492. }
  493. private void IncludeManaged_Checked(object sender, RoutedEventArgs e)
  494. {
  495. }
  496. private void ViewType_SelectionChanged(object sender, SelectionChangedEventArgs e)
  497. {
  498. }
  499. private void TaskType_SelectionChanged(object sender, SelectionChangedEventArgs e)
  500. {
  501. }
  502. private void Search_KeyUp(object sender, KeyEventArgs e)
  503. {
  504. }
  505. private void ItemsControl_DragOver(object sender, DragEventArgs e)
  506. {
  507. }
  508. private void ItemsControl_Drop(object sender, DragEventArgs e)
  509. {
  510. }
  511. }