MAJOR CHANGE for ButtonGrids, Dynamic data sources, etc

- introducing a dynamic data source class (IDataSource)
- introducing a ButtonRow class for better managability
- replacing that List<ButtonBase> with ButtonRow object
- introducing ButtonFormDataSource with special methods for ButtonGrid controls
- updating ButtonGrid and refactoring of the Load/Action methods
- updating CheckButtonList and refactoring of the Load/Action methods
- updating TaggedButtonGrid and refactoring of the Load/Action methods
- adding example to the Test project
This commit is contained in:
FlorianDahn
2021-07-26 15:10:10 +02:00
parent 894d0799c8
commit 71433c0e4e
14 changed files with 1225 additions and 266 deletions
+173 -87
View File
@@ -10,6 +10,7 @@ using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Datasources;
using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
@@ -30,7 +31,26 @@ namespace TelegramBotBase.Controls.Hybrid
private readonly EventHandlerList Events = new EventHandlerList();
public ButtonForm ButtonsForm { get; set; }
/// <summary>
///
/// </summary>
[Obsolete("This property is obsolete. Please use the DataSource property instead.")]
public ButtonForm ButtonsForm
{
get
{
return DataSource.ButtonForm;
}
set
{
DataSource = new ButtonFormDataSource(value);
}
}
/// <summary>
/// Data source of the items.
/// </summary>
public ButtonFormDataSource DataSource { get; set; }
public int? MessageId { get; set; }
@@ -89,12 +109,12 @@ namespace TelegramBotBase.Controls.Hybrid
/// <summary>
/// Layout of the buttons which should be displayed always on top.
/// </summary>
public List<ButtonBase> HeadLayoutButtonRow { get; set; }
public ButtonRow HeadLayoutButtonRow { get; set; }
/// <summary>
/// Layout of columns which should be displayed below the header
/// </summary>
public List<ButtonBase> SubHeadLayoutButtonRow { get; set; }
public ButtonRow SubHeadLayoutButtonRow { get; set; }
/// <summary>
/// Defines which type of Button Keyboard should be rendered.
@@ -123,8 +143,7 @@ namespace TelegramBotBase.Controls.Hybrid
public ButtonGrid()
{
this.ButtonsForm = new ButtonForm();
this.DataSource = new ButtonFormDataSource();
}
@@ -136,7 +155,7 @@ namespace TelegramBotBase.Controls.Hybrid
public ButtonGrid(ButtonForm form)
{
this.ButtonsForm = form;
this.DataSource = new ButtonFormDataSource(form);
}
@@ -188,22 +207,49 @@ namespace TelegramBotBase.Controls.Hybrid
if (!result.IsFirstHandler)
return;
if (result.MessageText == null)
if (result.MessageText == null || result.MessageText == "")
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 matches = new List<ButtonRow>();
ButtonRow match = null;
int index = -1;
var index = ButtonsForm.FindRowByButton(button);
if (button != null)
if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
match = HeadLayoutButtonRow;
goto check;
}
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.MessageText);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText);
// bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText)
//var index = bf.FindRowByButton(button);
check:
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
if (match != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match));
result.Handled = true;
return;
@@ -261,22 +307,48 @@ namespace TelegramBotBase.Controls.Hybrid
if (!result.IsFirstHandler)
return;
await result.ConfirmAction(this.ConfirmationText ?? "");
//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);
ButtonRow match = null;
int index = -1;
var index = ButtonsForm.FindRowByButton(button);
if (button != null)
if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
match = HeadLayoutButtonRow;
goto check;
}
if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.RawData, false);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var bf = DataSource.ButtonForm;
//var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData);
//var index = bf.FindRowByButton(button);
check:
if (match != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match));
result.Handled = true;
return;
@@ -313,28 +385,28 @@ namespace TelegramBotBase.Controls.Hybrid
{
case eKeyboardType.InlineKeyBoard:
if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
}
break;
case eKeyboardType.ReplyKeyboard:
if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
}
break;
@@ -351,22 +423,21 @@ namespace TelegramBotBase.Controls.Hybrid
this.RenderNecessary = false;
Message m = null;
ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, (this.EnableSearch ? this.SearchQuery : 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.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// form = form.FilterDuplicate(this.SearchQuery, true);
//}
//else
//{
// form = form.Duplicate();
//}
if (this.EnablePaging)
{
form = GeneratePagingView(form);
IntegratePagingView(form);
}
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
@@ -386,26 +457,37 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
Message m = null;
switch (this.KeyboardType)
{
//Reply Keyboard could only be updated with a new keyboard.
case eKeyboardType.ReplyKeyboard:
if (this.MessageId != null)
if (form.Count == 0)
{
if (form.Count == 0)
if (this.MessageId != null)
{
await this.Device.HideReplyKeyboard();
this.MessageId = null;
return;
}
if (this.DeletePreviousMessage)
await this.Device.DeleteMessage(this.MessageId.Value);
return;
}
if (form.Count == 0)
return;
//if (this.MessageId != null)
//{
// if (form.Count == 0)
// {
// await this.Device.HideReplyKeyboard();
// this.MessageId = null;
// return;
// }
//}
//if (form.Count == 0)
// return;
var rkm = (ReplyKeyboardMarkup)form;
@@ -413,6 +495,10 @@ namespace TelegramBotBase.Controls.Hybrid
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
//Prevent flicker of keyboard
if (this.DeletePreviousMessage && this.MessageId != null)
await this.Device.DeleteMessage(this.MessageId.Value);
break;
case eKeyboardType.InlineKeyBoard:
@@ -443,47 +529,31 @@ namespace TelegramBotBase.Controls.Hybrid
}
private ButtonForm GeneratePagingView(ButtonForm dataForm)
private void IntegratePagingView(ButtonForm dataForm)
{
ButtonForm 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;
bf.AddButtonRow(dataForm[it]);
}
//No Items
if (this.ButtonsForm.Count == 0)
if (dataForm.Rows == 0)
{
bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
}
if (this.IsNavigationBarVisible)
{
//🔍
List<ButtonBase> lst = new List<ButtonBase>();
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$"));
ButtonRow row = new ButtonRow();
row.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
row.Add(new ButtonBase(NextPageLabel, "$next$"));
if (this.EnableSearch)
{
lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
row.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
}
bf.InsertButtonRow(0, lst);
dataForm.InsertButtonRow(0, row);
bf.AddButtonRow(lst);
dataForm.AddButtonRow(row);
}
return bf;
}
public bool PagingNecessary
@@ -545,7 +615,7 @@ namespace TelegramBotBase.Controls.Hybrid
{
get
{
return this.LayoutRows + ButtonsForm.Rows;
return this.LayoutRows + DataSource.RowCount;
}
}
@@ -572,24 +642,40 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
/// <summary>
/// Returns the number of item rows per page.
/// </summary>
public int ItemRowsPerPage
{
get
{
return this.MaximumRow - this.LayoutRows;
}
}
/// <summary>
/// Return the number of pages.
/// </summary>
public int PageCount
{
get
{
if (this.ButtonsForm.Count == 0)
if (DataSource.RowCount == 0)
return 1;
var bf = this.ButtonsForm;
//var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null);
if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
{
bf = bf.FilterDuplicate(this.SearchQuery);
}
var max = this.DataSource.CalculateMax(this.EnableSearch ? this.SearchQuery : null);
if (bf.Rows == 0)
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// bf = bf.FilterDuplicate(this.SearchQuery);
//}
if (max == 0)
return 1;
return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows)));
return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage));
}
}
@@ -0,0 +1,112 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using TelegramBotBase.Form;
namespace TelegramBotBase.Controls.Hybrid
{
[DebuggerDisplay("{Count} columns")]
public class ButtonRow
{
List<ButtonBase> __buttons = new List<ButtonBase>();
public ButtonRow()
{
}
public ButtonRow(params ButtonBase[] buttons)
{
__buttons = buttons.ToList();
}
public ButtonBase this[int index]
{
get
{
return __buttons[index];
}
}
public int Count
{
get
{
return __buttons.Count;
}
}
public void Add(ButtonBase button)
{
__buttons.Add(button);
}
public void AddRange(ButtonBase button)
{
__buttons.Add(button);
}
public void Insert(int index, ButtonBase button)
{
__buttons.Insert(index, button);
}
public IEnumerator<ButtonBase> GetEnumerator()
{
return __buttons.GetEnumerator();
}
public ButtonBase[] ToArray()
{
return __buttons.ToArray();
}
public List<ButtonBase> ToList()
{
return __buttons.ToList();
}
public bool Matches(String text, bool useText = true)
{
foreach (var b in __buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
/// <summary>
/// Returns the button inside of the row which matches.
/// </summary>
/// <param name="text"></param>
/// <param name="useText"></param>
/// <returns></returns>
public ButtonBase GetButtonMatch(String text, bool useText = true)
{
foreach (var b in __buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
return b;
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
return b;
}
return null;
}
public static implicit operator ButtonRow(List<ButtonBase> list)
{
return new ButtonRow() { __buttons = list };
}
}
}
@@ -10,6 +10,7 @@ using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Datasources;
using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
@@ -32,7 +33,23 @@ namespace TelegramBotBase.Controls.Hybrid
private readonly EventHandlerList Events = new EventHandlerList();
public ButtonForm ButtonsForm { get; set; }
[Obsolete("This property is obsolete. Please use the DataSource property instead.")]
public ButtonForm ButtonsForm
{
get
{
return DataSource.ButtonForm;
}
set
{
DataSource = new ButtonFormDataSource(value);
}
}
/// <summary>
/// Data source of the items.
/// </summary>
public ButtonFormDataSource DataSource { get; set; }
List<int> CheckedRows { get; set; } = new List<int>();
@@ -51,6 +68,11 @@ namespace TelegramBotBase.Controls.Hybrid
public bool DeletePreviousMessage { get; set; } = true;
/// <summary>
/// Removes the reply message from a user.
/// </summary>
public bool DeleteReplyMessage { get; set; } = true;
/// <summary>
/// Parsemode of the message.
/// </summary>
@@ -92,12 +114,12 @@ namespace TelegramBotBase.Controls.Hybrid
/// <summary>
/// Layout of the buttons which should be displayed always on top.
/// </summary>
public List<ButtonBase> HeadLayoutButtonRow { get; set; }
public ButtonRow HeadLayoutButtonRow { get; set; }
/// <summary>
/// Layout of columns which should be displayed below the header
/// </summary>
public List<ButtonBase> SubHeadLayoutButtonRow { get; set; }
public ButtonRow SubHeadLayoutButtonRow { get; set; }
/// <summary>
/// Defines which type of Button Keyboard should be rendered.
@@ -126,7 +148,7 @@ namespace TelegramBotBase.Controls.Hybrid
public CheckedButtonList()
{
this.ButtonsForm = new ButtonForm();
this.DataSource = new ButtonFormDataSource();
}
@@ -139,7 +161,7 @@ namespace TelegramBotBase.Controls.Hybrid
public CheckedButtonList(ButtonForm form)
{
this.ButtonsForm = form;
this.DataSource = new ButtonFormDataSource(form);
}
public event AsyncEventHandler<ButtonClickedEventArgs> ButtonClicked
@@ -198,16 +220,50 @@ namespace TelegramBotBase.Controls.Hybrid
if (!result.IsFirstHandler)
return;
if (result.MessageText == null)
if (result.MessageText == null || result.MessageText == "")
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 matches = new List<ButtonRow>();
ButtonRow match = null;
int index = -1;
var index = ButtonsForm.FindRowByButton(button);
if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = HeadLayoutButtonRow;
goto check;
}
if (button == null)
if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.MessageText);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText);
// bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText)
//var index = bf.FindRowByButton(button);
check:
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
if (match == null)
{
if (result.MessageText == PreviousPageLabel)
{
@@ -226,7 +282,7 @@ namespace TelegramBotBase.Controls.Hybrid
else if (result.MessageText.EndsWith(CheckedIconLabel))
{
var s = result.MessageText.Split(' ', '.');
index = int.Parse(s[0]);
index = int.Parse(s[0]) - 1;
if (!this.CheckedRows.Contains(index))
return;
@@ -241,7 +297,7 @@ namespace TelegramBotBase.Controls.Hybrid
else if (result.MessageText.EndsWith(UncheckedIconLabel))
{
var s = result.MessageText.Split(' ', '.');
index = int.Parse(s[0]);
index = int.Parse(s[0]) - 1;
if (this.CheckedRows.Contains(index))
return;
@@ -285,14 +341,18 @@ namespace TelegramBotBase.Controls.Hybrid
}
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
//Remove button click message
if (this.DeletePreviousMessage)
await Device.DeleteMessage(result.MessageId);
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match));
result.Handled = true;
//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)
@@ -309,20 +369,55 @@ namespace TelegramBotBase.Controls.Hybrid
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);
ButtonRow match = null;
int index = -1;
var index = ButtonsForm.FindRowByButton(button);
if (button != null)
if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
match = HeadLayoutButtonRow;
goto check;
}
if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.RawData, false);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var bf = DataSource.ButtonForm;
//var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData);
//var index = bf.FindRowByButton(button);
check:
if (match != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match));
result.Handled = true;
return;
}
//if (button != null)
//{
// await OnButtonClicked(new ButtonClickedEventArgs(button, index));
// result.Handled = true;
// return;
//}
switch (result.RawData)
{
case "$previous$":
@@ -396,28 +491,28 @@ namespace TelegramBotBase.Controls.Hybrid
{
case eKeyboardType.InlineKeyBoard:
if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
}
break;
case eKeyboardType.ReplyKeyboard:
if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
}
break;
@@ -436,7 +531,7 @@ namespace TelegramBotBase.Controls.Hybrid
Message m = null;
ButtonForm form = this.ButtonsForm;
ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, null);
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
@@ -444,12 +539,12 @@ namespace TelegramBotBase.Controls.Hybrid
//}
//else
//{
form = form.Duplicate();
//form = form.Duplicate();
//}
if (this.EnablePaging)
{
form = GeneratePagingView(form);
IntegratePagingView(form);
}
else
{
@@ -458,18 +553,18 @@ namespace TelegramBotBase.Controls.Hybrid
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
{
form.InsertButtonRow(0, this.HeadLayoutButtonRow);
form.InsertButtonRow(0, this.HeadLayoutButtonRow.ToArray());
}
if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0)
{
if (this.IsNavigationBarVisible)
{
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow);
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow.ToArray());
}
else
{
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow);
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow.ToArray());
}
}
@@ -478,21 +573,19 @@ namespace TelegramBotBase.Controls.Hybrid
//Reply Keyboard could only be updated with a new keyboard.
case eKeyboardType.ReplyKeyboard:
if (this.MessageId != null)
if (form.Count == 0)
{
if (form.Count == 0)
if (this.MessageId != null)
{
await this.Device.HideReplyKeyboard();
this.MessageId = null;
return;
}
if (this.DeletePreviousMessage)
await this.Device.DeleteMessage(this.MessageId.Value);
return;
}
if (form.Count == 0)
return;
//if (form.Count == 0)
// return;
var rkm = (ReplyKeyboardMarkup)form;
@@ -500,6 +593,10 @@ namespace TelegramBotBase.Controls.Hybrid
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
//Prevent flicker of keyboard
if (this.DeletePreviousMessage && this.MessageId != null)
await this.Device.DeleteMessage(this.MessageId.Value);
break;
case eKeyboardType.InlineKeyBoard:
@@ -524,58 +621,52 @@ namespace TelegramBotBase.Controls.Hybrid
}
private ButtonForm GeneratePagingView(ButtonForm dataForm)
private void IntegratePagingView(ButtonForm dataForm)
{
//No Items
if (dataForm.Rows == 0)
{
dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
}
ButtonForm bf = new ButtonForm();
bf = PrepareCheckableLayout(dataForm);
//No Items
if (this.ButtonsForm.Count == 0)
{
bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
}
if (this.IsNavigationBarVisible)
{
//🔍
List<ButtonBase> lst = new List<ButtonBase>();
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$"));
ButtonRow row = new ButtonRow();
row.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
row.Add(new ButtonBase(NextPageLabel, "$next$"));
//if (this.EnableSearch)
//{
// lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
//}
bf.InsertButtonRow(0, lst);
dataForm.InsertButtonRow(0, row);
bf.AddButtonRow(lst);
dataForm.AddButtonRow(row);
}
return bf;
}
private ButtonForm PrepareCheckableLayout(ButtonForm dataForm)
{
var bf = new ButtonForm();
for (int i = 0; i < this.MaximumRow - LayoutRows; i++)
for (int i = 0; i < dataForm.Rows; i++)
{
int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i;
if (it > dataForm.Rows - 1)
break;
//if (it > dataForm.Rows - 1)
// break;
var r = dataForm[it];
var r = dataForm[i];
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;
s = $"{it + 1}. " + s;
}
if (CheckedRows.Contains(it))
@@ -652,7 +743,7 @@ namespace TelegramBotBase.Controls.Hybrid
{
get
{
return this.LayoutRows + ButtonsForm.Rows;
return this.LayoutRows + DataSource.RowCount;
}
}
@@ -679,24 +770,37 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
/// <summary>
/// Returns the number of item rows per page.
/// </summary>
public int ItemRowsPerPage
{
get
{
return this.MaximumRow - this.LayoutRows;
}
}
public int PageCount
{
get
{
if (this.ButtonsForm.Count == 0)
if (DataSource.RowCount == 0)
return 1;
var bf = this.ButtonsForm;
//var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null);
var max = this.DataSource.RowCount;
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// bf = bf.FilterDuplicate(this.SearchQuery);
//}
if (bf.Rows == 0)
if (max == 0)
return 1;
return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows)));
return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage));
}
}
@@ -7,9 +7,11 @@ using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InlineQueryResults;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Datasources;
using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
@@ -30,7 +32,23 @@ namespace TelegramBotBase.Controls.Hybrid
private readonly EventHandlerList Events = new EventHandlerList();
public ButtonForm ButtonsForm { get; set; }
[Obsolete("This property is obsolete. Please use the DataSource property instead.")]
public ButtonForm ButtonsForm
{
get
{
return DataSource.ButtonForm;
}
set
{
DataSource = new ButtonFormDataSource(value);
}
}
/// <summary>
/// Data source of the items.
/// </summary>
public ButtonFormDataSource DataSource { get; set; }
public int? MessageId { get; set; }
@@ -62,6 +80,11 @@ namespace TelegramBotBase.Controls.Hybrid
/// </summary>
public bool EnablePaging { get; set; } = false;
/// <summary>
/// Shows un-/check all tags options
/// </summary>
public bool EnableCheckAllTools { get; set; } = false;
/// <summary>
/// Enabled a search function.
/// </summary>
@@ -87,15 +110,24 @@ namespace TelegramBotBase.Controls.Hybrid
public String BackLabel = Localizations.Default.Language["ButtonGrid_Back"];
public String CheckAllLabel = Localizations.Default.Language["ButtonGrid_CheckAll"];
public String UncheckAllLabel = Localizations.Default.Language["ButtonGrid_UncheckAll"];
/// <summary>
/// Layout of the buttons which should be displayed always on top.
/// </summary>
public List<ButtonBase> HeadLayoutButtonRow { get; set; }
public ButtonRow HeadLayoutButtonRow { get; set; }
/// <summary>
/// Layout of columns which should be displayed below the header
/// </summary>
public List<ButtonBase> SubHeadLayoutButtonRow { get; set; }
public ButtonRow SubHeadLayoutButtonRow { get; set; }
/// <summary>
/// Layout of columns which should be displayed below the header
/// </summary>
private ButtonRow TagsSubHeadLayoutButtonRow { get; set; }
/// <summary>
/// List of Tags which will be allowed to filter by.
@@ -134,7 +166,7 @@ namespace TelegramBotBase.Controls.Hybrid
public TaggedButtonGrid()
{
this.ButtonsForm = new ButtonForm();
this.DataSource = new ButtonFormDataSource();
this.SelectedViewIndex = 0;
}
@@ -147,7 +179,7 @@ namespace TelegramBotBase.Controls.Hybrid
public TaggedButtonGrid(ButtonForm form)
{
this.ButtonsForm = form;
this.DataSource = new ButtonFormDataSource(form);
}
@@ -202,27 +234,60 @@ namespace TelegramBotBase.Controls.Hybrid
if (result.MessageText == null || result.MessageText == "")
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 matches = new List<ButtonRow>();
ButtonRow match = null;
int index = -1;
if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = HeadLayoutButtonRow;
goto check;
}
if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
if (TagsSubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
{
match = TagsSubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.MessageText);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText);
// bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText)
//var index = bf.FindRowByButton(button);
check:
var index = ButtonsForm.FindRowByButton(button);
switch (this.SelectedViewIndex)
{
case 0:
if (button != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
if (match != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match));
result.Handled = true;
return;
}
@@ -287,17 +352,25 @@ namespace TelegramBotBase.Controls.Hybrid
break;
case 1:
//Remove button click message
if (this.DeleteReplyMessage)
await Device.DeleteMessage(result.MessageId);
if (result.MessageText == this.BackLabel)
{
//Remove button click message
if (this.DeletePreviousMessage)
await Device.DeleteMessage(result.MessageId);
this.SelectedViewIndex = 0;
this.Updated();
result.Handled = true;
return;
}
else if (result.MessageText == this.CheckAllLabel)
{
this.CheckAllTags();
}
else if (result.MessageText == this.UncheckAllLabel)
{
this.UncheckAllTags();
}
var i = result.MessageText.LastIndexOf(" ");
if (i == -1)
@@ -340,15 +413,48 @@ namespace TelegramBotBase.Controls.Hybrid
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);
ButtonRow match = null;
int index = -1;
var index = ButtonsForm.FindRowByButton(button);
if (button != null)
if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
await OnButtonClicked(new ButtonClickedEventArgs(button, index));
match = HeadLayoutButtonRow;
goto check;
}
if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
{
match = SubHeadLayoutButtonRow;
goto check;
}
if (TagsSubHeadLayoutButtonRow?.Matches(result.RawData) ?? false)
{
match = TagsSubHeadLayoutButtonRow;
goto check;
}
var br = DataSource.FindRow(result.RawData, false);
if (br != null)
{
match = br.Item1;
index = br.Item2;
}
//var bf = DataSource.ButtonForm;
//var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData);
//var index = bf.FindRowByButton(button);
check:
if (match != null)
{
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match));
result.Handled = true;
return;
@@ -386,6 +492,18 @@ namespace TelegramBotBase.Controls.Hybrid
this.SelectedViewIndex = 0;
this.Updated();
break;
case "$checkall$":
this.CheckAllTags();
break;
case "$uncheckall$":
this.UncheckAllTags();
break;
}
@@ -400,28 +518,28 @@ namespace TelegramBotBase.Controls.Hybrid
{
case eKeyboardType.InlineKeyBoard:
if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
}
break;
case eKeyboardType.ReplyKeyboard:
if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols)
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
}
break;
@@ -468,16 +586,16 @@ namespace TelegramBotBase.Controls.Hybrid
{
Message m = null;
ButtonForm form = this.ButtonsForm;
ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, (this.EnableSearch ? this.SearchQuery : null));
if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
{
form = form.FilterDuplicate(this.SearchQuery, true);
}
else
{
form = form.Duplicate();
}
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// form = form.FilterDuplicate(this.SearchQuery, true);
//}
//else
//{
// form = form.Duplicate();
//}
if (this.Tags != null && this.SelectedTags != null)
{
@@ -486,23 +604,23 @@ namespace TelegramBotBase.Controls.Hybrid
if (this.EnablePaging)
{
form = GeneratePagingView(form);
IntegratePagingView(form);
}
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
{
form.InsertButtonRow(0, this.HeadLayoutButtonRow);
form.InsertButtonRow(0, this.HeadLayoutButtonRow.ToArray());
}
if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0)
{
if (this.IsNavigationBarVisible)
{
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow);
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow.ToArray());
}
else
{
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow);
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow.ToArray());
}
}
@@ -559,52 +677,37 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
private ButtonForm GeneratePagingView(ButtonForm dataForm)
private void IntegratePagingView(ButtonForm dataForm)
{
ButtonForm 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;
bf.AddButtonRow(dataForm[it]);
}
//No Items
if (this.ButtonsForm.Count == 0)
if (dataForm.Rows == 0)
{
bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
}
if (this.IsNavigationBarVisible)
{
//🔍
List<ButtonBase> lst = new List<ButtonBase>();
lst.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
ButtonRow row = new ButtonRow();
row.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
if (this.Tags != null && this.Tags.Count > 0)
{
lst.Add(new ButtonBase("📁", "$filter$"));
row.Add(new ButtonBase("📁", "$filter$"));
}
lst.Add(new ButtonBase(NextPageLabel, "$next$"));
row.Add(new ButtonBase(NextPageLabel, "$next$"));
if (this.EnableSearch)
{
lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
row.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
}
bf.InsertButtonRow(0, lst);
dataForm.InsertButtonRow(0, row);
bf.AddButtonRow(lst);
dataForm.AddButtonRow(row);
}
return bf;
}
#endregion
@@ -620,6 +723,12 @@ namespace TelegramBotBase.Controls.Hybrid
bf.AddButtonRow(this.BackLabel, "$back$");
if (EnableCheckAllTools)
{
this.TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(CheckAllLabel, "$checkall$"), new ButtonBase(UncheckAllLabel, "$uncheckall$"));
bf.AddButtonRow(TagsSubHeadLayoutButtonRow);
}
foreach (var t in this.Tags)
{
@@ -748,7 +857,7 @@ namespace TelegramBotBase.Controls.Hybrid
{
get
{
return this.LayoutRows + ButtonsForm.Rows;
return this.LayoutRows + DataSource.RowCount;
}
}
@@ -771,28 +880,46 @@ namespace TelegramBotBase.Controls.Hybrid
if (this.SubHeadLayoutButtonRow != null && this.SubHeadLayoutButtonRow.Count > 0)
layoutRows++;
if (EnableCheckAllTools && this.SelectedViewIndex == 1)
{
layoutRows++;
}
return layoutRows;
}
}
/// <summary>
/// Returns the number of item rows per page.
/// </summary>
public int ItemRowsPerPage
{
get
{
return this.MaximumRow - this.LayoutRows;
}
}
public int PageCount
{
get
{
if (this.ButtonsForm.Count == 0)
if (DataSource.RowCount == 0)
return 1;
var bf = this.ButtonsForm;
//var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null);
if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
{
bf = bf.FilterDuplicate(this.SearchQuery);
}
var max = this.DataSource.CalculateMax(this.EnableSearch ? this.SearchQuery : null);
if (bf.Rows == 0)
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// bf = bf.FilterDuplicate(this.SearchQuery);
//}
if (max == 0)
return 1;
return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - this.LayoutRows)));
return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage));
}
}
@@ -844,6 +971,8 @@ namespace TelegramBotBase.Controls.Hybrid
break;
}
}
/// <summary>
/// Checks all tags for filtering.