3

I am currently trying to create an adaptive card in a Waterfall Dialog for one of my bots that will display the name and search item (both strings) when rendered. Both of the values I want to use are stored in the Context.Activity.Value property of my dialog, so all I need to know is how to insert those values into my adaptive card at some point during its creation so that the "text" values of the text blocks can contain my values.

I have looked into using empty JSON objects in the adaptive card schema that I could fill somehow during the adaptive card's creation, but have not figured out how to insert said values. I'm a relative beginner with C# and Bot Framework, so I don't know what to try.

Below is the step in my Waterfall Dialog where the adaptive card is made:

private async Task<DialogTurnResult> AdaptiveCardTest(WaterfallStepContext stepContext, 
CancellationToken cancellationToken)
        {
            var introCard = File.ReadAllText("./Content/AdaptiveCardTest.json");

            var card = AdaptiveCard.FromJson(introCard).Card;
            var attachment = new Attachment(AdaptiveCard.ContentType, content: card);

            var response = MessageFactory.Attachment(attachment, ssml: card.Speak, 
            inputHint: InputHints.AcceptingInput);

            await stepContext.Context.SendActivityAsync(response);

            return await stepContext.NextAsync();
        }

AdaptiveCardTest.json is the adaptive card's json file. At the moment it just has an image popup with some text which includes placeholders where I would like the user name and search item to go. The placeholder links are there because the actual links are ridiculously long.

{
    "type": "AdaptiveCard",
    "id": "NewUserGreeting",
    "backgroundImage": "image_url_placeholder"
    "body": [
        {
            "type": "Container",
            "items": [
                {
                    "type": "Image",
                    "url": "image_url_placeholder_2"",
                    "size": "Stretch"
                }
            ]
        },


        {
            "type": "Container",
            "spacing": "None",
            "backgroundImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAXCAIAAACAiijJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAqSURBVDhPY1RgL2SgDDBBaQrAqBEIMGoEAowagQCjRiDAqBEIQLERDAwAIisAxhgAwtEAAAAASUVORK5CYII=",
            "items": [
                {
                    "type": "TextBlock",
                    "id": "title",
                    "spacing": "Medium",
                    "size": "Large",
                    "weight": "Bolder",
                    "color": "Light",
                    "text": "Hi, I'm **your** Virtual Assistant",
                    "wrap": true
                },
                {
                    "type": "TextBlock",
                    "id": "body",
                    "size": "Medium",
                    "color": "Light",
                    "text": "The user {{Name}} would like to know more about {{SearchItem}}.",
                    "wrap": true
                }
            ]
        }
    ],

}

Any help would be greatly appreciated, thank you!

slugster
  • 49,403
  • 14
  • 95
  • 145
Narmanino
  • 33
  • 1
  • 3

2 Answers2

1

For your simple scenario I would go with @MikeP's suggestion. In the future if you want to do something more complex where a template won't suffice, then you can build the Adaptive Card dynamically using the .NET SDK once you have installed the AdaptiveCard NuGet package.

The documentation on the .NET SDK is pretty limited but the properties of the AdaptiveCard object usually align with their JSON counterparts.

An example is:

const string ISO8601Format = "yyyy-MM-dd";
string text = "dynamic-text-here;

DateTime today = DateTime.Today;
string todayAsIso = today.ToString(ISO8601Format);

// Create card
AdaptiveCard adaptiveCard = new AdaptiveCard("1.0")
{
    Body =
    {
        new AdaptiveContainer
        {
            Items =
            {
                new AdaptiveTextBlock
                {
                    Text = question,
                    Wrap = true
                },
                new AdaptiveDateInput
                {
                    // This Id matches the property in DialogValueDto so it will automatically be set
                    Id = "UserInput",
                    Value = todayAsIso,
                    Min = today.AddDays(-7).ToString(ISO8601Format),
                    Max = todayAsIso,
                    Placeholder = todayAsIso
                }
            }
        }
    },
    Actions = new List<AdaptiveAction>
    {
        new AdaptiveSubmitAction
        {
            // Data can be an object but this will require the value provided for the 
            // Content property to be serialised it to a string
            // as per this answer https://stackoverflow.com/a/56297792/5209435
            // See the attachment block below for how this is handled
            Data = "your-submit-data",
            Title = "Confirm",
            Type = "Action.Submit"
        }
    }
};

// Create message attachment
Attachment attachment = new Attachment
{
    ContentType = AdaptiveCard.ContentType,
    // Trick to get Adapative Cards to work with prompts as per https://github.com/Microsoft/botbuilder-dotnet/issues/614#issuecomment-443549810
    Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(adaptiveCard))
};

cardActivity.Attachments.Add(attachment);

// Send the message
context.SendActivityAsync(cardActivity);

Because Items and Actions are collections, you could have conditional logic inside your code to build up these collections based on some condition at runtime then pass the build collection to Items or Actions which will allow you more flexibility than having a JSON template that you replace placeholder tokens at a known location.

Matt Stannett
  • 2,700
  • 1
  • 15
  • 36
  • This is great, I'll definitely be trying it out. Thanks! – Narmanino Jun 18 '19 at 17:35
  • Since you’ll probably run into the same issues as I did regard retrieving adaptive card submit values I’ll leave [this answer](https://stackoverflow.com/a/56626814) here and if you’re running an adaptive card inside a waterfall but want it to function as a prompt I.e capture user input then you’ll need to refer to [this question and answer plus the links in it](https://stackoverflow.com/q/56588668/5209435) (some light reading haha.) – Matt Stannett Jun 18 '19 at 18:46
  • I have indeed! Im using mdrichardson's fix from here: https://stackoverflow.com/questions/56004289/botframework-how-to-capture-extract-the-values-submitted-through-adaptive-car/56010355#56010355. I still am not getting the text input to go through into the handleresponseasync. Any ideas? – Narmanino Jun 18 '19 at 19:36
  • Two options: 1) Instead of setting the `Text` property on the `postbackActivity` try setting it directly on `dc.Context.Activity.Text`, that way you might be able to avoid having this line `await dc.Context.SendActivityAsync(postbackActivity);` 2) Check inside your `OnEventAsync` method in MainDialog.cs and make sure that there is nothing preventing your code from falling through to the `if (forward)` then actually executing the `dc.ContinueDialogAsync()` call - I had this issue so it would be worth putting a debugger in here and stepping through when submitting your adaptive card. – Matt Stannett Jun 18 '19 at 21:06
0

This is something that I have done in the past using Handlebars which is a nice way of replacing tokens in your adaptive card JSON with properties from a model. Just make sure the tokens in your adaptive card JSON match the model properties

Have a look at their site for more detail but it is just a case of doing:

Handlebars.Compile(<Adaptive card template>);

Handlebars is available as a Nuget package you can add into your project.

MikeP
  • 117
  • 10