2010年1月24日日曜日

第7回(前編) 複数のタイムラインを表示する

今回は複数のタイムラインを表示するようにします。

仕上がりイメージ↓(例によって文章は一応ぼかしています)


まず、今回の機能に必須ではないのですが、
前回の作りがひどかったなと思って作り直します。
すみません。

ViewModelが通信してデータをとってくるのはやっぱりおかしいですよね、
ということで、タイムラインを管理するTimelineクラスを作ります。

このクラスはあらかじめTimelineのURLをコンストラクタに与えておき
Update()を呼び出すとタイムラインのデータを取得します。
また、新しいつぶやき(Status)があったらイベントStatusAddedを発生させます。




namespace WTwitter.Model.Twitter {
 class StatusAddedEventArgs : EventArgs {
  private Status _item;
  public StatusAddedEventArgs(Status item) {
   _item = item;
  }

  /// <summary>
  /// 追加されたアイテム
  /// </summary>
  public Status Item {
   get { return _item; }
  }
 }

 /// <summary>
 /// タイムラインを管理するクラス
 /// </summary>
 class Timeline {
  private string _url;
  private List<Status> _allItems = new List<Status>();

  /// <summary>
  /// コンストラクタ
  /// </summary>
  /// <param name="timelineURL">タイムラインを取得するためのURL</param>
  public Timeline(string timelineURL) {
   _url = timelineURL;
   StatusAdded += (sender, e) => { }; //あとでnullチェックしないですむように
  }

  /// <summary>
  /// Statusが追加されたことを通知するイベント
  /// </summary>
  public event EventHandler<StatusAddedEventArgs> StatusAdded;

  /// <summary>
  /// タイムラインに含まれるすべてのアイテム
  /// </summary>
  public List<Status> AllItems {
   get { return _allItems; }
  }

  /// Twitterサーバからデータを取得する
  /// </summary>
  public void Update() {
   //HTTP用の要求と応答
   HttpWebRequest request = HttpWebRequest.Create(_url) as HttpWebRequest;
   HttpWebResponse response = request.GetResponse() as HttpWebResponse;

   //Json形式でデータを取得する
   DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Status>));
   var stream = response.GetResponseStream();
   var result = serializer.ReadObject(stream) as List<Status>;

   response.Close();

   //とってきたアイテムを、取得済みでない場合は追加する
   foreach (var item in result) {
    if (!_allItems.Contains(item)) {
     _allItems.Add(item);
     StatusAdded(this, new StatusAddedEventArgs(item));
    }
   }
  }
 }
}





コードには2つのクラスが入っています。
以前のViewModelの流用部分が多いので、
EventHandlerの仕組みさえ知っていれば簡単だと思います。

EventHandlerは、主に外のクラスがこの変数にメソッドを登録しておくことで、
イベントの発生時に登録していたメソッドを呼び出してもらう仕組みです。
その際にEventArgsを継承したクラスを介して情報を渡します。
一般的な仕組みなので、詳しくはEventHandlerやdelegateなどのキーワードで調べてください。


前半のStatusAddedEventArgsはその情報を渡すためのクラスです。
新しいStatusがあったときにそのStatusを保持して、イベントに渡されます。

TimelineクラスはURLを保持していて、Update()が呼ばれるたびにURLにアクセスして
新しいStatusがあったらイベントを発生させます。

StatusAdded += (sender, e) => { }



の部分ですが、これは、
EventHandlerは複数のメソッドを登録できるのですが、
(↑つまりイベントが発生したときに複数箇所で通知を受け取ることができる)
登録がない可能性がある場合は呼び出しのたびにnullチェックをしないといけません。
このnullチェックを省略するために、何もしないメソッドを登録しています。
矢印みたいなのはラムダ式というC# 3.0(かな?)で新しく追加された書き方です。
通常


private void XXXEvent(object sender, EventArgs e)


みたいなメソッドをどこかに用意しないといけないのですが、
ラムダ式で匿名関数とすることで、その場で書けます。
(sender, e)が引数を表していて、中括弧の中がメソッドの処理(今回は空)を表します。
引数の型はStatudAdded変数の型から推測可能なので、上記のように省略できます。

地味に変更しているのが、_allItems.Contains()ですでに登録済みかどうかをチェックするところです。
実際は同じ内容のStatusだったとしても、別のタイミングでnewしたものはコンピュータは同一だと判断できないので、
Statusクラスに以下を追加して、同一である判断をIDでするようにします。



  #region IEquatable<Status> メンバ

  public bool Equals(Status other) {
   if (other == null) {
    return false;
   }

   return this.Id == other.Id;
  }

  #endregion

  public override int GetHashCode() {
   return this.Id.GetHashCode();
  }



Equalsを書いたらGetHashCodeも書かないといけません。
ここはこれ以上今回説明しないので他で調べてください。

長くなりそうなので後編に続きます。

0 件のコメント:

コメントを投稿