Adaptivecards: Extensibility and custom renderers

Created on 2 Jul 2019  路  4Comments  路  Source: microsoft/AdaptiveCards

Platform

  • JavaScript
  • UWP

Author or host

Both

Version of SDK

1.0 & 1.2
(UWP host 1.2, Javascript host 1.0, C# author (bot framework) 1.0

Issue

We are trying to handle custom rendering in our hosts, but feel that the extensibility documentation is not good enough to get an example working.
What we are essentially doing is that we want to render a custom control for the Input.ChoiceSet and items.
Whithin the card we also have a Action.SubmitAction, when fired we want the values of our custom element to be given to the page "controller".

Example below of how we are currently trying to get it working:
Registering of custom renderer:

_renderer = new AdaptiveCardRenderer();
_renderer.ElementRenderers.Set("Input.ChoiceSet", new ComboBoxAdaptiveCardRenderer());

Custom renderer:

public class ComboBoxAdaptiveCardRenderer : IAdaptiveElementRenderer
    {
        public UIElement Render(IAdaptiveCardElement element, AdaptiveRenderContext context, AdaptiveRenderArgs renderArgs)
        {
            var options = element as AdaptiveChoiceSetInput;
            var comboBox = new ComboBox() { ItemsSource = options.Choices };
            var fontSizeFactor = 2 * SettingsHelper.GetFontSizeFactor();
            var familyName = FontFamilyLookUp.Instance.LookUp("Roboto-Regular.ttf#Roboto#Roboto-Regular");
            var fontFamily = new Windows.UI.Xaml.Media.FontFamily(familyName);

            comboBox.BorderBrush = new SolidColorBrush(Colors.Black);
            comboBox.BorderThickness = new Windows.UI.Xaml.Thickness(1);

            comboBox.FontFamily = fontFamily;
            comboBox.FontSize = (double)FontSizeLookUp.Instance.LookUp(fontSizeFactor);

            return comboBox;
        }
    }

Action:

private void AdaptiveCardAction(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args)
{
     // Here we want to consume the selected choice of the ComboBox UI Element. Neither in args.Action.
     var inputs = args.Inputs.AsJson();
     _viewModel.PostAnswer(inputs.ToString());
}

Card json:

{
  "type": "AdaptiveCard",
  "version": "1.0",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "size": "extraLarge",
          "weight": "bolder",
          "text": "Some text block text",
          "wrap": true
        },
        {
          "type": "TextBlock",
          "weight": "bolder",
          "text": "Some Text block text",
          "wrap": true
        }
      ],
      "separator": true
    },
    {
      "type": "Container",
      "items": [
        {
          "type": "TextBlock",
          "size": "medium",
          "weight": "bolder",
          "text": "Some Text block text",
          "wrap": true
        },
        {
          "type": "TextBlock",
          "size": "medium",
          "text": "Choose a number below",
          "wrap": true
        },
        {
          "type": "Input.ChoiceSet",
          "id": "5b5fdf01-8c85-4a04-b83d-b14dd115511f",
          "style": "compact",
          "isMultiSelect": false,
          "choices": [
            {
              "title": "0",
              "value": "0"
            },
            {
              "title": "1",
              "value": "1"
            },
            {
              "title": "2",
              "value": "2"
            },
            {
              "title": "3",
              "value": "3"
            },
            {
              "title": "4",
              "value": "4"
            },
            {
              "title": "5",
              "value": "5"
            },
            {
              "title": "6",
              "value": "6"
            },
            {
              "title": "7",
              "value": "7"
            },
            {
              "title": "8",
              "value": "8"
            },
            {
              "title": "9",
              "value": "9"
            },
            {
              "title": "10",
              "value": "10"
            }
          ]
        }
      ],
      "separator": true
    }
  ],
  "actions": [
    {
      "type": "Action.Submit",
      "title": "Send"
    }
  ]
}

I understand that this might not be implemented as intended, but the problem is that there is no documentation, good enough, for how to handle it correctly.
We're in a trial and error kind of state at the moment. Is it possible to get some hints of how to properly render and data bind to a custom control?
Neihter do we feel that the native styling section in documentation is quite satisfactory. What values and properties can you actually use/modify?

