今回はTwitterのタイムラインを表示するサンプルを紹介します。なんという事はなく、WebClientクラスのDownloadStringAsyncメソッドをRxに乗せてみた、というやつです。Twitterからのタイムライン取得はAtomフィードを利用しています。
サンプルコード
まず、DownloadStringAsyncメソッドをIObservable<T>に変換する部分を拡張メソッドとして切り出します。Observable.Createメソッドを利用して、DownloadStringAsyncAsObservableメソッドからの戻り値のIObservable<T>をSubscribeしたタイミングでダウンロード処理が実行されるようにしています。
今回の例では特に必要ありませんが、ダウンロード処理中に購読解除が行われた場合はダウンロードをキャンセルするようにしています。
using System; using System.Net; using System.Reactive.Linq; namespace Sample41_Timeline { public static class WebClientExtensions { public static IObservable<string> DownloadStringAsyncAsObservable(this WebClient client, Uri uri) { return Observable.Create<string>(observer => { //---- 購読 bool completed = false; var subscription = Observable.FromEvent<DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs> ( handler => (sender, e) => handler(e), handler => client.DownloadStringCompleted += handler, handler => client.DownloadStringCompleted -= handler ) .Take(1) .Select(e => { completed = true; if (e.Error != null) throw e.Error; if (e.Cancelled) throw new InvalidOperationException("Cancelled"); return e.Result; }) .Subscribe(observer); //---- 実行 client.DownloadStringAsync(uri); //---- 購読解除 return () => { subscription.Dispose(); if (!completed) client.CancelAsync(); }; }); } } }
次にXAMLコードを示します。ツイートの表示整形するためにちょこっと細々していますが、UIコンポーネントはTweetを表示するためのListBoxが置いてあるだけです。
<Window x:Class="Sample41_Timeline.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Sample41_Timeline" Height="400" Width="400"> <Window.Resources> <!- リストボックスのアイテムの幅を調整するおまじない -> <Style TargetType="ListBoxItem"> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> </Style> <!- ひとつのアイテムの表示内容 -> <DataTemplate x:Key="FeedItem"> <Grid Margin="5,5" Background="MintCream"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Image Source="{Binding Image}" Margin="0,0,5,0" VerticalAlignment="Top" /> <StackPanel Grid.Column="1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock FontWeight="Bold" Text="{Binding Author}" /> <TextBlock FontWeight="Bold" Text="{Binding Published}" Grid.Column="1" HorizontalAlignment="Right" /> </Grid> <TextBlock Text="{Binding Content}" TextWrapping="Wrap" /> </StackPanel> </Grid> </DataTemplate> </Window.Resources> <!- UIコンポーネントはListBoxだけ -> <ListBox ItemsSource="{Binding}" ItemTemplate="{StaticResource FeedItem}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/> </Window>
最後に、Atomフィードをダウンロードしてデータを整形する部分のコードを示します。AtomフィードはただのXML形式のファイルなので、それをちょこちょこっとゴネゴネっとしてデータを抜き取っています。
using System; using System.IO; using System.Net; using System.Reactive.Linq; using System.ServiceModel.Syndication; using System.Threading; using System.Web; using System.Windows; using System.Xml; namespace Sample41_Timeline { public partial class MainWindow : Window { public MainWindow() { this.InitializeComponent(); var url = "http://search.twitter.com/search.atom?rpp=50&q=" + HttpUtility.UrlEncode("xin9le"); Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5)) //--- 5秒間隔で自動更新 .SelectMany(_ => new WebClient().DownloadStringAsyncAsObservable(new Uri(url, UriKind.Absolute)) //--- Atomをダウンロード .Select(text => new StringReader(text)) .Select(reader => XmlReader.Create(reader)) .SelectMany(reader => SyndicationFeed.Load(reader).Items) //--- Atomフィードを解析 .Select(item => new { Content = item.Title.Text, Published = item.PublishDate.LocalDateTime.ToString(), Author = "@" + item.Authors[0].Name, Image = item.Links[1].Uri, }) .ToArray() .Catch(Observable.Never<object>())) //--- エラーは握りつぶす .ObserveOn(SynchronizationContext.Current) .Subscribe(items => this.DataContext = items); //--- 匿名クラスのコレクションをDataContextとして登録 } } }
実行結果
実行すると以下のようになります。それっぽく取得できていますね!もちろん、取得中は非同期に処理が実行されます。