using System ;
using System.Collections.Generic ;
using Windows.UI.Xaml ;
using Windows.UI.Xaml.Controls ;
using AdaptiveCards.Rendering.Uwp ;
using Monaco ;
using Monaco.Helpers ;
using Newtonsoft.Json ;
// probs don't need these
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices.WindowsRuntime ;
using System.Threading.Tasks ;
using Windows.Foundation ;
using Windows.Foundation.Collections ;
using Windows.UI.Xaml.Controls.Primitives ;
using Windows.UI.Xaml.Data ;
using Windows.UI.Xaml.Input ;
using Windows.UI.Xaml.Media ;
using Windows.UI.Xaml.Navigation ;
using Monaco.Editor ;
using Monaco.Languages ;
using Newtonsoft.Json.Linq ;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace Adaptive_Card_Editor_UWP
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
/// <summary>
/// Create DispatcherTimer for handling input delay logic
/// </summary>
public DispatcherTimer keyTimer = new DispatcherTimer ( ) ;
/// <summary>
/// timestamp in ticks (hundred nanoseconds) of last captured KeyUp event
/// </summary>
public long lastKeyUp ;
/// <summary>
/// boolean indicating whether input has changed since last tick
/// </summary>
public bool isRendered ;
/// <summary>
/// timer interval in ticks (hundred nanoseconds)
/// </summary>
public long interval = 5 0 0 0 0 0 0 ;
/// <summary>
/// list of known entities that contain templated content
/// </summary>
/// <remarks>
/// If the data referenced in the container doesn't exist, the container shouldn't be included in the rendered output
/// </remarks>
public List < string > Containers = new List < string >
{
"ActionSet" ,
"Container" ,
"ColumnSet" ,
"Column" ,
"FactSheet" ,
"Fact" ,
"ImageSet"
} ;
public MainPage ( )
{
// set up a window timer for handling the keyup delay
TimerSetup ( ) ;
this . InitializeComponent ( ) ;
}
/// <summary>
/// Sets up a window timer with a 500ms tick interval and starts the timer
/// </summary>
public void TimerSetup ( )
{
// on every timer tick, run TimerTick()
keyTimer . Tick + = TimerTick ;
// set the timer interval to half a second
keyTimer . Interval = TimeSpan . Parse ( "00:00:00.05" ) ;
// start the timer
keyTimer . Start ( ) ;
}
/// <summary>
/// fires when txtInput sees keyboard input
/// </summary>
private void txtInput_KeyDown ( CodeEditor sender , WebKeyEventArgs e )
{
// last key up event = integer value of current time
lastKeyUp = DateTime . Now . Ticks ;
// user is inputting text, so we're going to rerender
isRendered = false ;
}
/// <summary>
/// fires when txtData sees keyboard input
/// </summary>
private void txtData_KeyDown ( CodeEditor sender , WebKeyEventArgs args )
{
// last key up event = integer value of current time
lastKeyUp = DateTime . Now . Ticks ;
// user is inputting text, so we're going to rerender
isRendered = false ;
}
/// <summary>
/// Fires when the timer ticks
/// </summary>
void TimerTick ( object sender , object args )
{
// if isRendered is true, there have been no changes to input since the last tick
if ( isRendered )
{
return ;
}
// otherwise, we done got input, so do the thing
else
{
if ( DateTime . Now . Ticks > = lastKeyUp + interval )
{
// we done got some input! let's render it.
// we don't care what the input is; if it's not valid JSON ignore this keystroke
// render the text as a plain textbox
TextBlock txtOutput = new TextBlock ( ) ;
txtOutput . TextWrapping = TextWrapping . Wrap ;
txtOutput . Padding = new Thickness ( 1 0 ) ;
txtOutput . Text = txtInput . Text ;
// clear the grid of existing content
grdCard . Children . Clear ( ) ;
// render an adaptive card
try
{
AdaptiveCardRenderer cardRenderer = new AdaptiveCardRenderer ( ) ;
AdaptiveCardParseResult parsedCard = AdaptiveCard . FromJsonString ( txtInput . Text ) ;
RenderedAdaptiveCard theCard = cardRenderer . RenderAdaptiveCard ( parsedCard . AdaptiveCard ) ;
grdCard . Children . Add ( theCard . FrameworkElement ) ;
}
catch ( Exception ex )
{
// this means bad data was ingested by the adaptive card renderer
// so just display the exception details
txtOutput . Text = ex . ToString ( ) ;
grdCard . Children . Add ( txtOutput ) ;
}
// render a card using template and data
try
{
string Template = txtInput . Text ;
string Data = txtData . Text ;
string Rendered = JsonFromTemplate ( Template , Data ) ;
// render the card from the rendered template + data
AdaptiveCardRenderer cardRenderer = new AdaptiveCardRenderer ( ) ;
AdaptiveCardParseResult parsedCard = AdaptiveCard . FromJsonString ( Rendered ) ;
RenderedAdaptiveCard theCard = cardRenderer . RenderAdaptiveCard ( parsedCard . AdaptiveCard ) ;
grdTemplated . Children . Add ( theCard . FrameworkElement ) ;
}
catch ( Exception ex )
{
// this means bad data was ingested by the adaptive card renderer
// so just display the exception details
txtOutput . Text = ex . ToString ( ) ;
grdTemplated . Children . Add ( txtOutput ) ;
}
grdCard . UpdateLayout ( ) ;
isRendered = true ;
}
else
{
return ;
}
}
}
/// <summary>
/// Creates an adaptive card JSON payload from a template and data model. Follows the official templating language.
/// </summary>
/// <param name="Template">JSON adaptive card template</param>
/// <param name="Data">JSON data model</param>
/// <returns>JSON string to be ingested by adaptive card renderer</returns>
public string JsonFromTemplate ( string strTemplate , string strData )
{
string output = "" ;
// first create JSON objects out of the input
JObject Template = JObject . Parse ( strTemplate ) ;
JObject Data = JObject . Parse ( strData ) ;
return output ;
}
}
}