CHx Xq
CHx Xq

Reputation: 25

Bot Framework Formflow Dialog with list?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using BotVetAlpha3.Core;
using Microsoft.Bot.Builder.FormFlow;

namespace BotVetAlpha3.Dialog
{
public enum SandwichOptions
{
    BLT, BlackForestHam, BuffaloChicken, ChickenAndBaconRanchMelt, ColdCutCombo, MeatballMarinara,
    OvenRoastedChicken, RoastBeef, RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna,
    TurkeyBreast, Veggie
};
public enum LengthOptions { SixInch, FootLong };
public enum BreadOptions { NineGrainWheat, NineGrainHoneyOat, Italian, ItalianHerbsAndCheese, Flatbread };
public enum CheeseOptions { American, MontereyCheddar, Pepperjack };
public enum ToppingOptions
{
    Avocado, BananaPeppers, Cucumbers, GreenBellPeppers, Jalapenos,
    Lettuce, Olives, Pickles, RedOnion, Spinach, Tomatoes
};
public enum SauceOptions
{
    ChipotleSouthwest, HoneyMustard, LightMayonnaise, RegularMayonnaise,
    Mustard, Oil, Pepper, Ranch, SweetOnion, Vinegar
};




[Serializable]
public class RootDialog
{

    public SandwichOptions? Sandwich;
    public LengthOptions? Length;
    public BreadOptions? Bread;
    public CheeseOptions? Cheese;
    public List<ToppingOptions> Toppings;
    public List<SauceOptions> Sauce;

    public static IForm<RootDialog> BuildForm()
    {
        return new FormBuilder<RootDialog>()
                .Message("Welcome to the simple sandwich order bot!")
                .Build();
    }
};

}

So this is my current class from an example of MS but I want to change it I have been trying to do it but Im not being able... What I want to do is instead of using enum to build my dialog I want to use List of strings . Is that possible ? If it is all help is welcome I have been banging on the wall with this... Finding information on this subject is also very hard.

Upvotes: 0

Views: 1619

Answers (2)

Paweł
Paweł

Reputation: 280

I'm going to go out on a limb and assume that you also want to specify those strings dynamically? well let's run down a little journey together.

first let's start by defining a Form

using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Bot.Builder.FormFlow.Advanced;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace pc.Bot.Form
{
    [Serializable]
    public class MyForm
    {
        public MyForm(List<string> Names) { _names = Names; }

        List<string> _names;

       [Template(TemplateUsage.NotUnderstood, "**{0}** isn't a valid selection",  ChoiceStyle = ChoiceStyleOptions.PerLine)]
       [Prompt("**Choose from the following names**:  {||}")]
       public List<string> Names { get; set; }

       public static IForm<MyForm> BuildForm() {
            return new FormBuilder<MyForm>()
             .Field(new FieldReflector<MyForm>(nameof(Names))
                .SetType(null)
                .SetActive(form => form._names != null && form._names.Count > 0)
                .SetDefine(async (form, field) =>
                {
                    form?._names.ForEach(name=> field.AddDescription(name, name).AddTerms(name, name));

                    return await Task.FromResult(true);
                }))
            .Build();
        }
    }
}

Now notice that our form is serializable and has a constructor that takes in a list of strings, then in our BuildForm static function we add our Names field and dynamically populate it.

now let's take a look at our dialog

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using pc.Bot.Form;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace pc.Bot.Dialogs
{
    [Serializable]
    public class RootDialog : IDialog<object>
    {
        public async Task StartAsync(IDialogContext context)
        {
            context.Wait(MessageReceivedAsync);

            await Task.CompletedTask;
        }

        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
        {
            var names = new List<string>() { "Pawel", "Tomek", "Marin", "Jakub", "Marco" };
            var form = new FormDialog<MyForm>(new MyForm(names), MyForm.BuildForm, FormOptions.PromptInStart);

            context.Call(form, Form_Callback);

            await Task.CompletedTask;
        }

       private async Task Form_Callback(IDialogContext context, IAwaitable<MyForm> result)
       {
           var formData = await result;

           //output our selected names form our form
           await context.PostAsync("You selected the following names:");
           await context.PostAsync(formData.Names?.Aggregate((x, y) => $"{x}, {y}") ?? "No names to select" );
       }
   }
}

now in our dialog when we instantiate our form in the constructor of the form dialog class we simply pass in our names list. If you didn't want to pass your list in from your dialog, then you could just set the _names field in the constructor of your form.

i'm sure you've moved on with your life since the original question, but if someone else comes across this post, this might help them.

One caveat, this is my second week of working with the botframework, so if this is some horrible practice or has some grave consequences, please give me a heads up before i go to production.

Upvotes: 3

Dmytro Zhluktenko
Dmytro Zhluktenko

Reputation: 160

I guess I'm late, but I've got an answer! FormFlow Dialog doesn't allow you to pass List as field to collect in conversation with user :(

Allowed types:

  • Integral – sbyte, byte, short, ushort, int, uint, long, ulong
  • Floating point - float, double
  • String
  • DateTime
  • Enum
  • List of enum

Source

P.S. Recently I faced the same problem: my questions were stored in external database and I wanted bot to ask them, but there is no clear way to do it :( Anyway, requesting expert help for this question!

Upvotes: 0

Related Questions