Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
前回はダイアログ応用編として DialogPrompt の機能など紹介しました。今回はフォームフロー (FormFlow) 入門編をお届けします。
概要
前回 DialogPrompt を利用して、イベント作成に必要な情報を集めました。しかしフォームフローはこれをさらに簡単にしてくれます。フォームフローはクラスの定義を元に、適切なダイアログを作ってくれます。百読は一コードに如かずということで早速。
フォームフローを利用したイベントの作成
モデルの作成
フォームフローはモデルベースであるため、モデルを作ります。Microsoft.Graph.Event クラスをそのままモデルに使いたいんですが、Serializable 属性ないんですよね。あと不要なフィールドなどもあるので、新規作成を。
1. Visual Studio でボットアプリプロジェクトに Models フォルダを追加。
2. Models フォルダ内に、OutlookEvent.cs を追加し、以下と差し替え。Serializable 属性忘れないように。
using System;
namespace O365Bot.Models
{
[Serializable]
public class OutlookEvent
{
public string Subject { get; set; }
public string Description { get; set; }
public DateTime Start { get; set; }
public bool IsAllDay { get; set; }
public double Hours { get; set; }
}
}
CreateEventDialog.cs の変更
PromptDialog によるチェーンではなく、フォームフローを直接利用するようにします。以下のコードに差し替え。コード短くなりましたね。
using Autofac;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Graph;
using O365Bot.Models;
using O365Bot.Services;
using System;
using System.Threading.Tasks;
namespace O365Bot.Dialogs
{
[Serializable]
public class CreateEventDialog : IDialog<bool> // このダイアログが完了時に返す型
{
public async Task StartAsync(IDialogContext context)
{
// FormFlow でダイアログを作成して、呼び出し。
var outlookEventFormDialog = FormDialog.FromForm(this.BuildOutlookEventForm, FormOptions.PromptInStart);
context.Call(outlookEventFormDialog, this.ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<OutlookEvent> result)
{
await context.PostAsync("イベントを作成しました。");
// ダイアログの完了を宣言
context.Done(true);
}
private IForm<OutlookEvent> BuildOutlookEventForm()
{
OnCompletionAsyncDelegate<OutlookEvent> processOutlookEventCreate = async (context, state) =>
{
using (var scope = WebApiApplication.Container.BeginLifetimeScope())
{
IEventService service = scope.Resolve<IEventService>(new TypedParameter(typeof(IDialogContext), context));
// TimeZone は https://graph.microsoft.com/beta/me/mailboxSettings で取得可能だがここでは一旦ハードコード
Event @event = new Event()
{
Subject = state.Subject,
Start = new DateTimeTimeZone() { DateTime = state.Start.ToString(), TimeZone = "Tokyo Standard Time" },
IsAllDay = state.IsAllDay,
End = state.IsAllDay ? null : new DateTimeTimeZone() { DateTime = state.Start.AddHours(state.Hours).ToString(), TimeZone = "Tokyo Standard Time" },
Body = new ItemBody() { Content = state.Description, ContentType = BodyType.Text }
};
await service.CreateEvent(@event);
}
};
return new FormBuilder<OutlookEvent>()
.Message("イベントを作成します。")
.AddRemainingFields() // すべてのフィールドを処理対象として追加
.OnCompletion(processOutlookEventCreate)
.Build();
}
}
}
エミュレーターによる検証
エミュレータで実行すると、以下のような感じになります。
色々文言や質問の順番がきれいではありませんが、動くのは動きますね。ではこの辺直してみましょう。
表示の改善
モデルの変更
ユーザーに対する質問や順番はモデルをいじることで変更できます。モデルをいじらない方法は次回。
モデルを以下のように変更します。属性ですべてを制御できます。また {||} のようなパターンも使えます。使えるパターンはこちら。
using Microsoft.Bot.Builder.FormFlow;
using System;
namespace O365Bot.Models
{
[Serializable]
public class OutlookEvent
{
[Prompt("件名は?")]
public string Subject { get; set; }
[Prompt("詳細は?")]
public string Description { get; set; }
[Prompt("いつから?yyyy/MM/dd HH:mm 形式で入力してください。")]
public DateTime Start { get; set; }
[Prompt("終日イベント?{||}")]
public bool IsAllDay { get; set; }
[Prompt("何時間?")]
public double Hours { get; set; }
}
}
エミュレーターによる検証
前回と同じ感じになりました。
チェックインしてテストが通るかも確認してください。(この時点では失敗するテストがあるんですが、解決は次回)
まとめ
フォームフロー簡単すぎて拍子抜けしますが、次回は応用編ということでより高度な処理について紹介します。