エンジニア

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

投稿日:2014年7月15日 更新日:

今回のエンジニアブログ担当の岩原です。
今回は、「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

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-,

© WonderPlanet Inc.