【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

コンソールのエミュレーション

n7shi2009-07-13


Console.WriteLine()やConsole.ReadLine()で入出力を行うコンソールアプリケーションをSilverlightに移植するため、コンソールをエミュレートしてみました。サンプルの電卓プログラムです。 ⇒ 動作確認とソース

Silverlightにデフォルトでフォーカスを与えることができないため、入力する前にクリックするようになっています。コメントで紹介していただいた方法(id:beta_magnus:20090806:1249550117)でフォーカスが与えられました。

構造を簡単にするため、TextBoxをログ出力用(textBox1)とコマンド入力用(textBox2)に分けます。出力はtextBox1に文字を追加するだけです。

public static void Write(string value)
{
    textBox1.SelectionStart = TextBox.Text.Length;
    textBox1.SelectedText = value;
}

SilverlightのTextBoxにはAppendText()がないためこのような回りくどいことをしています。

入力は少し厄介です。ReadLine()の中でイベントを回して、textBox2で[Enter]が入力されたら戻るようにしたいのですが、イベントを明示的に回す方法がありません。そのため以下のような構造にしてみました。

  1. コンソールアプリケーションのコードはサブスレッドで回す
  2. ReadLine()はMonitor.Wait()で待機
  3. UIスレッドで[Enter]を検出したらMonitor.PulseAll()を送る
  4. サブスレッド対応のためWrite()でDispatcherを使う
private static object mutex = new object();
private static string line;

public static void ReadLine()
{
    string ret = null;
    lock (mutex)
    {
        Monitor.Wait(mutex);
        ret = line;
    }
    return ret;
}

private static void textBox2_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.Key)
    {
        case Key.Enter:
            lock (mutex)
            {
                line = textBox2.Text;
                textBox2.Text = "";
                Monitor.PulseAll(mutex);
            }
            e.Handled = true;
            break;
    }
}

public static void Write(string value)
{
    textBox1.Dispatcher.BeginInvoke(() =>
    {
        textBox1.SelectionStart = TextBox.Text.Length;
        textBox1.SelectedText = value;
    });
}

Consoleクラスの代替となるようにインターフェースを合わせたり、履歴機能を加えたりしたものが冒頭のサンプルです。Program.csを単独でコンパイルすればコンソールアプリケーションとして動きます。