Custom fields with FormBuilder in the Microsoft Bot Framework

前端 未结 2 1910
青春惊慌失措
青春惊慌失措 2021-01-07 08:48

Using the AlarmBot Sample and the Improved Sandwich Bot, I\'m trying to understand how to combine the FormBuilder with custom dialog behavior. Specifically, I want to take t

相关标签:
2条回答
  • 2021-01-07 09:39

    Originally we used Chronic for DateTime fields in FormFlow, but when we moved to signing our dll we could no longer use the unsigned C# Chronic library. I have reached out to the author though to ask him to sign it, but have not heard back yet--if he does we will make it a part of FormFlow.

    As to implementing IField, if you want to you can derive from Field and supply your own implementation of IFieldState. Field has data structures for all the declarative stuff and you can override all the methods. This would allow you to use Chronic in your unsigned DLL.

    0 讨论(0)
  • 2021-01-07 09:46

    After looking and stepping through the source, the easiest answer isn't about the IFieldState. The key interface is IRecognize. Using it correctly requires understanding a little bit of what's going on under the covers.

    Start by creating your own custom field. The framework nicely allows us the derive from the FieldReflector which does most of the work.

    public class BetterDateTimeField : FieldReflector<MyOrder> 
    {
      public BetterDateTimeField(string name, bool ignoreAnnotations = false) 
              : base(name, ignoreAnnotations)  { }
    
       public override IForm<MyOrder> Form
       {
          set
          {
            base.Form = value;
            base.SetRecognizer(new BetterDateTimeRecognizer<MyOrder>(this, CultureInfo.CurrentCulture));
          }
        }
    }
    

    The main idea here is to create your own recognizer since that's the part that gets the raw input text. The trick is to know when you can create an instance of the recognizer. It has to be done AFTER the field Form has been set. The underlying Recognizer base class will look at the fields form in the constructor. (If you completely abandon using the base Recognizer classes this may not be an issue.)

    Next you can create your own custom IRecognize implementation. Unfortunately, the bot framework seals many of the basic/primitive type recognizer classes so deriving from RecognizeDateTime and overloading Parse isn't an option (hopefully someday they'll unseal them). However, it's easy enough to copy and edit into your own custom class.

    using Chronic;
    public class BetterDateTimeRecognizer<T> : RecognizePrimitive<T> where T : class
    {
       private CultureInfo _culture;
       private Parser _parser;
    
       public BetterDateTimeRecognizer(IField<T> field, CultureInfo culture) 
              : base(field)
       {
          _culture = culture;
          _parser = new Chronic.Parser();
       }
    
       public override string Help(T state, object defaultValue)
       {
         var prompt = new Prompter<T>(_field.Template(TemplateUsage.DateTimeHelp), _field.Form, null);
         var args = HelpArgs(state, defaultValue);
         return prompt.Prompt(state, _field.Name, args.ToArray());
    
       }
    
       public override TermMatch Parse(string input)
       {
         TermMatch match = null;
         // the original code
         //DateTime dt;
         //if (DateTime.TryParse(input, out dt))
         //{
         //    match = new TermMatch(0, input.Length, 1.0, dt);
         //}
    
         var parse = _parser.Parse(input);
         if (parse != null && parse.Start.HasValue)
         {
             match = new TermMatch(0, input.Length, 1.0, parse.Start.Value);
         }
    
         return match;
      }
    
      public override IEnumerable<string> ValidInputs(object value)
      {
         yield return ValueDescription(value);
      }
    
      public override string ValueDescription(object value)
      {
         return ((DateTime)value).ToString(CultureInfo.CurrentCulture.DateTimeFormat);
      }
    }
    

    Lastly, you just have to wire it up to your form builder in BuildForm():

    var form = builder.Message("Hello there. What can I help you today")
               .Field(new BetterDateTimeField("<NAME of YOUR DateTime Field HERE>")
               .Build();
    
    0 讨论(0)
提交回复
热议问题