2010年1月20日水曜日

第3回 少しMVVMっぽく作る

第2回までを書いて、簡潔に説明を書くのは難しいなと思いました。
初心者には十分なほど書いてなくて、中級者以上にはまどろっこしいだけの
『誰得』な中途半端になっているような気も。。

というわけで、少し説明を減らしてキーワードを入れていく感じを目指したいと思います。
キーワードを検索して詳しいサイトを見るとわかりますよ、くらいの。

今回は前回作ったものをMVVMライクに書き換えます。
MVVMは
http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx
で紹介されているデザインパターンです。

実は私も正しく理解している自信がないのですが、
簡単に説明して、今回この形に近づけます。

MVVMはModel-View-ViewModelの略で
Model=データを扱うところ
ViewModel=ユーザーインターフェイスをモデル化したところ
View=ユーザーインターフェイスそのもの
といったところでしょうか。

プログラミングでは一般的に、データ部分は比較的変更が少なく
ユーザーインターフェイス部分は変更が多いと言われています。
MVVMではView→ViewModel→Modelの一方向の依存関係のみで
逆方向に依存関係はなく、そのため
ユーザーフェイスの仕様変更でモデルまで変更しなければならない、ということが少なくなることが期待できます。
また、ユーザーフェイスが絡むとユニットテストしにくいですが、
ViewModelとModelはViewに依存しないためテストしやすくなります。

もし以上をわかんなくても、なんとなくコードはわかるかと思います。

Modelは前回のStatusとUserクラスをそのまま使います。

ViewModelは一つのタイムラインを表すTimelineViewModelクラスと
そのタイムラインの1つのアイテムを表すTimelineItemViewModelクラスを作ります。



namespace WTwitter.ViewModel.Twitter {
 /// <summary>
 /// タイムラインを表すViewModel
 /// </summary>
 class TimelineViewModel {
  ObservableCollection<TimelineItemViewModel> _allItems = new ObservableCollection<TimelineItemViewModel>();

  /// <summary>
  /// Twitterサーバからデータを取得する
  /// </summary>
  public void Load() {
   //取得するURL
   string url = "http://twitter.com/statuses/user_timeline/yuki1090.json";

   //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();

   //取得した結果をCollectionに格納
   foreach (var item in result) {
    _allItems.Add(new TimelineItemViewModel(item));
   }
  }

  /// <summary>
  /// ViewModelが対象とするすべてのアイテム
  /// </summary>
  public ObservableCollection<TimelineItemViewModel> AllItems {
   get { return _allItems; }
  }
 }
}





namespace WTwitter.ViewModel.Twitter {
 /// <summary>
 /// タイムラインで表示する1アイテムを表すViewModel
 /// </summary>
 class TimelineItemViewModel {
  private Status _item;
  public TimelineItemViewModel(Status item) {
   _item = item;
  }

  public string Text {
   get { return _item.Text; }
  }

  public string Name {
   get { return _item.User.ScreenName; }
  }

  public override string ToString() {
   return _item.ToString();
  }
 }
}



TimelineItemViewModelは_item変数で保持したModelのデータへ仲介するだけです。



TimelineViewModelは表示するTimelineItemViewModelをすべて保持します。
すべてのアイテムをAllItemsプロパティが提供します。
ObservableCollectionクラスは、追加変更が通知されるリストです。
あとで説明するBindingのために追加変更を観察できる必要があるのでこのクラスを使います。

Load()イベントは前回のWindow1クラスのLoadedイベントの内容を持ってきました。
最後に取得データをTextBoxに流し込んでいたところを
AllItemsに追加するように変更しています。
これによって、このViewModelがView(Textbox)を参照しないですむ構造になっていることに注目してください。
ViewModelがViewにデータを渡すのではなくて、
ViewがViewModelを観察して表示に反映するのです。

最後にビューは以下のように簡単になります。



<Window x:Class="WTwitter.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WTwitter" Height="300" Width="300" >
 <DockPanel>
  <ListBox ItemsSource="{Binding Path=AllItems}"/>
 </DockPanel>
</Window>






namespace WTwitter {
 /// <summary>
 /// Window1.xaml の相互作用ロジック
 /// </summary>
 public partial class Window1 : Window {
  public Window1() {
   var viewModel = new TimelineViewModel();
   viewModel.Load();
   this.DataContext = viewModel;

   InitializeComponent();
  }
 }
}




TextboxをなくしてListboxにしました。
まず.csの方ですが、DataContextは、アバウトに書くと、今このデータを扱っていますよーということです。
TimelineViewModelのデータを作ってこれを入れておきます。

次に.xamlの方ですが、Bindingは、ある要素とデータを関連づけるってことです。
ListboxのItemsSourceをPath=AllItemと関連づけています。
本当は『XXクラスのYYプロパティと関連づける』のように書くのですが、
XXクラスを省略しているのでDataContext(つまりTimelineViewModel)のAllItemsと関連づいています。
ListboxはItemsSourceに指定した要素の個々を順に表示するものですが、
表示の仕方を指定していないので、ToString()した結果を表示しているだけになります。

Bindingは詳しく書くと長くなるので本読んだり検索したりしてください!


今回はView適当なので次回以降作ります。

今回のソース
http://wtwitter.codeplex.com/SourceControl/changeset/view/37990

0 件のコメント:

コメントを投稿