背景
ChakraCoreという、C#から利用できるJavaScriptエンジンがあります。
WPFでユーザーインターフェースを作って、REPLにしてみましょう。
完成イメージ
JavaScriptを入力して実行(RUN)
ボタンを押すと結果を表示します。
分割代入が動くのが、(今の所)ChakraCoreならではです。
手順
VisualStudio Community 2015 Update1で新しいWPFプロジェクトを作ります。
ChakraCoreのdllを作る
ChakraCoreをビルドするを参考にしてビルドしてください。
ChakraCoreをC#から呼び出すと同様にプロジェクトに追加します。
NugetでReactivePropertyをインストールする
viewとviewModelをバインドするためにReactivePropertyを使います。
ReactivePropertyを使う理由はINotifyPropertyChanged実装のありえない面倒くささと、ReactivePropertyの信じられない素晴らしさを見てください。
MainWindowを作成する
コンポーネントを適当に配置します。
<Window x:Class="REPL.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:REPL"
mc:Ignorable="d"
Title="ChakraCore REPL"
Height="450"
Width="600" Closing="DisposeChakraRuntime">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox
Height="250"
Width="600"
AcceptsReturn="True"
TextWrapping="Wrap"
Text="{Binding Script.Value}"
VerticalScrollBarVisibility="Auto"
FontSize="20"
/>
<Button Click="Button_Click">実行(RUN)</Button>
<Button Click="ResetContext">初期化(Reset)</Button>
<TextBlock
TextWrapping="Wrap"
Text="{Binding Result.Value}"
/>
</StackPanel>
</Window>
ViewModelを作る
JavaScript文字列と結果文字列持つビューモデルを作ります。
public class ViewModel
{
public ReactiveProperty<string> Script { get; } = new ReactiveProperty<string>("(()=>{return \'Hello world!\';})()");
public ReactiveProperty<string> Result { get; } = new ReactiveProperty<string>();
}
初期値に"(()=>{return \'Hello world!\';})()
を入れます。
公式サンプルの真似です。
JavaScriptの実行部
Hosting
ChakraCoreをC#から呼び出すと同様に公式サンプルのHosting
ディレクトリをコピーします。
実行エンジンの初期化
JavaScriptRuntime.Create
メソッドでランタイムを作ります。
ランタイムのCreateContext
メソッドで実行コンテキストを作ります。
private JavaScriptRuntime runtime;
private ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
DataContext = vm;
InitChakraRuntime();
Run(runtime, vm);
}
private void InitChakraRuntime()
{
runtime = JavaScriptRuntime.Create();
JavaScriptContext.Current = runtime.CreateContext();
}
実行
事前に設定したJavaScriptContext
のRunScript
メソッドに文字列を与えます。
ConvertToString
メソッドとToString
メソッドで結果をC#の文字列に変換します。
private static void Run(JavaScriptRuntime runtime, ViewModel vm)
{
try
{
vm.Result.Value = JavaScriptContext.RunScript(vm.Script.Value)
.ConvertToString()
.ToString();
}
catch (JavaScriptScriptException e)
{
vm.Result.Value = e.Error
.ConvertToString()
.ToString();
}
}
イベントハンドラーを作る
ボタンのイベントハンドラーを設定します。
private void Button_Click(object sender, RoutedEventArgs e)
{
Run(runtime, vm);
}
その他の雑用
コンテキストを初期化し直すボタンをつけます。
グローバル変数の定義を消すために使います。
private void ResetContext(object sender, RoutedEventArgs e)
{
JavaScriptContext.Current = runtime.CreateContext();
}
Windowを閉じる時にランタイムを消します。
private void DisposeChakraRuntime(object sender, System.ComponentModel.CancelEventArgs e)
{
// Dispose runtime
JavaScriptContext.Current = JavaScriptContext.Invalid;
runtime.Dispose();
}
ソースコード
githubにあります。
感想
公式サンプルのHostingのJavaScriptRuntime.csがC#っぽいAPIを提供しています。
これを使うと公式サンプルよりスマートに書けて良い感じです。