From dfe64b22ab02ef497f7096cb15320b509eb87ab9 Mon Sep 17 00:00:00 2001 From: FlorianDahn Date: Sun, 28 Feb 2021 15:35:49 +0100 Subject: [PATCH] New control: CheckedButtonList - new control for having a checked listview with paging possible - new CheckedChangedEventArgs class - adding Test for CheckedButtonList - refactoring MultiView Test --- .../Args/CheckedChangedEventArgs.cs | 43 + .../Controls/Hybrid/CheckedButtonList.cs | 770 ++++++++++++++++++ .../Tests/Controls/CheckedButtonListForm.cs | 89 ++ .../Tests/Controls/Subclass/MultiViewTest.cs | 18 +- TelegramBotBaseTest/Tests/Menu.cs | 13 + 5 files changed, 919 insertions(+), 14 deletions(-) create mode 100644 TelegramBotBase/Args/CheckedChangedEventArgs.cs create mode 100644 TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs create mode 100644 TelegramBotBaseTest/Tests/Controls/CheckedButtonListForm.cs diff --git a/TelegramBotBase/Args/CheckedChangedEventArgs.cs b/TelegramBotBase/Args/CheckedChangedEventArgs.cs new file mode 100644 index 0000000..90d4014 --- /dev/null +++ b/TelegramBotBase/Args/CheckedChangedEventArgs.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TelegramBotBase.Form; + +namespace TelegramBotBase.Args +{ + public class CheckedChangedEventArgs : EventArgs + { + /// + /// Contains the index of the row where the button is inside. + /// Contains -1 when it is a layout button or not found. + /// + public int Index { get; set; } + + + /// + /// Contains all buttons within this row, excluding the checkbox. + /// + public List Row { get; set; } + + + /// + /// Contains the new checked status of the row. + /// + public bool Checked { get; set; } + + + public CheckedChangedEventArgs() + { + + } + + public CheckedChangedEventArgs(List row, int Index, bool Checked) + { + this.Row = row; + this.Index = Index; + this.Checked = Checked; + } + + + } +} diff --git a/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs b/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs new file mode 100644 index 0000000..fe3b8c9 --- /dev/null +++ b/TelegramBotBase/Controls/Hybrid/CheckedButtonList.cs @@ -0,0 +1,770 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.SymbolStore; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.ReplyMarkups; +using TelegramBotBase.Args; +using TelegramBotBase.Base; +using TelegramBotBase.Enums; +using TelegramBotBase.Exceptions; +using TelegramBotBase.Form; +using static TelegramBotBase.Base.Async; + +namespace TelegramBotBase.Controls.Hybrid +{ + public class CheckedButtonList : Base.ControlBase + { + + public String Title { get; set; } = Localizations.Default.Language["ButtonGrid_Title"]; + + public String ConfirmationText { get; set; } = ""; + + private bool RenderNecessary = true; + + private static readonly object __evButtonClicked = new object(); + + private static readonly object __evCheckedChanged = new object(); + + private readonly EventHandlerList Events = new EventHandlerList(); + + public ButtonForm ButtonsForm { get; set; } + + List CheckedRows { get; set; } = new List(); + + public int? MessageId { get; set; } + + + /// + /// Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. + /// Source: https://core.telegram.org/bots/api#replykeyboardmarkup + /// + public bool ResizeKeyboard { get; set; } = false; + + public bool OneTimeKeyboard { get; set; } = false; + + public bool HideKeyboardOnCleanup { get; set; } = true; + + public bool DeletePreviousMessage { get; set; } = true; + + /// + /// Parsemode of the message. + /// + public ParseMode MessageParseMode { get; set; } = ParseMode.Default; + + /// + /// Enables automatic paging of buttons when the amount of rows is exceeding the limits. + /// + public bool EnablePaging { get; set; } = false; + + + ///// + ///// Enabled a search function. + ///// + //public bool EnableSearch { get; set; } = false; + + //public String SearchQuery { get; set; } + + public eNavigationBarVisibility NavigationBarVisibility { get; set; } = eNavigationBarVisibility.always; + + + /// + /// Index of the current page + /// + public int CurrentPageIndex { get; set; } = 0; + + public String PreviousPageLabel = Localizations.Default.Language["ButtonGrid_PreviousPage"]; + + public String NextPageLabel = Localizations.Default.Language["ButtonGrid_NextPage"]; + + public String NoItemsLabel = Localizations.Default.Language["ButtonGrid_NoItems"]; + + //public String SearchLabel = Localizations.Default.Language["ButtonGrid_SearchFeature"]; + + public String CheckedIconLabel { get; set; } = "✅"; + + public String UncheckedIconLabel { get; set; } = "◻️"; + + /// + /// Layout of the buttons which should be displayed always on top. + /// + public List HeadLayoutButtonRow { get; set; } + + /// + /// Layout of columns which should be displayed below the header + /// + public List SubHeadLayoutButtonRow { get; set; } + + /// + /// Defines which type of Button Keyboard should be rendered. + /// + public eKeyboardType KeyboardType + { + get + { + return m_eKeyboardType; + } + set + { + if (m_eKeyboardType != value) + { + this.RenderNecessary = true; + + Cleanup().Wait(); + + m_eKeyboardType = value; + } + + } + } + + private eKeyboardType m_eKeyboardType = eKeyboardType.ReplyKeyboard; + + public CheckedButtonList() + { + this.ButtonsForm = new ButtonForm(); + + + } + + public CheckedButtonList(eKeyboardType type) : this() + { + m_eKeyboardType = type; + } + + + public CheckedButtonList(ButtonForm form) + { + this.ButtonsForm = form; + } + + public event AsyncEventHandler ButtonClicked + { + add + { + this.Events.AddHandler(__evButtonClicked, value); + } + remove + { + this.Events.RemoveHandler(__evButtonClicked, value); + } + } + + public async Task OnButtonClicked(ButtonClickedEventArgs e) + { + var handler = this.Events[__evButtonClicked]?.GetInvocationList().Cast>(); + if (handler == null) + return; + + foreach (var h in handler) + { + await Async.InvokeAllAsync(h, this, e); + } + } + + public event AsyncEventHandler CheckedChanged + { + add + { + this.Events.AddHandler(__evCheckedChanged, value); + } + remove + { + this.Events.RemoveHandler(__evCheckedChanged, value); + } + } + + public async Task OnCheckedChanged(CheckedChangedEventArgs e) + { + var handler = this.Events[__evCheckedChanged]?.GetInvocationList().Cast>(); + if (handler == null) + return; + + foreach (var h in handler) + { + await Async.InvokeAllAsync(h, this, e); + } + } + + public async override Task Load(MessageResult result) + { + if (this.KeyboardType != eKeyboardType.ReplyKeyboard) + return; + + if (!result.IsFirstHandler) + return; + + if (result.MessageText == null) + return; + + var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) + ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) + ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText); + + var index = ButtonsForm.FindRowByButton(button); + + if (button == null) + { + if (result.MessageText == PreviousPageLabel) + { + if (this.CurrentPageIndex > 0) + this.CurrentPageIndex--; + + this.Updated(); + } + else if (result.MessageText == NextPageLabel) + { + if (this.CurrentPageIndex < this.PageCount - 1) + this.CurrentPageIndex++; + + this.Updated(); + } + else if (result.MessageText.EndsWith(CheckedIconLabel)) + { + var s = result.MessageText.Split(' ', '.'); + index = int.Parse(s[0]); + + if (!this.CheckedRows.Contains(index)) + return; + + this.CheckedRows.Remove(index); + + this.Updated(); + + await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, false)); + + } + else if (result.MessageText.EndsWith(UncheckedIconLabel)) + { + var s = result.MessageText.Split(' ', '.'); + index = int.Parse(s[0]); + + if (this.CheckedRows.Contains(index)) + return; + + + this.CheckedRows.Add(index); + + this.Updated(); + + await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, true)); + + } + //else if (this.EnableSearch) + //{ + // if (result.MessageText.StartsWith("🔍")) + // { + // //Sent note about searching + // if (this.SearchQuery == null) + // { + // await this.Device.Send(this.SearchLabel); + // } + + // this.SearchQuery = null; + // this.Updated(); + // return; + // } + + // this.SearchQuery = result.MessageText; + + // if (this.SearchQuery != null && this.SearchQuery != "") + // { + // this.CurrentPageIndex = 0; + // this.Updated(); + // } + + //} + + + + return; + } + + + await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + + //Remove button click message + if (this.DeletePreviousMessage) + await Device.DeleteMessage(result.MessageId); + + result.Handled = true; + + } + + public async override Task Action(MessageResult result, string value = null) + { + if (result.Handled) + return; + + if (!result.IsFirstHandler) + return; + + //Find clicked button depending on Text or Value (depending on markup type) + if (this.KeyboardType != eKeyboardType.InlineKeyBoard) + return; + + await result.ConfirmAction(this.ConfirmationText ?? ""); + + var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) + ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData); + + var index = ButtonsForm.FindRowByButton(button); + + if (button != null) + { + await OnButtonClicked(new ButtonClickedEventArgs(button, index)); + + result.Handled = true; + return; + } + + switch (result.RawData) + { + case "$previous$": + + if (this.CurrentPageIndex > 0) + this.CurrentPageIndex--; + + this.Updated(); + + break; + case "$next$": + + if (this.CurrentPageIndex < this.PageCount - 1) + this.CurrentPageIndex++; + + this.Updated(); + + break; + + default: + + var s = result.RawData.Split('$'); + + + switch (s[0]) + { + case "check": + + index = int.Parse(s[1]); + + if (!this.CheckedRows.Contains(index)) + { + this.CheckedRows.Add(index); + + this.Updated(); + + await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, true)); + } + + break; + + case "uncheck": + + index = int.Parse(s[1]); + + if (this.CheckedRows.Contains(index)) + { + this.CheckedRows.Remove(index); + + this.Updated(); + + await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, false)); + } + + break; + } + + + break; + + } + + } + + /// + /// This method checks of the amount of buttons + /// + private void CheckGrid() + { + switch (m_eKeyboardType) + { + case eKeyboardType.InlineKeyBoard: + + if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging) + { + throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; + } + + if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols) + { + throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols }; + } + + break; + + case eKeyboardType.ReplyKeyboard: + + if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging) + { + throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; + } + + if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols) + { + throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols }; + } + + break; + } + } + + public async override Task Render(MessageResult result) + { + if (!this.RenderNecessary) + return; + + //Check for rows and column limits + CheckGrid(); + + this.RenderNecessary = false; + + Message m = null; + + ButtonForm form = this.ButtonsForm; + + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // form = form.FilterDuplicate(this.SearchQuery, true); + //} + //else + //{ + form = form.Duplicate(); + //} + + if (this.EnablePaging) + { + form = GeneratePagingView(form); + } + else + { + form = PrepareCheckableLayout(form); + } + + if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0) + { + form.InsertButtonRow(0, this.HeadLayoutButtonRow); + } + + if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0) + { + if (this.IsNavigationBarVisible) + { + form.InsertButtonRow(2, this.SubHeadLayoutButtonRow); + } + else + { + form.InsertButtonRow(1, this.SubHeadLayoutButtonRow); + } + } + + switch (this.KeyboardType) + { + //Reply Keyboard could only be updated with a new keyboard. + case eKeyboardType.ReplyKeyboard: + + if (this.MessageId != null) + { + if (form.Count == 0) + { + await this.Device.HideReplyKeyboard(); + this.MessageId = null; + return; + } + + if (this.DeletePreviousMessage) + await this.Device.DeleteMessage(this.MessageId.Value); + } + + if (form.Count == 0) + return; + + + var rkm = (ReplyKeyboardMarkup)form; + rkm.ResizeKeyboard = this.ResizeKeyboard; + rkm.OneTimeKeyboard = this.OneTimeKeyboard; + m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false); + + break; + + case eKeyboardType.InlineKeyBoard: + + if (this.MessageId != null) + { + m = await this.Device.Edit(this.MessageId.Value, this.Title, (InlineKeyboardMarkup)form); + } + else + { + m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false); + } + + break; + } + + if (m != null) + { + this.MessageId = m.MessageId; + } + + + } + + private ButtonForm GeneratePagingView(ButtonForm dataForm) + { + + ButtonForm bf = new ButtonForm(); + + bf = PrepareCheckableLayout(dataForm); + + //No Items + if (this.ButtonsForm.Count == 0) + { + bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$")); + } + + if (this.IsNavigationBarVisible) + { + //🔍 + List lst = new List(); + lst.Add(new ButtonBase(PreviousPageLabel, "$previous$")); + lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$")); + lst.Add(new ButtonBase(NextPageLabel, "$next$")); + + //if (this.EnableSearch) + //{ + // lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$")); + //} + + bf.InsertButtonRow(0, lst); + + bf.AddButtonRow(lst); + } + + return bf; + } + + private ButtonForm PrepareCheckableLayout(ButtonForm dataForm) + { + var bf = new ButtonForm(); + for (int i = 0; i < this.MaximumRow - LayoutRows; i++) + { + int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i; + + if (it > dataForm.Rows - 1) + break; + + var r = dataForm[it]; + + String s = CheckedRows.Contains(it) ? this.CheckedIconLabel : this.UncheckedIconLabel; + + //On reply keyboards we need a unique text. + if (this.KeyboardType == eKeyboardType.ReplyKeyboard) + { + s = $"{it}. " + s; + } + + if (CheckedRows.Contains(it)) + { + r.Insert(0, new ButtonBase(s, "uncheck$" + it.ToString())); + } + else + { + r.Insert(0, new ButtonBase(s, "check$" + it.ToString())); + } + + bf.AddButtonRow(r); + } + + return bf; + } + + public bool PagingNecessary + { + get + { + if (this.KeyboardType == eKeyboardType.InlineKeyBoard && TotalRows > Constants.Telegram.MaxInlineKeyBoardRows) + { + return true; + } + + if (this.KeyboardType == eKeyboardType.ReplyKeyboard && TotalRows > Constants.Telegram.MaxReplyKeyboardRows) + { + return true; + } + + return false; + } + } + + public bool IsNavigationBarVisible + { + get + { + if (this.NavigationBarVisibility == eNavigationBarVisibility.always | (this.NavigationBarVisibility == eNavigationBarVisibility.auto && PagingNecessary)) + { + return true; + } + + return false; + } + } + + /// + /// Returns the maximum number of rows + /// + public int MaximumRow + { + get + { + switch (this.KeyboardType) + { + case eKeyboardType.InlineKeyBoard: + return Constants.Telegram.MaxInlineKeyBoardRows; + + case eKeyboardType.ReplyKeyboard: + return Constants.Telegram.MaxReplyKeyboardRows; + + default: + return 0; + } + } + } + + /// + /// Returns the number of all rows (layout + navigation + content); + /// + public int TotalRows + { + get + { + return this.LayoutRows + ButtonsForm.Rows; + } + } + + + /// + /// Contains the Number of Rows which are used by the layout. + /// + private int LayoutRows + { + get + { + int layoutRows = 0; + + if (this.NavigationBarVisibility == eNavigationBarVisibility.always | this.NavigationBarVisibility == eNavigationBarVisibility.auto) + layoutRows += 2; + + if (this.HeadLayoutButtonRow != null && this.HeadLayoutButtonRow.Count > 0) + layoutRows++; + + if (this.SubHeadLayoutButtonRow != null && this.SubHeadLayoutButtonRow.Count > 0) + layoutRows++; + + return layoutRows; + } + } + + public int PageCount + { + get + { + if (this.ButtonsForm.Count == 0) + return 1; + + var bf = this.ButtonsForm; + + //if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "") + //{ + // bf = bf.FilterDuplicate(this.SearchQuery); + //} + + if (bf.Rows == 0) + return 1; + + return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows))); + } + } + + public override async Task Hidden(bool FormClose) + { + //Prepare for opening Modal, and comming back + if (!FormClose) + { + this.Updated(); + } + } + + public List CheckedItems + { + get + { + List lst = new List(); + + foreach (var c in CheckedRows) + { + lst.Add(this.ButtonsForm[c][0]); + } + + return lst; + } + } + + + /// + /// Tells the control that it has been updated. + /// + public void Updated() + { + this.RenderNecessary = true; + } + + public async override Task Cleanup() + { + if (this.MessageId == null) + return; + + switch (this.KeyboardType) + { + case eKeyboardType.InlineKeyBoard: + + await this.Device.DeleteMessage(this.MessageId.Value); + + this.MessageId = null; + + break; + case eKeyboardType.ReplyKeyboard: + + if (this.HideKeyboardOnCleanup) + { + await this.Device.HideReplyKeyboard(); + } + + this.MessageId = null; + + break; + } + + + + + } + + } + + +} diff --git a/TelegramBotBaseTest/Tests/Controls/CheckedButtonListForm.cs b/TelegramBotBaseTest/Tests/Controls/CheckedButtonListForm.cs new file mode 100644 index 0000000..3fd162e --- /dev/null +++ b/TelegramBotBaseTest/Tests/Controls/CheckedButtonListForm.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Args; +using TelegramBotBase.Controls; +using TelegramBotBase.Controls.Hybrid; +using TelegramBotBase.Form; + +namespace TelegramBotBaseTest.Tests.Controls +{ + public class CheckedButtonListForm : AutoCleanForm + { + + CheckedButtonList m_Buttons = null; + + public CheckedButtonListForm() + { + this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm; + + this.Init += CheckedButtonListForm_Init; + } + + private async Task CheckedButtonListForm_Init(object sender, InitEventArgs e) + { + m_Buttons = new CheckedButtonList(); + + m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard; + m_Buttons.EnablePaging = true; + + m_Buttons.HeadLayoutButtonRow = new List() { new ButtonBase("Back", "back"), new ButtonBase("Switch Keyboard", "switch") }; + + m_Buttons.SubHeadLayoutButtonRow = new List() { new ButtonBase("No checked items", "$") }; + + ButtonForm bf = new ButtonForm(); + + for(int i = 0;i < 30;i++) + { + bf.AddButtonRow($"{i}. Item", i.ToString()); + } + + m_Buttons.ButtonsForm = bf; + + m_Buttons.ButtonClicked += Bg_ButtonClicked; + m_Buttons.CheckedChanged += M_Buttons_CheckedChanged; + + this.AddControl(m_Buttons); + } + + private async Task M_Buttons_CheckedChanged(object sender, CheckedChangedEventArgs e) + { + m_Buttons.SubHeadLayoutButtonRow = new List() { new ButtonBase($"{m_Buttons.CheckedItems.Count} checked items", "$") }; + } + + private async Task Bg_ButtonClicked(object sender, ButtonClickedEventArgs e) + { + if (e.Button == null) + return; + + if (e.Button.Value == "back") + { + var start = new Menu(); + await this.NavigateTo(start); + } + else if (e.Button.Value == "switch") + { + switch (m_Buttons.KeyboardType) + { + case TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard: + m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard; + break; + case TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard: + m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard; + break; + } + + + } + else + { + + await this.Device.Send($"Button clicked with Text: {e.Button.Text} and Value {e.Button.Value}"); + } + + + } + } +} diff --git a/TelegramBotBaseTest/Tests/Controls/Subclass/MultiViewTest.cs b/TelegramBotBaseTest/Tests/Controls/Subclass/MultiViewTest.cs index cc2812d..f376903 100644 --- a/TelegramBotBaseTest/Tests/Controls/Subclass/MultiViewTest.cs +++ b/TelegramBotBaseTest/Tests/Controls/Subclass/MultiViewTest.cs @@ -14,8 +14,8 @@ namespace TelegramBotBaseTest.Tests.Controls.Subclass public override async Task Action(MessageResult result, string value = null) { - - switch(result.RawData) + + switch (result.RawData) { case "back": @@ -37,23 +37,13 @@ namespace TelegramBotBaseTest.Tests.Controls.Subclass ButtonForm bf = new ButtonForm(); bf.AddButtonRow(new ButtonBase("Back", "back"), new ButtonBase("Next", "next")); - switch(e.CurrentView) + switch (e.CurrentView) { case 0: - - await Device.Send("Page 1", bf); - - break; - case 1: - - await Device.Send("Page 2", bf); - - break; - case 2: - await Device.Send("Page 3", bf); + await Device.Send($"Page {e.CurrentView + 1}", bf); break; diff --git a/TelegramBotBaseTest/Tests/Menu.cs b/TelegramBotBaseTest/Tests/Menu.cs index 001dff3..d72d04f 100644 --- a/TelegramBotBaseTest/Tests/Menu.cs +++ b/TelegramBotBaseTest/Tests/Menu.cs @@ -179,6 +179,17 @@ namespace TelegramBotBaseTest.Tests await NavigateTo(mvf); + break; + + case "checkedbuttonlist": + + message.Handled = true; + + var cbl = new CheckedButtonListForm(); + + await NavigateTo(cbl); + + break; } @@ -214,6 +225,8 @@ namespace TelegramBotBaseTest.Tests btn.AddButtonRow(new ButtonBase("#15 MultiView", new CallbackData("a", "multiview").Serialize())); + btn.AddButtonRow(new ButtonBase("#16 CheckedButtonList", new CallbackData("a", "checkedbuttonlist").Serialize())); + await this.Device.Send("Choose your test:", btn); }