WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

MacでC#を書いてみよう(GUI編)後編

今回のエンジニアブログ担当の岩原です。
今回は、「MacでC#を書いてみよう(GUI編)後編」です。
初めて見る方は、前回の記事を先にご覧ください。

Xamarin Studioがメジャーバージョンアップし、F#がつかえるようになりましたね。
アップデートしておきましょう。

では、前回の続きです。
まずはお天気Webサービス仕様 - Weather Hacks - livedoor 天気情報を開き、JSONの仕様を確認します。

次に、ここを開き
自分の住んでいる地域のcityのidを確認します。
今回は名古屋の天気を取得します。名古屋は230010です。

したがって、名古屋の天気をJSONで取得する場合、
http://weather.livedoor.com/forecast/webservice/json/v1?city=230010
になります。

今度は、JSON文字列をクラス化します。
愚直にクラス化すると面倒なので、json2csharp - generate c# classes from jsonを使用します。
JSON文字列をコピーし、「Generate」ボタンをクリックすれば、クラス化されたソースが取得できます。

ここで取得したソースとJson.NETを使えば、お手軽にパースできます。

まずは、生成されたソースをコピーします。
コピーしたソースをC#ファイルとして貼り付けます。
RootObjectではわかりづらいので、クラス名をWeatherInfoに変更します。
C#では、プロパティの先頭は基本的に大文字にすべきなので、WeatherInfoと他のクラスのプロパティもすべて先頭大文字に変更します。

次にボタンのイベントを作成します。

デザイナタブをクリックしてデザイナモードにします。
ここで画面右側にプロパティが表示されていない場合、Visual Designを表示させます。
画面上部のビュー→Visual Designの順にクリックすると表示されます。
02_designerMode

その後、ボタンを選択、シグナルタブの「Button Signals」を展開し、「Clicked」に「onbtnGetList」と入力します。
すると、エディタにメソッドが追加されているので、それに処理をゴリゴリ書いていきます。
01_AddEvent

では、まずは取得処理。
JSONのURLは定数化しておきましょう。

取得処理は別メソッド化します。
このメソッドは別スレッドから呼ばれるメソッドになるため、画面部品は触らないようにします。

   private String GetWheatherInfo ()
    {  
        //JSON_URLはJsonを取得するURL定数  
        var req = WebRequest.Create (JSON_URL);  
        var res = req.GetResponse ();  
  
        using (Stream st = res.GetResponseStream ()) {
            using (StreamReader sr = new StreamReader (st, Encoding.UTF8)) {  
                string json = sr.ReadToEnd ();  
                return json;  
            }  
        }  
    }  

では、上記メソッドの呼び出し側を書いていきます。
せっかくなので、Taskクラスを使って非同期に書いていきます。

   protected void onbtnGetList (object sender, EventArgs e)
    {  
        btnGetList.Sensitive = false;  
        //別スレッドで実行するようにスケジューリングする  
        var task = Task.Factory.StartNew (() => {  
            String json = GetWheatherInfo ();  
  
            WeatherInfo info = JsonConvert.DeserializeObject<WeatherInfo> (json);  
  
            //タイトル  
            string text = info.Title;  
            String newLine = System.Environment.NewLine;  
            text += newLine ;  
            foreach (var item in info.Forecasts) {  
                //いつ  
                text += newLine + item.DateLabel;  
  
                //お天気  
                text += newLine + item.Telop;  
                //温度は取得できたら表示する  
                if (item.Temperature != null) {  
                    if (item.Temperature.Max != null) {  
                        text += newLine + "最高気温:" + item.Temperature.Max.Celsius + "℃";  
                    }  
                    if (item.Temperature.Min != null) {  
                        text += newLine + "最低気温:" + item.Temperature.Min.Celsius + "℃";  
                    }  
                }  
                text += newLine;  
            }  
            text += newLine + info.Description.Text;  
            return text;  
        });  
  
        //取得完了後、UIスレッドで実行するようにスケジューリングする  
        var taskScheduler = TaskScheduler.Current;  
        task.ContinueWith (t => {  
            tvJson.Buffer.Text = t.Result;  
            btnGetList.Sensitive = true;  
        }, taskScheduler);  
  
    }  

先頭に以下のusingを追加しておきます。

using Newtonsoft.Json;  
using System.Threading.Tasks;  

ポイントとしては2点あります。
1点目は取得処理は別スレッドから呼んでいること。
ネットワークを経由する処理なので、ある程度時間がかかることも予想されます。
したがって、UIスレッドで呼ぶとフリーズしているように見えてしまいます。
コレを防ぐため、Taskで別スレッドから呼ぶようにしています。

2点目は、ContinueWithメソッドをUIスレッドで実行するようにしていること。
基本的に、ボタンやテキストビューを触るときはメインスレッドで行うようにしましょう。
Gtk#では、別スレッドで行ってもExceptionは発生しませんが、いつエラー扱いになるかわからないため、
お作法に従います。

TaskScheduler.Currentで現在実行しているタスクのスケジューラを取得しています。
本来ならばTaskScheduler. FromCurrentSynchronizationContextのほうが良いのですが、
なぜか取得できないので、代わりに呼び出してます。

これでJSONを取得し、パースし、データを表示することが出来ました。
取得するボタンを押し、こんな感じに画面が表示されていればOKです。
03_window