Question no-recent-activity

Most helpful comment

@bandmask, I agree that the documentation leaves something to be desired, and looking at the documentation github it looks like there is an issue tracking improvement here (https://github.com/MicrosoftDocs/AdaptiveCards/issues/93).

In the meantime, I can get you pointed in the right direction. The key thing you're missing in order to get the input collected is the AdaptiveRenderContext.AddInputValue method. This method allows you to add an object that can report the current value of the input when called back. You should create an object that implements IAdaptiveInputValue and call AdaptiveRenderContext.AddInputValue as part of your implementation of Render(). Let me know if you have follow up questions on how to make this work.

As for native styling, the Xaml values that you set in your style will be applied to the corresponding Xaml type directly by Xaml. For the text block case, for example, the settable properties are the properties on Windows.UI.Xaml.Controls.TextBlock. What's missing from the documentation is the mapping of AdaptiveCard types to xaml types so that you know the appropriate Xaml type to set as the TargetType. I logged https://github.com/MicrosoftDocs/AdaptiveCards/issues/196 to update. Let me know if you have a specific element you're looking to style and I can let you know what type to target.

All 4 comments

Thanks for bringing this up @bandmask esp the bit about the documentation feedback as well. Are you actually running into explicit errors at this point or is it just approaching our extensibility story and looking for general guidance here? What documentation have you been working with again? Is it linked out of https://docs.microsoft.com/en-us/adaptive-cards/rendering-cards/extensibility ? Also could you elaborate on "native styling section in documentation" - what page are you referring to again?

Also @matthidinger / @andrewleader are one of the source/community renderers we have in our repo something that they can leverage while we try to understand the sample and documentation gaps? Maybe the newer FabricUI renderer could serve as an example @dfisenko ?

Thank you for answering @shalinijoshi19
We've been experimenting with the UWP rendering extensibility, found here https://docs.microsoft.com/en-us/adaptive-cards/sdk/rendering-cards/uwp/extensibility
The native styling I referred to is https://docs.microsoft.com/en-us/adaptive-cards/sdk/rendering-cards/uwp/native-styling

The extensibility page exists, but the example is only a simple TexBlock implementation, which works well, but for collecting input with a custom input, the documentation has a lot more information to give.
The native styling does not give you any information of what values that are actually possible to use. Background won't get you that far.

As of now, we have, through a lot of trial and error, managed to encapsulate our own implementation of a xamarin/UWP dropdown from an AdaptiveCoiceSet. Although, this is done by also implementing our own ActionRenderer where we can manually extract the values from the custom inputs, but this seems highly overcomplicated.
Is there some way where we could e.g. data bind to the custom control that adheres better to the framework or use any existing feature for easier implementations?

@bandmask, I agree that the documentation leaves something to be desired, and looking at the documentation github it looks like there is an issue tracking improvement here (https://github.com/MicrosoftDocs/AdaptiveCards/issues/93).

In the meantime, I can get you pointed in the right direction. The key thing you're missing in order to get the input collected is the AdaptiveRenderContext.AddInputValue method. This method allows you to add an object that can report the current value of the input when called back. You should create an object that implements IAdaptiveInputValue and call AdaptiveRenderContext.AddInputValue as part of your implementation of Render(). Let me know if you have follow up questions on how to make this work.

As for native styling, the Xaml values that you set in your style will be applied to the corresponding Xaml type directly by Xaml. For the text block case, for example, the settable properties are the properties on Windows.UI.Xaml.Controls.TextBlock. What's missing from the documentation is the mapping of AdaptiveCard types to xaml types so that you know the appropriate Xaml type to set as the TargetType. I logged https://github.com/MicrosoftDocs/AdaptiveCards/issues/196 to update. Let me know if you have a specific element you're looking to style and I can let you know what type to target.

This issue has been automatically marked as stale because it has not had any activity for 5 days.

Was this page helpful?
0 / 5 - 0 ratings