HTTP通信と検索機能

 今回はHTTP通信、検索等の機能を使ったサンプルを作成してみます。
 HTTP通信用のサーバーは ATND の イベントサーチAPI(ATND BETA)を使わせてもらいます。
 ATND API リファレンス

サンプルの仕様

 サンプルは“分割アプリケーション”のテンプレートを元に作成します。
 主な仕様は以下の通りです。

  • ATND の API にアクセスしてイベント情報を取得し画面に表示する。
  • 検索コントラクトを使ってイベント情報の検索ができるようにする。
  • イベントの詳細情報をブラウザを起動して表示する。

HTTP通信

 まずHTTP通信部分を作成します。
 ATND の API から色々な情報を取得できますが、今回は画面表示用にタイトル、イベント開催日時、開催会場、ブラウザ起動用に ATND の URL を使用しますので、これらの情報を保持するデータソースクラスを作成します。

public class EventDataItem : HttpTest.Common.BindableBase
{
    public EventDataItem(string title, string started_at, string place, string event_url)
    {
        this._title = title;
        this._started_at = started_at;
        this._place = place;
        this._event_url = event_url;
    }

    private string _title = string.Empty;
    public string Title
    {
        get { return this._title; }
        set { this.SetProperty(ref this._title, value); }
    }

    private string _started_at = string.Empty;
    public string StartedAt
    {
        get { return this._started_at; }
        set { this.SetProperty(ref this._started_at, value); }
    }

    private string _place = string.Empty;
    public string Place
    {
        get { return this._place; }
        set { this.SetProperty(ref this._place, value); }
    }

    private string _event_url = string.Empty;
    public string EventUrl
    {
        get { return this._event_url; }
        set { this.SetProperty(ref this._event_url, value); }
    }
}

public sealed class EventDataSource
{
    private ObservableCollection<EventDataItem> _items = new ObservableCollection<EventDataItem>();
    public ObservableCollection<EventDataItem> Items
    {
        get { return this._items; }
    }

    public EventDataSource()
    {
    }
}

 作成したデータソースを使って画面を表示するためにデータテンプレートを作成します(ItemsPage.xaml に追加)。

<UserControl.Resources>

    <CollectionViewSource
        x:Name="itemsViewSource"
        Source="{Binding Items}"/>

    <DataTemplate x:Key="EventItemTemplate">
        <Grid HorizontalAlignment="Left" Width="300" Height="80">
            <StackPanel Background="{StaticResource ListViewItemOverlayBackgroundBrush}">
                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayTextBrush}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding StartedAt}" Foreground="{StaticResource ListViewItemOverlaySecondaryTextBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding Place}" Foreground="{StaticResource ListViewItemOverlaySecondaryTextBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
            </StackPanel>
        </Grid>
    </DataTemplate>

</UserControl.Resources>

 GridView の ItemTemplate も、上記で定義した EventItemTemplate を使うように変更します。

 実際に通信を行う処理は ItemsPage.xaml.cs に以下のように追加します。

// 検索して画面に表示
public async void SearchData(string keyword)
{
    // 指定された検索キーワードを使ってリクエストURLを生成
    var sb = new StringBuilder();
    sb.Append("http://api.atnd.org/events/?count=30&format=json");
    if (!String.IsNullOrEmpty(keyword))
    {
        sb.AppendFormat("&keyword={0}", keyword);
    }

    // リクエストを送信して結果文字列を取得
    var client = new HttpClient();
    client.MaxResponseContentBufferSize = 1024 * 1024 * 10;
    var result = await client.GetStringAsync(sb.ToString());

    // レスポンスを解析して一覧に追加
    var rootElement = Windows.Data.Json.JsonObject.Parse(result);
    var eventElements = rootElement.GetNamedArray("events");

    foreach (var elem in eventElements)
    {
        var title = elem.GetObject().GetNamedString("title");
        var started_at = elem.GetObject().GetNamedString("started_at");
        var place = elem.GetObject().GetNamedString("place");
        var event_url = elem.GetObject().GetNamedString("event_url");

        var item = new EventDataItem(title, started_at, place, event_url);

        _source.Items.Add(item);
    }
}

 ATND の API に取得する最大個数とレスポンスの形式(json)を固定で指定し、検索文字列が渡された場合はキーワードとして追加で指定します。
 実際の通信は HttpClient の GetStringAsync メソッドを使って行い、結果を解析してデータソースに追加します。

検索機能

 次の検索機能や起動時の処理等を追加していきます。
 検索機能は宣言が必要ですので、アプリケーションマニフェストの宣言に検索を追加します。

 まず、起動時の処理を App.xaml.cs に追加します。

// 通常の起動処理
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    var _rootFrame = new Frame();
    _rootFrame.Navigate(typeof(ItemsPage), null);

    Window.Current.Content = _rootFrame;
    Window.Current.Activate();
}

