Bot Framework v4 Web渠道下自适应卡片级联下拉菜单实现咨询
Hey Sanjeev, great question! Cascading dropdowns in Adaptive Cards for Bot Framework v4 on the Web channel are totally doable—they just require a mix of Adaptive Card design and bot-side logic since Adaptive Cards themselves don’t natively support cascading out of the box. Let me walk you through the core steps:
Step 1: Create the Initial Adaptive Card with the First Dropdown
Start by designing a card that includes your first dropdown (e.g., "Country"). This card will trigger a bot action when the user selects an option.
Here’s a sample JSON for the initial card:
{ "type": "AdaptiveCard", "version": "1.0", "body": [ { "type": "TextBlock", "text": "Select your Country", "weight": "Bolder", "size": "Medium" }, { "type": "Input.ChoiceSet", "id": "countrySelect", "choices": [ { "title": "USA", "value": "usa" }, { "title": "Canada", "value": "canada" }, { "title": "UK", "value": "uk" } ], "placeholder": "Choose a country" } ], "actions": [ { "type": "Action.Submit", "title": "Next", "data": { "action": "loadStates" } } ] }
Step 2: Handle the First Selection in Your Bot
When the user submits the first dropdown, your bot needs to capture the selected value, fetch the corresponding options for the second dropdown (e.g., "States" for the selected country), and send a new Adaptive Card with the second dropdown.
Example in C#:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var activityValue = turnContext.Activity.Value as JObject; if (activityValue != null && activityValue["action"]?.ToString() == "loadStates") { var selectedCountry = activityValue["countrySelect"]?.ToString(); // Fetch states based on country (replace with your data source) var stateChoices = GetStatesForCountry(selectedCountry); // Create a new Adaptive Card with state dropdown var stateCard = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0)) { Body = { new AdaptiveTextBlock { Text = $"Select your State in {selectedCountry}", Weight = AdaptiveTextWeight.Bolder, Size = AdaptiveTextSize.Medium }, new AdaptiveChoiceSetInput { Id = "stateSelect", Choices = stateChoices.Select(s => new AdaptiveChoice { Title = s, Value = s }).ToList() } }, Actions = { new AdaptiveSubmitAction { Title = "Submit", Data = new { action = "submitSelection" } } } }; await turnContext.SendActivityAsync(MessageFactory.Attachment(new Attachment { ContentType = AdaptiveCard.ContentType, Content = stateCard }), cancellationToken); return; } // Handle initial message or other actions await turnContext.SendActivityAsync(MessageFactory.Attachment(InitialCountryCard()), cancellationToken); } private List<string> GetStatesForCountry(string country) { switch(country) { case "usa": return new List<string> { "California", "Texas", "New York" }; case "canada": return new List<string> { "Ontario", "Quebec", "British Columbia" }; case "uk": return new List<string> { "England", "Scotland", "Wales" }; default: return new List<string>(); } }
Example in Node.js:
this.onMessage(async (context, next) => { const activityValue = context.activity.value; if (activityValue && activityValue.action === 'loadStates') { const selectedCountry = activityValue.countrySelect; const stateChoices = getStatesForCountry(selectedCountry); // Build state card JSON const stateCard = { type: "AdaptiveCard", version: "1.0", body: [ { type: "TextBlock", text: `Select your State in ${selectedCountry}`, weight: "Bolder", size: "Medium" }, { type: "Input.ChoiceSet", id: "stateSelect", choices: stateChoices.map(s => ({ title: s, value: s })) } ], actions: [{ type: "Action.Submit", title: "Submit", data: { action: "submitSelection" } }] }; await context.sendActivity({ attachments: [{ contentType: "application/vnd.microsoft.card.adaptive", content: stateCard }] }); return; } // Send initial country card await context.sendActivity({ attachments: [{ contentType: "application/vnd.microsoft.card.adaptive", content: initialCountryCard() }] }); await next(); }); function getStatesForCountry(country) { switch(country) { case 'usa': return ['California', 'Texas', 'New York']; case 'canada': return ['Ontario', 'Quebec', 'British Columbia']; case 'uk': return ['England', 'Scotland', 'Wales']; default: return []; } }
Step 3: Handle the Final Submission
Once the user selects the second dropdown and submits, your bot can capture both values and process them as needed. For example, in C#:
else if (activityValue["action"]?.ToString() == "submitSelection") { var selectedCountry = activityValue["countrySelect"]?.ToString(); // If you persisted this, or pass it in the submit data var selectedState = activityValue["stateSelect"]?.ToString(); await turnContext.SendActivityAsync(MessageFactory.Text($"You selected {selectedState}, {selectedCountry}!"), cancellationToken); }
Key Notes
- Persist Context: If you need to retain the first selection for the final submit, you can either store it in bot state (like UserState) or include it in the second card’s submit data (e.g., add
country: selectedCountryto the submit action’s data). - Web Channel Compatibility: This approach works seamlessly on the Web channel since it relies on standard Adaptive Card submit actions and bot-side processing—no special channel-specific hacks needed.
- Dynamic Data: Replace the hardcoded state lists with calls to your actual data source (API, database, etc.) for real-world use.
内容的提问来源于stack exchange,提问作者Sanjeev Gautam

