From 904f04df0e001b1ef6067180ab7c99dc4d3a2940 Mon Sep 17 00:00:00 2001 From: Fabian R Date: Sat, 23 May 2020 20:30:46 -0500 Subject: [PATCH 1/4] Removed redundant properties on MenuBar View -- Redundant members isMenuClosed and MenuOpen were replaced with a more consistent and intuitive single IsMenuOpen property. - CloseMenu method is now Public. - StartMenu has been renamed to OpenMenu and made Public. + Added missing XmlDoc to OpenMenu and CloseMenu, and fixed an small typo to MenuOpen/IsMenuOpen This commit breaks compatibility with previous versions. Changes required to host apps: MenuBar.MenuOpen property renamed to IsMenuOpen MenuBar.StartMenu method renamed to OpenMenu, use it to open the Menu Use MenuBar.CloseMenu to close the active menu. --- Terminal.Gui/Views/Menu.cs | 61 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 0a920a8ed..7795cd0a4 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -320,7 +320,7 @@ namespace Terminal.Gui { public override void PositionCursor () { - if (host == null || !host.isMenuClosed) + if (host == null || host.IsMenuOpen) if (barItems.IsTopLevel) { host.PositionCursor (); } else @@ -403,11 +403,11 @@ namespace Terminal.Gui { var item = barItems.Children [current]; if (item == null || !item.IsEnabled ()) disabled = true; if (host.UseKeysUpDownAsKeysLeftRight && barItems.Children [current]?.SubMenu != null && - !disabled && !host.isMenuClosed) { + !disabled && host.IsMenuOpen) { CheckSubMenu (); break; } - if (host.isMenuClosed) + if (!host.IsMenuOpen) host.OpenMenu (host.selected); } while (barItems.Children [current] == null || disabled); SetNeedsDisplay (); @@ -563,7 +563,7 @@ namespace Terminal.Gui { selectedSub = -1; ColorScheme = Colors.Menu; WantMousePositionReports = true; - isMenuClosed = true; + IsMenuOpen = false; } bool openedByAltKey; @@ -584,14 +584,14 @@ namespace Terminal.Gui { { if (keyEvent.IsAlt) { // User pressed Alt - this may be a precursor to a menu accelerator (e.g. Alt-F) - if (!keyEvent.IsCtrl && openedByAltKey && isMenuClosed && openMenu == null && ((uint)keyEvent.Key & (uint)Key.CharMask) == 0) { + if (!keyEvent.IsCtrl && openedByAltKey && !IsMenuOpen && openMenu == null && ((uint)keyEvent.Key & (uint)Key.CharMask) == 0) { // There's no open menu, the first menu item should be highlight. // The right way to do this is to SetFocus(MenuBar), but for some reason // that faults. //Activate (0); //StartMenu (); - isMenuClosed = false; + IsMenuOpen = true; selected = 0; CanFocus = true; lastFocused = SuperView.MostFocused; @@ -606,7 +606,7 @@ namespace Terminal.Gui { if (openMenu != null) CloseAllMenus (); openedByAltKey = false; - isMenuClosed = true; + IsMenuOpen = false; selected = -1; CanFocus = false; if (lastFocused != null) @@ -658,13 +658,13 @@ namespace Terminal.Gui { for (int i = 0; i < Menus.Length; i++) { if (i == selected) { pos++; - if (!isMenuClosed) + if (IsMenuOpen) Move (pos + 1, 0); else Move (pos + 1, 0); return; } else { - if (!isMenuClosed) + if (IsMenuOpen) pos += 1 + Menus [i].TitleLength + 2; else pos += 2 + Menus [i].TitleLength + 1; @@ -695,12 +695,11 @@ namespace Terminal.Gui { View previousFocused; internal bool isMenuOpening; internal bool isMenuClosing; - internal bool isMenuClosed; /// - /// True of the menu is open; otherwise false. + /// True if the menu is open; otherwise false. /// - public bool MenuOpen { get; set; } + public bool IsMenuOpen { get; protected set; } View lastFocused; @@ -748,12 +747,13 @@ namespace Terminal.Gui { break; } isMenuOpening = false; - isMenuClosed = false; - MenuOpen = true; + IsMenuOpen = true; } - // Starts the menu from a hotkey - void StartMenu () + /// + /// Opens the current Menu programatically. + /// + public void OpenMenu () { if (openMenu != null) return; @@ -778,6 +778,13 @@ namespace Terminal.Gui { SetNeedsDisplay (); } + /// + /// Closes the current Menu programatically, if open. + /// + public void CloseMenu () + { + CloseMenu (false, false); + } internal void CloseMenu (bool reopen = false, bool isSubMenu = false) { isMenuClosing = true; @@ -799,12 +806,12 @@ namespace Terminal.Gui { if (!reopen) selected = -1; LastFocused.SuperView?.SetFocus (LastFocused); + IsMenuOpen = false; } else { SuperView.SetFocus (this); - isMenuClosed = true; + IsMenuOpen = false; PositionCursor (); } - isMenuClosed = true; break; case true: @@ -816,7 +823,7 @@ namespace Terminal.Gui { break; } isMenuClosing = false; - MenuOpen = false; + IsMenuOpen = false; } void RemoveSubMenu (int index) @@ -879,7 +886,7 @@ namespace Terminal.Gui { if (LastFocused != null && LastFocused != this) selected = -1; } - isMenuClosed = true; + IsMenuOpen = false; openedByHotKey = false; openedByAltKey = false; } @@ -993,8 +1000,8 @@ namespace Terminal.Gui { public override bool ProcessHotKey (KeyEvent kb) { if (kb.Key == Key.F9) { - if (isMenuClosed) - StartMenu (); + if (!IsMenuOpen) + OpenMenu (); else CloseAllMenus (); return true; @@ -1085,7 +1092,7 @@ namespace Terminal.Gui { for (int i = 0; i < Menus.Length; i++) { if (cx > pos && me.X < pos + 1 + Menus [i].TitleLength) { if (selected == i && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) && - !isMenuClosed) { + IsMenuOpen) { Application.UngrabMouse (); if (Menus [i].IsTopLevel) { var menu = new Menu (this, i, 0, Menus [i]); @@ -1094,7 +1101,7 @@ namespace Terminal.Gui { CloseMenu (); } } else if ((me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) && - isMenuClosed) { + !IsMenuOpen) { if (Menus [i].IsTopLevel) { var menu = new Menu (this, i, 0, Menus [i]); menu.Run (Menus [i].Action); @@ -1102,12 +1109,12 @@ namespace Terminal.Gui { Activate (i); } } else if (selected != i && selected > -1 && me.Flags == MouseFlags.ReportMousePosition) { - if (!isMenuClosed) { + if (IsMenuOpen) { CloseMenu (); Activate (i); } } else { - if (!isMenuClosed) + if (IsMenuOpen) Activate (i); } return true; @@ -1139,7 +1146,7 @@ namespace Terminal.Gui { handled = false; return false; } - } else if (isMenuClosed && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || + } else if (!IsMenuOpen && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) { Application.GrabMouse (current); } else { From 61c86d9c2a8f3d13f050c22a21993472b62014a7 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sat, 23 May 2020 23:23:37 -0600 Subject: [PATCH 2/4] forgot to delete Program.cs after rename --- UICatalog/Program.cs | 280 ------------------------------------------- 1 file changed, 280 deletions(-) delete mode 100644 UICatalog/Program.cs diff --git a/UICatalog/Program.cs b/UICatalog/Program.cs deleted file mode 100644 index 86ad5405d..000000000 --- a/UICatalog/Program.cs +++ /dev/null @@ -1,280 +0,0 @@ -using NStack; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using Terminal.Gui; - -namespace UICatalog { - /// - /// Main program for the Terminal.gui UI Catalog app. This app provides a chooser that allows - /// for a calalog of UI demos, examples, and tests. - /// - internal class Program { - private static Toplevel _top; - private static MenuBar _menu; - private static int _nameColumnWidth; - private static Window _leftPane; - private static List _categories; - private static ListView _categoryListView; - private static Window _rightPane; - private static List _scenarios; - private static ListView _scenarioListView; - private static StatusBar _statusBar; - - private static Scenario _selectedScenario = null; - - static void Main (string [] args) - { - if (Debugger.IsAttached) - CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); - - _scenarios = Scenario.GetDerivedClassesCollection ().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList(); - - if (args.Length > 0) { - var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase)); - _selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item]); - _selectedScenario.Init (Application.Top); - _selectedScenario.Setup (); - _selectedScenario.Run (); - _selectedScenario = null; - return; - } - - Scenario scenario = GetScenarioToRun (); - while (scenario != null) { - scenario.Init (Application.Top); - scenario.Setup (); - scenario.Run (); - scenario = GetScenarioToRun (); - } - - } - - /// - /// Create all controls. This gets called once and the controls remain with their state between Sceanrio runs. - /// - private static void Setup () - { - _menu = new MenuBar (new MenuBarItem [] { - new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("_Quit", "", () => Application.RequestStop() ) - }), - new MenuBarItem ("_About...", "About this app", () => MessageBox.Query (0, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")), - }); - - _leftPane = new Window ("Categories") { - X = 0, - Y = 1, // for menu - Width = 25, - Height = Dim.Fill (), - CanFocus = false, - }; - - - _categories = Scenario.GetAllCategories ().OrderBy(c => c).ToList(); - _categoryListView = new ListView (_categories) { - X = 1, - Y = 0, - Width = Dim.Fill (0), - Height = Dim.Fill (2), - AllowsMarking = false, - CanFocus = true, - }; - _categoryListView.OpenSelectedItem += (o, a) => { - _top.SetFocus (_rightPane); - }; - _categoryListView.SelectedChanged += CategoryListView_SelectedChanged; - _leftPane.Add (_categoryListView); - - _rightPane = new Window ("Scenarios") { - X = 25, - Y = 1, // for menu - Width = Dim.Fill (), - Height = Dim.Fill (), - CanFocus = false, - - }; - - _nameColumnWidth = Scenario.ScenarioMetadata.GetName (_scenarios.OrderByDescending (t => Scenario.ScenarioMetadata.GetName (t).Length).FirstOrDefault ()).Length; - - _scenarioListView = new ListView () { - X = 0, - Y = 0, - Width = Dim.Fill (0), - Height = Dim.Fill (0), - AllowsMarking = false, - CanFocus = true, - }; - - //_scenarioListView.OnKeyPress += (KeyEvent ke) => { - // if (_top.MostFocused == _scenarioListView && ke.Key == Key.Enter) { - // _scenarioListView_OpenSelectedItem (null, null); - // } - //}; - - _scenarioListView.OpenSelectedItem += _scenarioListView_OpenSelectedItem; - _rightPane.Add (_scenarioListView); - - _categoryListView.SelectedItem = 0; - _categoryListView.OnSelectedChanged (); - - _statusBar = new StatusBar (new StatusItem [] { - //new StatusItem(Key.F1, "~F1~ Help", () => Help()), - new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => { - if (_selectedScenario is null){ - // This causes GetScenarioToRun to return null - _selectedScenario = null; - Application.RequestStop(); - } else { - _selectedScenario.RequestStop(); - } - }), - }); - } - - /// - /// This shows the selection UI. Each time it is run, it calls Application.Init to reset everything. - /// - /// - private static Scenario GetScenarioToRun () - { - Application.Init (); - - if (_menu == null) { - Setup (); - } - - _top = Application.Top; - - _top.KeyUp += KeyUpHandler; - - _top.Add (_menu); - _top.Add (_leftPane); - _top.Add (_rightPane); - _top.Add (_statusBar); - - // HACK: There is no other way to SetFocus before Application.Run. See Issue #445 -#if false - if (_runningScenario != null) - Application.Iteration += Application_Iteration; -#else - _top.Ready += (o, a) => { - if (_selectedScenario != null) { - _top.SetFocus (_rightPane); - _selectedScenario = null; - } - }; -#endif - - Application.Run (_top); - Application.Shutdown (); - return _selectedScenario; - } - -#if false - private static void Application_Iteration (object sender, EventArgs e) - { - Application.Iteration -= Application_Iteration; - _top.SetFocus (_rightPane); - } -#endif - private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e) - { - if (_selectedScenario is null) { - var source = _scenarioListView.Source as ScenarioListDataSource; - _selectedScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]); - Application.RequestStop (); - } - } - - internal class ScenarioListDataSource : IListDataSource { - public List Scenarios { get; set; } - - public bool IsMarked (int item) => false;// Scenarios [item].IsMarked; - - public int Count => Scenarios.Count; - - public ScenarioListDataSource (List itemList) => Scenarios = itemList; - - public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width) - { - container.Move (col, line); - // Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible - var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item])); - RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width); - } - - public void SetMark (int item, bool value) - { - } - - // A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461 - private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width) - { - int used = 0; - int index = 0; - while (index < ustr.Length) { - (var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length); - var count = Rune.ColumnWidth (rune); - if (used + count >= width) break; - driver.AddRune (rune); - used += count; - index += size; - } - - while (used < width) { - driver.AddRune (' '); - used++; - } - } - - public IList ToList () - { - return Scenarios; - } - - } - - /// - /// When Scenarios are running we need to override the behavior of the Menu - /// and Statusbar to enable Scenarios that use those (or related key input) - /// to not be impacted. Same as for tabs. - /// - /// - private static void KeyUpHandler (object sender, View.KeyEventEventArgs a) - { - if (_selectedScenario != null) { - //switch (ke.Key) { - //case Key.Esc: - // //_runningScenario.RequestStop (); - // break; - //case Key.Enter: - // break; - //}< - } else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) { - // BUGBUG: Work around Issue #434 by implementing our own TAB navigation - if (_top.MostFocused == _categoryListView) - _top.SetFocus (_rightPane); - else - _top.SetFocus (_leftPane); - } - } - - private static void CategoryListView_SelectedChanged (object sender, ListViewItemEventArgs e) - { - var item = _categories [_categoryListView.SelectedItem]; - List newlist; - if (item.Equals ("All")) { - newlist = _scenarios; - - } else { - newlist = _scenarios.Where (t => Scenario.ScenarioCategory.GetCategories (t).Contains (item)).ToList (); - } - _scenarioListView.Source = new ScenarioListDataSource (newlist); - _scenarioListView.SelectedItem = 0; - } - } -} From 46b4c9025b645db79d02c8713a38be8143be4069 Mon Sep 17 00:00:00 2001 From: Fabian R Date: Sun, 24 May 2020 06:36:42 -0500 Subject: [PATCH 3/4] Improved View Key event handling + Added Handled property of type bool to the KeyEventEventArgs class. + Added ability to stop further propagation for already handled events on Views for Keyboard related event subscribers (like KeyDown, KeyUp and KeyPress). The driver will check the Handled property of the KeyEventEventArgs passed to the subscribers and will stop any further invocations when its found true. + Updated Example project to expose the ability to programatically Open/Close a MenuBar from custom keystrokes. This commit fixes an issue where the library would crash if the Subviews collection of the currently active View gets updated from inside any of the Keyboard event handlers, such as when the view is updated as a result of a custom Hotkey/Coldkey press. --- Example/demo.cs | 15 ++++++++++++++- Terminal.Gui/Core.cs | 31 ++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Example/demo.cs b/Example/demo.cs index 53974b3c0..95a5726e6 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -190,7 +190,7 @@ static class Demo { new DateField (3, 22, DateTime.Now), new DateField (23, 22, DateTime.Now, true), progress, - new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"), + new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) or Ctrl+T to activate the menubar"), menuKeysStyle, menuAutoMouseNav @@ -636,10 +636,23 @@ static class Demo { }; #endif + win.KeyPress += Win_KeyPress; + top.Add (win); //top.Add (menu); top.Add (menu, statusBar); Application.Run (); } + + private static void Win_KeyPress (object sender, View.KeyEventEventArgs e) + { + if (e.KeyEvent.Key == Key.ControlT) { + if (menu.IsMenuOpen) + menu.CloseMenu (); + else + menu.OpenMenu (); + e.Handled = true; + } + } } diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 3e0032e14..55330a50c 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -1087,6 +1087,11 @@ namespace Terminal.Gui { /// The for the event. /// public KeyEvent KeyEvent { get; set; } + /// + /// Indicates if the current Key event has already been processed and the driver should stop notifying any other event subscriber. + /// Its important to set this value to true specially when updating any View's layout from inside the subscriber method. + /// + public bool Handled { get; set; } = false; } /// @@ -1097,7 +1102,11 @@ namespace Terminal.Gui { /// public override bool ProcessKey (KeyEvent keyEvent) { - KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent)); + + KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + KeyPress?.Invoke (this, args); + if (args.Handled) + return true; if (Focused?.ProcessKey (keyEvent) == true) return true; @@ -1107,7 +1116,10 @@ namespace Terminal.Gui { /// public override bool ProcessHotKey (KeyEvent keyEvent) { - KeyPress?.Invoke (this, new KeyEventEventArgs (keyEvent)); + KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + KeyPress?.Invoke (this, args); + if (args.Handled) + return true; if (subviews == null || subviews.Count == 0) return false; foreach (var view in subviews) @@ -1119,7 +1131,10 @@ namespace Terminal.Gui { /// public override bool ProcessColdKey (KeyEvent keyEvent) { - KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent)); + KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + KeyPress?.Invoke (this, args); + if (args.Handled) + return true; if (subviews == null || subviews.Count == 0) return false; foreach (var view in subviews) @@ -1136,7 +1151,10 @@ namespace Terminal.Gui { /// Contains the details about the key that produced the event. public override bool OnKeyDown (KeyEvent keyEvent) { - KeyDown?.Invoke (this, new KeyEventEventArgs (keyEvent)); + KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + KeyDown?.Invoke (this, args); + if (args.Handled) + return true; if (subviews == null || subviews.Count == 0) return false; foreach (var view in subviews) @@ -1154,7 +1172,10 @@ namespace Terminal.Gui { /// Contains the details about the key that produced the event. public override bool OnKeyUp (KeyEvent keyEvent) { - KeyUp?.Invoke (this, new KeyEventEventArgs (keyEvent)); + KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + KeyUp?.Invoke (this, args); + if (args.Handled) + return true; if (subviews == null || subviews.Count == 0) return false; foreach (var view in subviews) From 12fecfbed5b876be6b8184c6a7c50403d86ccc7c Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 25 May 2020 01:43:13 +0100 Subject: [PATCH 4/4] Added Threading.cs file in the UI-Scenario. --- UICatalog/Scenarios/Threading.cs | 162 +++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 UICatalog/Scenarios/Threading.cs diff --git a/UICatalog/Scenarios/Threading.cs b/UICatalog/Scenarios/Threading.cs new file mode 100644 index 000000000..28b2a72d7 --- /dev/null +++ b/UICatalog/Scenarios/Threading.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Terminal.Gui; + +namespace UICatalog { + [ScenarioMetadata (Name: "Threading", Description: "Demonstration of how to use threading in different ways")] + [ScenarioCategory ("Threading")] + class Threading : Scenario { + private Action _action; + private Action _lambda; + private EventHandler _handler; + private Action _sync; + + private ListView _itemsList; + private Button _btnActionCancel; + List log = new List (); + private ListView _logJob; + + public override void Setup () + { + _action = LoadData; + _lambda = async () => { + _itemsList.Source = null; + LogJob ("Loading task lambda"); + var items = await LoadDataAsync (); + LogJob ("Returning from task lambda"); + _itemsList.SetSource (items); + }; + _handler = async (s, e) => { + _itemsList.Source = null; + LogJob ("Loading task handler"); + var items = await LoadDataAsync (); + LogJob ("Returning from task handler"); + _itemsList.SetSource (items); + + }; + _sync = () => { + _itemsList.Source = null; + LogJob ("Loading task synchronous"); + List items = new List () { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" }; + LogJob ("Returning from task synchronous"); + _itemsList.SetSource (items); + }; + + Application.Init (); + + _btnActionCancel = new Button (1, 1, "Cancelable Load Items"); + _btnActionCancel.Clicked += () => Application.MainLoop.Invoke (CallLoadItemsAsync); + + _itemsList = new ListView { + X = Pos.X (_btnActionCancel), + Y = Pos.Y (_btnActionCancel) + 4, + Width = 10, + Height = 10 + }; + + _logJob = new ListView (log) { + X = Pos.Right (_itemsList) + 10, + Y = Pos.Y (_itemsList), + Width = 50, + Height = Dim.Fill () + }; + + var text = new TextField (1, 3, 100, "Type anything after press the button"); + + var _btnAction = new Button (80, 10, "Load Data Action"); + _btnAction.Clicked += () => _action.Invoke (); + var _btnLambda = new Button (80, 12, "Load Data Lambda"); + _btnLambda.Clicked += () => _lambda.Invoke (); + var _btnHandler = new Button (80, 14, "Load Data Handler"); + _btnHandler.Clicked += () => _handler.Invoke (null, new EventArgs ()); + var _btnSync = new Button (80, 16, "Load Data Synchronous"); + _btnSync.Clicked += () => _sync.Invoke (); + var _btnMethod = new Button (80, 18, "Load Data Method"); + _btnMethod.Clicked += async () => await MethodAsync (); + var _btnClearData = new Button (80, 20, "Clear Data"); + _btnClearData.Clicked += () => { _itemsList.Source = null; LogJob ("Cleaning Data"); }; + var _btnQuit = new Button (80, 22, "Quit"); + _btnQuit.Clicked += Application.RequestStop; + + Win.Add (_itemsList, _btnActionCancel, _logJob, text, _btnAction, _btnLambda, _btnHandler, _btnSync, _btnMethod, _btnClearData, _btnQuit); + Application.Top.Add (Win); + Application.Run (); + + } + + private async void LoadData () + { + _itemsList.Source = null; + LogJob ("Loading task"); + var items = await LoadDataAsync (); + LogJob ("Returning from task"); + _itemsList.SetSource (items); + } + + private void LogJob (string job) + { + log.Add (job); + _logJob.MoveDown (); + } + + private async Task> LoadDataAsync () + { + _itemsList.Source = null; + LogJob ("Starting delay"); + await Task.Delay (3000); + LogJob ("Finished delay"); + return new List () { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" }; + } + + private async Task MethodAsync () + { + _itemsList.Source = null; + LogJob ("Loading task method"); + List items = new List () { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" }; + await Task.Delay (3000); + LogJob ("Returning from task method"); + await _itemsList.SetSourceAsync (items); + } + + private CancellationTokenSource cancellationTokenSource; + + private async void CallLoadItemsAsync () + { + cancellationTokenSource = new CancellationTokenSource (); + _itemsList.Source = null; + LogJob ($"Clicked the button"); + if (_btnActionCancel.Text == "Cancel") { + _btnActionCancel.Text = "Cancelable Load Items"; + cancellationTokenSource.Cancel (); + } else + _btnActionCancel.Text = "Cancel"; + try { + if (cancellationTokenSource.Token.IsCancellationRequested) + cancellationTokenSource.Token.ThrowIfCancellationRequested (); + LogJob ($"Calling task Thread:{Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); + var items = await Task.Run (LoadItemsAsync, cancellationTokenSource.Token); + if (!cancellationTokenSource.IsCancellationRequested) { + LogJob ($"Returned from task Thread:{Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); + _itemsList.SetSource (items); + LogJob ($"Finished populate list view Thread:{Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); + _btnActionCancel.Text = "Load Items"; + } else { + LogJob ("Task was canceled!"); + } + } catch (OperationCanceledException ex) { + LogJob (ex.Message); + } + } + + private async Task> LoadItemsAsync () + { + // Do something that takes lot of times. + LogJob ($"Starting delay Thread:{Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); + await Task.Delay (5000); + LogJob ($"Finished delay Thread:{Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); + return new List () { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" }; + } + } +}