// 検索ペインでアプリが選択された場合の起動処理
protected override void OnSearchActivated(SearchActivatedEventArgs args)
{
    base.OnSearchActivated(args);

    if (Window.Current.Content == null)
    {
        // 検索指示でアプリが起動された
        var _rootFrame = new Frame();
        _rootFrame.Navigate(typeof(ItemsPage), args.QueryText);

        Window.Current.Content = _rootFrame;
        Window.Current.Activate();
    }
    else
    {
        // 中断状態の時に検索指示
        ((ItemsPage)((Frame)Window.Current.Content).Content).UpdateQuery(args.QueryText);
    }
}

 OnLaunched は通常の起動処理です。
 OnSearchActivated は、このアプリがアクティブではない(起動していない or 中断状態)時に、検索ペインでアプリが選択された場合に呼び出されます。
 起動していない状態で呼び出された場合は、通常と同じようにページを表示します。この時、パラメータとして検索文字列を渡します。
 中断状態で呼び出された場合は既にページは作成されていますので、ページ側のメソッドを呼び出して更新します(更新に使う UpdateQuery メソッドは後でページ側に追加します)。

 続いてページ側(ItemsPage.xaml.cs)にも処理を追加します。

private EventDataSource _source = new EventDataSource();

public ItemsPage()
{
    this.InitializeComponent();

    var searchPane = SearchPane.GetForCurrentView();
    searchPane.QuerySubmitted += SearchPane_QuerySubmitted;
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.DefaultViewModel["Items"] = _source.Items;

    UpdateQuery(e.Parameter as string);
}

// アプリ動作中に検索文字列が渡された
void SearchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
{
    UpdateQuery(args.QueryText);
}

// クエリ文字列更新
public void UpdateQuery(string querytext)
{
    _source.Items.Clear();

    if (String.IsNullOrEmpty(querytext))
    {
        pageTitle.Text = "イベント";
    }
    else
    {
        pageTitle.Text = "イベント検索結果: " + querytext;
    }

    SearchData(querytext);
}

 更新処理として UpdateQuery メソッドを追加しています。
 このメソッドは、タイトルを変更した後で通信処理を呼び出して画面を更新します。

 UpdateQuery メソッドを呼び出すパターンは以下の三種類あります。

  • 起動時の動作として OnNavigatedTo メソッド内から呼び出します。
  • QuerySubmitted イベントのハンドラから呼び出します。このイベントはアプリがアクティブな状態の時に、検索ペインで検索文字列が確定された場合に呼び出されます。
  • アプリが中断状態の時に検索が実行された場合は、App クラスから直接呼び出します。

 これで、イベント表示や検索が行えるようになります。

ブラウザ起動

 最後にブラウザ起動処理を追加します。
 ItemsPage.xaml.cs の ItemView_ItemClick を以下のように書き換えます。

async void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
    var item = e.ClickedItem as EventDataItem;
    if (item != null)
    {
        // イベントURLを指定してブラウザ起動
        var opt = new LauncherOptions();
        await Launcher.LaunchUriAsync(new Uri(item.EventUrl)); 
    }
}

 表示されている項目をクリックすると、対応する URL がブラウザで表示されるようになります。

実行結果

 アプリを起動すると、イベント情報が表示されます。


 アプリが表示されている状態で、検索ペインから検索文字列を入力して確定させると、指定した文字列で検索されたイベント情報が表示されます。


 アプリを終了させてから検索ペインで検索文字列を入力してアプリを選択すると、アプリが起動され指定した文字列で検索されたイベント情報が表示されます。


 表示されているイベントをクリックすると、ブラウザが起動しイベントの詳細が表示されます。