首页 > 解决方案 > Bot 框架 - 如何创建可搜索列表

问题描述

我可以理解 bot 框架在它提供的控件方面还不成熟,我正在尝试向用户显示选项列表。当列表有更多元素时,只会显示前 5 个元素,外加一个查看更多元素的选项,该选项将显示所有带有搜索文本框的项目以避免滚动。因此,我正在尝试实现一个可搜索的列表。查看机器人框架,但提示和自适应卡片不是很有用,或者如果我错过了一些东西,得到一些帮助会很好。

注意 - 我正在使用机器人框架 v3

谢谢

标签: botframeworkadaptive-cardsadaptive-design

解决方案


我把这个放在一起。它做的事情类似于你想做的事情。它允许您将列表传递给它,然后它会显示一个提示对话框,其中包含一组选项,让您选择新的搜索以及下一个和上一个。

[Serializable]
public class OptionsDialog<T> : IDialog<OptionsResult<T>>
    where T : class
{
    private const string CurrentPage = "currentPage";

    private readonly IList<T> _items;

    private readonly Func<T, string> _descriptionFunc;

    private readonly Func<T, IReadOnlyList<string>> _synonymFunc;

    private readonly string _prompt;

    private int current = 0;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="items">Must be a distinct list</param>
    /// <param name="descriptionFunction">Function to get the description for an option</param>
    /// <param name="synonymFunction">Function to get the synonyms for an option</param>
    /// <param name="prompt">Prompt for the options</param>
    public OptionsDialog(IList<T> items, Func<T, string> descriptionFunction, Func<T, IReadOnlyList<string>> synonymFunction, string prompt)
    {
        _items = items;
        _descriptionFunc = descriptionFunction;
        _synonymFunc = synonymFunction;
        _prompt = prompt;
    }

    public Task StartAsync(IDialogContext context)
    {
        if (_items.Count() == 0)
        {
            context.Done(new OptionsResult<T>(null, false));
        }
        else if (_items.Count() == 1)
        {
            context.Done(new OptionsResult<T>(_items.First(), false));
        }
        else
        {
            SendContactOptions(context, _items);
        }

        return Task.CompletedTask;
    }

    private void SendContactOptions(IDialogContext context, IList<T> items)
    {
        int maxContacts = int.Parse(ConfigurationManager.AppSettings["MaxContactsShown"]);
        bool addNext = false;
        bool addPrevious = false;
        if (items.Count > maxContacts)
        {
            if (current > 0)
            {
                addPrevious = true;
            }

            if (items.Skip(current * maxContacts).Count() > maxContacts)
            {
                addNext = true;
            }

            items = items.Skip(current * maxContacts)
                               .Take(maxContacts)
                               .ToList();
        }

        List<string> descriptions;
        Dictionary<string, IReadOnlyList<string>> choices;

        descriptions = items.Select(item => _descriptionFunc(item)).ToList();

        choices = items.ToDictionary<T, string,
            IReadOnlyList<string>>(item => item.ToString(), item => _synonymFunc(item));

        if (addNext)
        {
            descriptions.Add("More");
            choices.Add("More", new List<string>() { "Next" });
        }

        if (addPrevious)
        {
            descriptions.Add("Previous");
            choices.Add("Previous", new List<string>() { "Previous", "Last", "Back" });
        }

        descriptions.Add("New Search");
        descriptions.Add("Cancel");

        choices.Add("New Search", new List<string>() { "None", "None of these", "Next Search", "Search Again", "Search" });
        choices.Add("Cancel", new List<string>() { "Stop", "Quit" });

        IPromptOptions<string> promptOptions = new PromptOptionsWithSynonyms<string>(_prompt, choices: choices, descriptions: descriptions,
            tooManyAttempts: DialogHelper.TooManyAttemptsText);
        PromptDialog.Choice(context, SelectItemAsync, promptOptions);
    }

    private async Task SelectItemAsync(IDialogContext context, IAwaitable<object> result)
    {
        try
        {
            string contactName = await result as string;

            if (contactName == "Cancel")
            {
                context.Done(new OptionsResult<T>(null, false, true));
            }
            else if (contactName == "New Search")
            {
                context.Done(new OptionsResult<T>(null, true));
            }
            else if (contactName == "More")
            {
                current++;
                SendContactOptions(context, _items);
            }
            else if (contactName == "Previous")
            {
                current--;
                SendContactOptions(context, _items);
            }
            else
            {
                T resultItem = _items.FirstOrDefault(c => c.ToString().ToLower() == contactName.ToLower());

                if (resultItem != null)
                {
                    DialogHelper.DeleteConversationItem(context, "searchContacts");
                    context.Done(new OptionsResult<T>(resultItem, false));
                }
                else
                {
                    context.Done(new OptionsResult<T>(null, false));
                }
            }
        }
        catch (TooManyAttemptsException)
        {
            context.Done(new OptionsResult<T>(null, false));
        }
    }
}


[Serializable]
public class OptionsResult<T>
    where T : class
{
    /// <summary>
    /// Result of the options
    /// </summary>
    public T Result { get; set; }

    /// <summary>
    /// Whether the parent dialog needs to search again
    /// </summary>
    public bool SearchAgain { get; set; }

    public bool IsCancelled { get; set; }


    public OptionsResult(T result, bool searchAgain, bool cancelled = false)
    {
        Result = result;
        SearchAgain = searchAgain;
        IsCancelled = cancelled;
    }
}

推荐阅读