Feeds:
投稿
コメント

暑いです

そんなこんなでWP7トライアスロンのほうに少し参加しようと頑張ってます
第1と第2までできたので(遅い)適当に書いときます

PictureResizer
絶対にあると思ってた。後悔はしてない。そもそも使う予定がない(

画像のサイズを1~200%に変更できます。直接入力で1~のpxを指定できます。スライダーで縦横を別々に縮小拡大できます。それだけです

まあ適当です、粗造アプリなんて出すなといわれても困りますけどね…(

WheresHereToTile
今朝審査通ってました。まだ公開されていないらしい…

これはBingマップから画像持ってきてタイルに表示する謎のアプリです。用途不明。そもそも地図が小さい

まあ適当に地図出しておくには使えるかもしれません。ズームレベル指定可能です。なぜかローカライズしてあります
画像取得のロジックがかなり怪しくて失敗してもメッセージ通知も何もしないんですけどなぜか通ってよくわかりません

以上です
第3のほうは何作ればいいのかわからなくて進んでいない現状が

とりあえずこの辺で

調べただけ系記事

VSTOでアドインを作ってそのアドインをファイルごとに設定できるようにするにはどうしたらいいかを考えてみます。たとえばExcelアドインで基本自動で背景を表示するけど特定のファイルだけ表示しないようにしたい とかしたいとします。
(企業とかでプレゼンとかほら)

この時設定をAppDataに書き込むのはあまりよくありません。自分で使うだけなら問題はないかと思いきや一つの環境だけなら となります。2つ以上環境がある場合はAppDataに書き込んで共有でもしない限り話になりません。プレゼンしようと開いたらかわいい女の子が表示されるなんてことはあってはならないわけですね
またopenxmlformatを調べて埋め込むっていうのもありっちゃありかもしれませんけどめんどくさすぎる気がします
(propsあるしできなくはないきがしました)

WPFとかのControlだとTagというプロパティがあるしそういうのないかなーと思ったらCustomDocumentPropertiesがほぼすべてで使えるように思われます。Word、Excel、PowerPointでは使えるみたいです。ほかは調べるのがめんどくさかったんです

CustomDocumentPropertiesはその名の通りユーザー定義のドキュメントプロパティを使用するために使われます

そもそもドキュメントプロパティって何かというとコードでいうならAssemblyInfoに書くような内容のあれです
アセンブリにポインタを合わせるとツールチップで作者名とかでるじゃないですか、あれですあれ

で、BuiltinDocumentPropertiesというものもあるのですがそれはさっきの作者名とか入れる方です。基本的な事項はこっちに入れます
ですけど背景画像を自動表示しないなんてプロパティはないのでユーザー定義という形で埋め込んで使用します

とりあえず追加するなら次のような感じ

Dim dp = DirectCast(CacheInfo.GetInstance.cacheApplication.ActiveWorkbook.CustomDocumentProperties, Microsoft.Office.Core.DocumentProperties)
dp.Add("IsNotShowAutoBackground", False, Microsoft.Office.Core.MsoDocProperties.msoPropertyTypeBoolean,True, )

DocumentPropertiesはOffice.Coreにあるので注意が必要です

DocumentProperties.Add(name,linkToConvert,type,value,linkSource)では主にnameが名前、linkToConvertはセルとリンクするかどうか(bool)、typeは値の型(variantですが実際はMicrosoft.Office.Core.MsoDocProperties)、valueはtypeに該当する型の書き込む値、linkSourceはlinkToConvert時にリンクするオブジェクトを指定します

ちなみにDocumentPropertiesのデータはなぜか__comObject地獄がない気がします。たぶんです。たぶん。今度調べます(

この辺で

Kinectデータ解析のためなんです(苦しい)

APIなんかを通じてダウンロードできるデータの形式はxmlなんかも多いですがやはりjsonが多いです。jsonのみはあってもxmlのみは割と見ない感じがします?

ところでExcelにはXMLをインポートする機能があります。外部データの取り込みとして使えるあいつです。
これが予想以上に便利でXML内のメンバーを全部拾ってテーブルとしてまとめてくれます。配列をいれると壮観です
これを使えばJsonViewとかそういうの使わなくてもよくなる!?とか思ったりおもわなかったり

XML…? そうだJSON!ってことでこの使いやすいXMLインポート機能をラップしてJSONをインポートさせましょう。もちろんうまくいってない部分が大きいですが気にしない

ExcelのWorkbookにはXmlImportとXmlImportXmlというメソッドがあります。XmlImportXmlとか何かすさまじいものを感じる名前ですが
Workbook.XmlImport(String, XmlMap, Object(Boolean), Object(String|Range))(Object後の括弧内は実際の型を表します)はUrlを使用したインポートを行います。つまりAPIなんかのエンドポイントに接続といったことが可能になったりしますね。このメソッドは主に外部のXMLをインポートするために用いられます

Workbook.XmlImportXml(String, XmlMap, Object(Boolean). Object(String|Range))は引数の型全く同じですが今度はXMLの文字列を渡します。(XmlImportのほうはいまだとUri使うかな) つまるところこっちは内部のXMLをインポートできます

外部と内部の違いは何かというとまあ単純に言えばStringとしてXMLのデータがあるほうが内部です。もってないほうが外部です
今回はJSONをXMLに変換しないといけないので当然XmlImportXmlのほうを使います

・JSON→XMLの変換
さて、使うメソッドは確認できましたので次はJSONのデータの文字列をXMLに変換しなくてはなりません。さすがに内部ではXMLにしてくれません
今回使うのはもちろんJsonReaderWriterFactoryです。いやまあがっつりXmlReaderに変換しますし

JsonReaderWriterFactory.CreateJsonReader[System.Runtime.Serialization.Json namespace]はJSONのByte()からXmlDictionaryReaderを生成します。JSONデータのByte()はSystem.Text.Encoding.Unicode.GetBytesとかでJSONデータのStringを変換すればいいかなと。XmlDictionaryReaderQuotasのほうは今回はそんな怪しいデータは使わないのでMaxにします。必要に応じて変えてください
MemoryStreamにJSONのデータ書き込んだりしてもいいんですけどusingで囲ったりするの面倒なのでStringで扱ったほうがいいかなーという感じです

この時の返り値のXmlDictionaryReaderをXDocumentのコンストラクタの引数にそのまま突っ込みます。どうせXmlReader継承してるしいいよねっていう感じで
XDocumentにする理由はXElementだと配列が配列になってしまうのでXML一個として扱うならXDocumentです
ちなみにXmlDictionaryReaderまともに扱ったら大変な事態になりました

ここまでをソースコードにまとめると次のような感じです。今回はファイルから読み込みます(Webから読み込みたかったんですけどBadRequest5回ほど返されて断念)

If Not String.IsNullOrEmpty(FileSourceTextBox.Text) AndAlso IO.File.Exists(FileSourceTextBox.Text) Then
    Dim content As String = ""
    Using xreader As New StreamReader(FileSourceTextBox.Text)
        content = xreader.ReadToEnd
    End Using
    Dim xdoc = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(System.Text.Encoding.Unicode.GetBytes(content), Xml.XmlDictionaryReaderQuotas.Max))
End If

一行に収まってて力技みたいになってますが普通です。きっと普通です

・XmlImportXmlを使う
準備できたので本題へ

XmlImportXmlに必要なのはWorkbookのインスタンスとDestinationです。なぜかDestinationにNothing渡すと参照がないとか言われます。意味が分かりません

第一引数にはさっき作ったXDocumentをToStringしたものを入れれば十分です。スキーマが足りないとかいわれますがExcelが勝手にやってくれるので問題ありません
第二引数のImportMapはByRefがついてるのでNothingで構いません。XmlMapの変数宣言して入れてもいいと思います。とりあえずNothing安定な感じ
OverwriteはBooleanです。上書きしていいかを指定するものです。たぶんこれはTrueでいいかなと
Destinationはセルの位置を指定します。StringかRangeのインスタンスが必要です。これはOptionalなのにNothing入れたら進めません

WorkbookはMicrosoft.Office.Interop.Excel.Application.ActiveWorkbookを使いましょう。ActiveWorksheetなんかと違ってちゃんと型指定されてます。すばらしい

さてこの辺をまとめたメソッドを作るとこんな感じになります。先のコードとかぶってる部分がありますがそこはうまく分けてください

Private Sub jsonTextImport(ByVal text As String, workbook As Microsoft.Office.Interop.Excel.Workbook, destination As String)
    Dim _map As Microsoft.Office.Interop.Excel.XmlMap = Nothing
    Dim xdoc = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(System.Text.Encoding.Unicode.GetBytes(text), Xml.XmlDictionaryReaderQuotas.Max))
    workbook.XmlImportXml(xdoc.ToString, _map, True, destination)
End Sub

まあこんな感じでできます。DataContractJsonSerializerで作ったJSONも普通に解析できるのでぜひとも活用していきたいですね

とりあえずここらへんで

ねむいので少しだけで終えます

今話題(?)のKinectにはKinect SDKがありWindowsにつないでKinectを動かすことができます
これをWPFアプリケーションに実装することを考えてみましょう

今回犠牲になってもらうアプリケーションは廃れたAyatterなる何かです
こいつにはゲームパッドで操作できるという点があるのでKinectでキャプチャしてジェスチャできればすぐなので今回の犠牲対象にはうってつけです

Kinectの初期化だのはたくさん出ていますのでそちらを参考に。
今回はスケルトンデータを読み込めば十分でしょう

さてさて、そもそもジェスチャするといっても全部自分で定義するの?って話ですがWPFにはInkCanvasがありますね。
InkCanvasではマウスやタッチの動きをキャプチャして線を描画したりそのままマウスジェスチャにも使えます
こんないいやつはいない!これにKinectで描画させよう! ってやったのが運のつきでした。1日無駄になりますので気をつけましょう
InkCanvasにはStrokesプロパティがあってこれにStrokeを追加すればいいのかというとそういうわけでもなくジェスチャがおこなわれません。ほかに描画メソッドがあるわけでもないのでつまるところInkCanvasにコードからジェスチャさせるのは無理!というわけです

ところでInkCanvasは実際どうジェスチャを認識しているか、という話です。実はGestureRecognizer [System.Windows.Ink namespace]を使っています。このクラス自体は普通のクラスでこれのRecognizeメソッドもPublicです。これを使えばInkCanvasに頭を悩ませなくてもStrokeCollectionに放り込んで認識させれば完璧です

ここからどうするかというと
・Kinectで取得したスケルトンから座標を取得する
・取得した座標をPointのIEnumerableに突っ込む
・StylusPointCollectionを作ってStrokeに変換
とやれば晴れて認識できるわけです

ここまでしかやってないのでこの辺で

・Page 0 : ReactiveOAuthを使う前に -WP7のアプリとLINQ,Rx- (https://fantasticswallow.wordpress.com/2012/07/12/dev-twitter-client-for-wp7-page0-whats-linq-and-rx/)
・Page 1 : ReactiveOAuthを使ってみる -Rxと通信層の作成- (https://fantasticswallow.wordpress.com/2012/07/12/dev-twitter-client-for-wp7-page1-using-reactive-oauth/)
・Page 2 : ツイートしてみる -ReactiveOAuthを用いたTwitterとの通信- (https://fantasticswallow.wordpress.com/2012/07/14/dev-twitter-client-for-wp7-page2-how-to-tweet-for-twitter/)
・Page 3 : Twitterに認証する -PIN Flowを用いた認証- (この記事)
・Page 4 : タイムラインを表示する -GETリクエストとXAMLの編集- (???)
・おまけ : Server Side Flowを用いたTwitterへの認証とその罠 (???)

注意点
・この記事はプログラミング初心者に向けたものではありません。C#|VB.NETがいくらかわかる前提で進めます。解説はしません
・Silverlight|WPFの開発経験が少しでもあるとわかりやすいです
・LINQが少しでもわかるととてもよいです
・Twitter APIなどについてはほとんど解説しません。あるがままに扱われます。その辺は適当にBing先生にでも
・コンシューマーキーの取得は済んでいるものとします
・WP7.5の開発環境がすでにあるものとします
・Twitterアカウントを持っているものとします
・初心者が書いてるのでMVVMパターンなどで開発したいなどなどは対応していません

Minecraftが楽しすぎて進んでいません

さて認証です。Twitterクライアントを作るうえでは必須の動作ですね

ここではPIN Flowによる認証を扱います。Server Side Flowはまた今度

今回もC#のみです
さあさあ本題へ

・今回のxaml
Page2のMainPageがあるものとします。
MainPageにまずApplicationBarを追加します。次のxamlを追加します

    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <!--<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>-->
            <shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="Twitterに認証" Click="ApplicationBarMenuItem_Click" />
            </shell:ApplicationBar.MenuItems>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

デフォルトのがあればそれをいじります。ApplicationBarIconButtonのほうは使わないのでコメントアウトします
次に認証するページのxamlを用意します。次の通り

<phone:PhoneApplicationPage 
    x:Class="TestPhoneApp1.AuthPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

    <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--ContentPanel - 追加コンテンツをここに入力します-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBlock Height="Auto" HorizontalAlignment="Left" Margin="14,14,0,0" Name="textBlock1" Text="Pin Code" VerticalAlignment="Top" FontSize="32" />
            <TextBox x:Name="PinTextBox" Height="75" Margin="133,0,99,0" TextWrapping="Wrap" VerticalAlignment="Top" InputScope="Number"/>
            <Button x:Name="AuthenticateButton" Content="Go" HorizontalAlignment="Right" Margin="0,4,0,0" VerticalAlignment="Top" Width="95" Click="AuthenticateButton_Click" />
            <phone:WebBrowser x:Name="AuthNavigateBrowser" Margin="0,87,0,0" d:LayoutOverrides="Width, Height"/>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

これでxamlの準備は完了です。認証してポストまで目指します

・認証部分のコードを書く
ここについてはReactiveOAuthのページのほうにサンプルがありますのでそのまま使って大丈夫です。今回は汎用性?をあげるために少し変更します

using System;
using System.Linq;
using Codeplex.OAuth;
using Microsoft.Phone.Reactive;

namespace TestPhoneApp1
{
    public class TwitterAuthorizer
    {
        private RequestToken reqToken;
        private AccessToken acsToken;

        private OAuthAuthorizer authorizer = new OAuthAuthorizer(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret);

        public delegate void GetPinCode(string navigateUri, TwitterAuthorizer author);

        public void GetRequestToken(GetPinCode getPinStr, TwitterAuthorizer  author)
        {
            var authorizer = new OAuthAuthorizer(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret);
            authorizer.GetRequestToken("https://twitter.com/oauth/request_token")
                .Select(res => res.Token)
                .ObserveOnDispatcher()
                .Subscribe(token =>
                {
                    reqToken = token;
                    var url = authorizer.BuildAuthorizeUrl("https://twitter.com/oauth/authorize", token);
                    getPinStr(url, this);
                });
        }

        public void GetAccessToken(string pinStr)
        {
            authorizer.GetAccessToken("https://twitter.com/oauth/access_token", reqToken, pinStr)
                .ObserveOnDispatcher()
                .Subscribe(res =>
                {
                    // Token取得時のレスポンスには、Token以外に幾つかのデータが含まれています
                    // Twitterの場合はuser_idとscreeen_nameがついてきます
                    // ILookup<string,string>なので、First()で取り出してください
                    TwitterInfoCS.OAuthUserId = UInt64.Parse(res.ExtraData["user_id"].First());
                    TwitterInfoCS.OAuthScreenName = res.ExtraData["screen_name"].First();
                    acsToken = res.Token; // AccessToken
                    TwitterInfoCS.OAuthToken = acsToken.Key;
                    TwitterInfoCS.OAuthTokenSecret = acsToken.Secret;
                    var evArgs = new TwitterAuthedEventArgs();
                    evArgs.OAuthToken = acsToken.Key; evArgs.OAuthTokenSecret = acsToken.Secret;
                    evArgs.UserId = TwitterInfoCS.OAuthUserId;
                    evArgs.UserScreenName = TwitterInfoCS.OAuthScreenName;
                    TwitterAuthed(evArgs);
                });
        }
        
        public delegate void oauthedTwitter(TwitterAuthedEventArgs e);
        public event oauthedTwitter TwitterAuthed;

        public class TwitterAuthedEventArgs
        {
            public UInt64 UserId { get; set; }
            public string UserScreenName { get; set; }
            public string OAuthToken { get; set; }
            public string OAuthTokenSecret { get; set; }
        }
    }
}

RequestTokenのほうではUriを取得したあとの処理をデリゲートに直しました。これによりwebBrowserの位置にとらわれずに認証処理ができます。ラムダ式でささっと書いてしまいましょう
AccessTokenのほうではTwitterへの認証に成功するとイベントを返すようにしてあります。このイベントをハンドルすることでメッセージを出したりできます

・AuthPageのコードビハインド
必要なのはロード時に指定したUriに移動することと認証ボタンを押してAccessTokenを取得する処理に移行するメソッドの二つです。あとはそれらに必要な参照などを追加します
こんなメソッドとかを追加します

        public static string navigateUri{get; set;}
        public static TwitterAuthorizer authRef { get; set; }

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            AuthNavigateBrowser.Navigate(new Uri(navigateUri));
        }

        private void AuthenticateButton_Click(object sender, RoutedEventArgs e)
        {
            bool isValid = false;
            try
            {
                var tagInt = int.Parse(PinTextBox.Text);
                if (tagInt >= 0 && tagInt < 10000000 && PinTextBox.Text.Length == 7) //7桁のコード
                {
                    isValid = true;
                }
            }
            catch
            {
                MessageBox.Show("正しいPINコードが入力されていません");
            }
            if (isValid)
            {
                authRef.GetAccessToken(PinTextBox.Text);
            }
        }

TwitterAuthorizerの参照とnavigateUriをstaticで宣言してあります。これは単純に参照がめんどくさいからですはい。
認証ボタンのところではPINが正しい形かどうかをチェックしています。ただし現在の7ケタの形から変わると即動かなくなりますので注意。あと必要ないなら削ってもいいかもしれません

・MainPageからAuthPageへの遷移
さてやっとMainPageのコードビハインドです
ところで前回の時にLoadedでOAuthToken設定してる場合は設定する部分を削除してください。念のためです
ConsumerKeyとConsumerSecretは削除しないでくださいね

先ほど作ったApplicationBarMenuItemのクリックイベントのハンドラとなります。まず押されたときにRequestTokenを取得してその後AuthPageに遷移します。ページの遷移にはNavigationServiceを使います。
ここで先ほどRequestTokenの取得後の処理をデリゲートにしておいたのでラムダ式で書いてみましょう。
次にAccessToken取得後にイベントが返るのでこれにハンドラを追加します。某Kriなんとかみたいに「Hello スクリーンネーム」って出してみましょう。その後元のページに戻します
これらをコードにすると次のような感じ

        private void ApplicationBarMenuItem_Click(object sender, EventArgs e)
        {
            var author = new TwitterAuthorizer();
            author.TwitterAuthed += _ =>
            {
                Dispatcher.BeginInvoke(() =>
                    {
                        MessageBox.Show("Hello! " + _.UserScreenName);
                        NavigationService.GoBack();
                    });
            };
            author.GetRequestToken((uri,athr) =>
            {
                AuthPage.navigateUri = uri;
                AuthPage.authRef = athr;
                NavigationService.Navigate(new Uri("/AuthPage.xaml",UriKind.Relative));
            }, author);
        }

この時AuthPage.xamlがないと動けないので適宜直してください

・さあ認証だ?
ここまでできたら実際に起動してApplicationBarの中のメニューをタップしてみましょう。ネットワークにつながってればたぶんブラウザが表示されてPIN入力までできるはずです

「姫様!もうすぐアプリケーションが閉じそうです!」

というわけでまともにこの通りにやってたらまたしても落ちてしまいます。しかもReactiveOAuthの中で
というのもTwitterとかいう何かはaccess_tokenを取得するとなぜか強制GZIP化して返してくださいます、とっても親切設計ですね!実際はデータ量増えるはずですが

さてさて、こんな理不尽な不具合に対処できないようでは一人前のTwitterクライアント作者にはなれませんので(こんな不具合ばっかです)カカッと対処しちゃいましょう

とはいってもこれどうやって対処するのってなりますがもちろん今いじってるアプリケーションからじゃどうしようもありませんので、ReactiveOAuth自体をいじります
まずはCodePlexからソースコードを拾ってきます。その辺は適当に
落ちる部分はOAuthAuthorizer.csの中のGetTokenResponseのメソッドのなかのレスポンスの文字列解析部分あたりなのでそもそも文字列解析の前にGzipエンコードを処理してやれば落ちることはないのでレスポンスの文字列取得時にデコードします

さて、こんなコードが見えると思います

return req.DownloadStringAsync()
                .Select(tokenBase =>
                {
                    var splitted = tokenBase.Split('&').Select(s => s.Split('=')).ToDictionary(s => s.First(), s => s.Last());
                    var token = tokenFactory(splitted["oauth_token"], splitted["oauth_token_secret"]);
                    var extraData = splitted.Where(kvp => kvp.Key != "oauth_token" && kvp.Key != "oauth_token_secret")
                        .ToLookup(kvp => kvp.Key, kvp => kvp.Value);
                    return new TokenResponse<T>(token, extraData);
                });

さてさて、これじゃ手の出しようがありません。というのもすでに文字列になってます
だったrDownloadStringAsyncを変えればいいじゃない! というわけですばらしいReactiveOAuthにはGetResponseAsObservableがありますのでこれに書き換えてWebResponseを取得します。

次にGzip対策にSharpZipLibのSilverlight版を取得します。http://slsharpziplib.codeplex.com/からダウンロードできます
この中のSharpZipLib.WindowsPhone7.dllをReactiveOAuth.WP7の参照に追加します。そうしないと使えません。

というわけでSharpZipLibを使ってGzip対策したりすると次のコードになります。上の部分を下のコードに差し替えてください

return req.GetResponseAsObservable()
                .Select(tokenArgs =>
                {
                    string tokenBase;
#if WINDOWS_PHONE
                    if (tokenArgs.Headers[HttpRequestHeader.ContentEncoding] == "gzip")
                    {
                        using (System.IO.StreamReader xreader = new System.IO.StreamReader(new ICSharpCode.SharpZipLib.GZip.GZipInputStream(tokenArgs.GetResponseStream())))
                        {
                            tokenBase = xreader.ReadToEnd();
                        }
                    }
                    else
                    {
                        using (System.IO.StreamReader xreader = new System.IO.StreamReader(tokenArgs.GetResponseStream()))
                        {
                            tokenBase = xreader.ReadToEnd();
                        }
                    }
#else
                    using (System.IO.StreamReader xreader = new System.IO.StreamReader(tokenArgs.GetResponseStream()))
                    {
                        tokenBase = xreader.ReadToEnd();
                    }
#endif
                    var splitted = tokenBase.Split('&').Select(s => s.Split('=')).ToDictionary(s => s.First(), s => s.Last());
                    var token = tokenFactory(splitted["oauth_token"], splitted["oauth_token_secret"]);
                    var extraData = splitted.Where(kvp => kvp.Key != "oauth_token" && kvp.Key != "oauth_token_secret")
                        .ToLookup(kvp => kvp.Key, kvp => kvp.Value);
                    return new TokenResponse<T>(token, extraData);
                });

こうしてやるともれなくGzipだろうとちゃんと処理できるようになります。これでビルドして新しいdllを作ったらもとのアプリケーションのほうの参照をこちらに変えます

・認証してみよう
お疲れ様でした。たぶん私の書き方が間違ってなければこれで認証できるはずです。
認証できたら思う存分ツイートしましょう。

とりあえず言っておくとここから先に進めば進むほど今回よりめんどくさい不具合がのしかかりますので下手な覚悟だと作れません。頑張ってくださいね

次回は最終回です。タイムラインを表示してみましょう

・Page 0 : ReactiveOAuthを使う前に -WP7のアプリとLINQ,Rx- (https://fantasticswallow.wordpress.com/2012/07/12/dev-twitter-client-for-wp7-page0-whats-linq-and-rx/)
・Page 1 : ReactiveOAuthを使ってみる -Rxと通信層の作成- (https://fantasticswallow.wordpress.com/2012/07/12/dev-twitter-client-for-wp7-page1-using-reactive-oauth/)
・Page 2 : ツイートしてみる -ReactiveOAuthを用いたTwitterとの通信- (この記事)
・Page 3 : Twitterに認証する -PIN Flowを用いた認証- (???)
・Page 4 : タイムラインを表示する -GETリクエストとXAMLの編集- (???)
・おまけ : Server Side Flowを用いたTwitterへの認証とその罠 (???)

注意点
・この記事はプログラミング初心者に向けたものではありません。C#|VB.NETがいくらかわかる前提で進めます。解説はしません
・Silverlight|WPFの開発経験が少しでもあるとわかりやすいです
・LINQが少しでもわかるととてもよいです
・Twitter APIなどについてはほとんど解説しません。あるがままに扱われます。その辺は適当にBing先生にでも
・コンシューマーキーの取得は済んでいるものとします
・WP7.5の開発環境がすでにあるものとします
・Twitterアカウントを持っているものとします
・初心者が書いてるのでMVVMパターンなどで開発したいなどなどは対応していません

さてやっとこツイートするところです
真面目に初心者だからこんなxaml望んでないとかなっても頑張って直してください

本題に入る前に前回の投稿のTwitterInfoCSとTwitterConnectionCSを用意してある前提で進めます。

あと前回書き忘れてたのですがSystem.Observableの参照追加しないとビルド通りません。追加してない場合は追加しましょう

ちなみにここからVBのコードがなくなります。あとで書きます

では本題へ

・今回のxaml
投稿しかしないので適当でいいかなって(
とりあえずTextBoxとButtonがあればいいです。こんな感じに用意してみました

<phone:PhoneApplicationPage 
    x:Class="TestPhoneApp1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">

    <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel は、アプリケーション名とページ タイトルを格納します-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="TestPhoneApp1" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="ツイート!" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - 追加コンテンツをここに入力します-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBox Height="178" HorizontalAlignment="Left" Margin="-4,21,0,0" Name="StatusTextBox" Text="" VerticalAlignment="Top" Width="460" />
            <Button Content="ツイート" Height="72" HorizontalAlignment="Left" Margin="28,223,0,0" Name="PostButton" VerticalAlignment="Top" Width="404" Click="PostButton_Click" />
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

恒例のセンス0画面です。あきらめます

・Twitterへのリクエスト
Twitterのツイートのリクエストにはstatuses/updateを使います。api.twitter.com/1/statuses/updateですね
先ほど作ったと思うTwitterConnectionCSの中のPostRequestを使います。GETじゃないですよ

using System;
using System.Windows;
using System.Xml.Linq;
using Codeplex.OAuth;

namespace TestPhoneApp1
{
    public class TwitterRequests
    {
        TwitterConnectionCS twCon = new TwitterConnectionCS();

        public void StatusUpdate(string postData,UInt64 in_reply_to)
        {
            var param = new ParameterCollection();
            param.Add("status",postData);
            if (in_reply_to != 0) 
            {
                param.Add("in_reply_to_status_id", in_reply_to);
            }
            twCon.PostRequest("https://api.twitter.com/1/statuses/update.xml", 
                                param,
                                s => var xelmnt = XElement.Load(s.GetResponseStream()), 
                                x => MessageBox.Show(x.Message));
        }
    }
}

in_reply_toにも対応してます。が今回はツイートするだけが目的なので使いません。
すでにバグが一つありますが気にしないでください。後で解説します

・MainPageのコードビハインドその1
MVVMな方は避けて通ります
(というかxamlもあれじゃだめな気がした)

まず認証情報が必要です。認証しないと残念ながらツイートできなくて終わりません。頑張ってください
必要なのはConsumerKey、ConsumerSecret、AccessToken、AccessTokenSecretの4つです。dev.twitter.comで全部取得できますのでここらで用意しときましょう。AccessToken周りは今回だけ使います。先にPage3の内容やってからやってもいいです

認証情報は真っ先に設定する必要があるのでMainPage_Loadedメソッドに追加します。
またボタンのクリックイベントのハンドラにも先ほどのTwitterRequests.StatusUpdateを呼び出すコードを追加します。次の二つのメソッドみたいにします

private TwitterRequests twReqs = new TwitterRequests();
private void PostButton_Click(object sender, RoutedEventArgs e)
{
    twReqs.StatusUpdate(StatusTextBox.Text, 0);   
}

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    TwitterInfoCS.ConsumerKey = "{Your ConsumerKey}";
    TwitterInfoCS.ConsumerSecret = "{Your ConsumerSecret}";
    TwitterInfoCS.OAuthToken = "{Your AccessToken}";
    TwitterInfoCS.OAuthTokenSecret = "{Your AccessTokenSecret}";
}

・XElementの処理
実はこの時点でちゃんと設定してあれば投稿自体はできます。できますが外では確認ができませんので通知しましょう。

今回は上の画像みたいな形を目指します。

先ほどTwitterRequestsですでにXElementを作成しましたのでこのXElementを処理します。XElementはあくまでXmlなのでノード名を入れれば要素が取得できます。なので簡単にメッセージを作ると次のような感じです

using System;
using System.Windows;
using System.Xml.Linq;

namespace TestPhoneApp1
{
    /// <summary>
    /// Twitterからのレスポンスを処理します
    /// </summary>
    public class TwitterProcessing
    {
        /// <summary>
        /// StatusUpdateのレスポンスを処理します
        /// </summary>
        public static void StatusUpdateCallback(XElement xelm)
        {
            string resData = "投稿しました:'" + xelm.Element("text").Value +"'" + Environment.NewLine;
            resData += "時刻:" + xelm.Element("created_at").Value;
            MessageBox.Show(resData);
        }
    }
}

このときTwitterRequests.StatusUpdateの中のs => var xelmnt = XElement.Load(s.GetResponseStream()), の部分を次のようにしておきます

s => TwitterProcessing.StatusUpdateCallback(XElement.Load(s.GetResponseStream())), 

では一回実行してみましょう

・非同期処理とDispatcher
「姫様!もうすぐ!アプリケーションが閉じそうです!」

というわけでまともに私と同じに書いてたら上のようなダイアログが出てクラッシュしてしまいます。これが非同期処理の難しい点でUIにはメインスレッド以外触ることができないという制約です。
つまり先ほどのMessageBox.Showは非同期スレッドから呼び出したためクロススレッドアクセスとなり実行できません
ちなみにPostRequestでObserveOnDispatcherを使うとたぶん今度はXElementが読めなくなります。どちらかというとXElementが読めないほうが厳しいです

えっじゃあ最初のTwitterRequests.StatusUpdateのMessageBox.Showもだめじゃないんです?っていうと実はそうです。

詰んだ! って思ってもまだ私たちにはDispatcherがあります!Dispatcherは特定のスレッドにアクセスできるためこれを使ってメインのスレッドにアクセスします。つまるところDispatcher.BeginInvokeでMessageBox.Showを包んでやればいいわけです。とても簡単ですね
じゃあどのスレッドかというとMainPageしかないのでMainPage.csにもうちょっと追加します

・MainPageのコードビハインドその2
またやってきました。まあやることは決まっていますのでちゃちゃっと片付けましょう

ところでMessageBox.Showは共有メソッドなので共有メソッドで呼びたいところですがするとDispatcherにアクセスできません。ですがそれを解決するのがイベントです(そんな使い方するものじゃないと思いますけど)
public static eventを定義してそれのハンドラを非共有メソッドにするということをします。別にここはMessageBoxが表示できればなんでもいいです

というわけで次のようなコードをさらに追加します

public static event NotifyDelegate MessageBoxNotify;

public delegate void NotifyDelegate(string msg);

public static void MessageNotify(string msg)
{
    MessageBoxNotify(msg);
}

引数がほしいのでデリゲートを定義してイベントにあてています。stringのみで十分かなと思います
またMainPage以外からイベントが呼べないのかわかりませんけど呼び方知らないので同じコードビハインド内でコールするメソッドを書いておきます

次にMainPage_Loadedメソッドに次の1行を追加します

MessageBoxNotify += _ => Dispatcher.BeginInvoke(() => MessageBox.Show(_));

MessageBox.Showをイベントに関連付けています。ないと動かないからです

というわけでMessageBox.ShowをMainPage.MessageNotifyに書き換えて実行しましょう。するときっと先ほどの例外もでなくなるはずです

ツイートのTextBoxのIsEnabledをいじったりとかはまたほかに書かなくてはいけないですが今回はすでに条件達成していますので(PostRequestの例外ハンドラが少し雑ですが)とりあえずここまでにしておきます。下手に書いてるとプログレスバーとかまで書くようなのでその辺は自分なりに書いてみるといいかもです

次はいよいよ認証です。ですがReactiveOAuthを使えば苦労はほとんどしないでいけるはずです

・Page 0 : ReactiveOAuthを使う前に -WP7のアプリとLINQ,Rx- (https://fantasticswallow.wordpress.com/2012/07/12/dev-twitter-client-for-wp7-page0-whats-linq-and-rx/)
・Page 1 : ReactiveOAuthを使ってみる -Rxと通信層の作成- (この記事)
・Page 2 : ツイートしてみる -ReactiveOAuthを用いたTwitterとの通信- (https://fantasticswallow.wordpress.com/2012/07/14/dev-twitter-client-for-wp7-page2-how-to-tweet-for-twitter/)
・Page 3 : Twitterに認証する -PIN Flowを用いた認証- (???)
・Page 4 : タイムラインを表示する -GETリクエストとXAMLの編集- (???)
・おまけ : Server Side Flowを用いたTwitterへの認証とその罠 (???)

注意点
・この記事はプログラミング初心者に向けたものではありません。C#|VB.NETがいくらかわかる前提で進めます。解説はしません
・Silverlight|WPFの開発経験が少しでもあるとわかりやすいです
・LINQが少しでもわかるととてもよいです
・Twitter APIなどについてはほとんど解説しません。あるがままに扱われます。その辺は適当にBing先生にでも
・コンシューマーキーの取得は済んでいるものとします
・WP7.5の開発環境がすでにあるものとします
・Twitterアカウントを持っているものとします

というわけで連投になるというより1に0を入れようとしたら長すぎて飽きてやめるという現象

さてではやっとこ本題に入りましょう

・ReactiveOAuthとは
ReactiveOAuthはneuecc大先生によるWPF(WinForms),Silverlight,WP7に対応した通信部分をRxを利用して書いたTwitterライブラリの一つです
もちろんそれ以外の通信にも使用できる素晴らしい代物だったりします
特にWP7ではライブラリが少ないのでこの手のライブラリはとても便利です

Ms-PLライセンスなのでGPL汚染などを気にせず使えます。
ダウンロードなどなどはhttp://reactiveoauth.codeplex.com/からできます。
書いてる当時は0.4.0.0が最新な気がしますのでそれを使って書いていきます。挫折しないように頑張ります

・ReactiveOAuthを使うために
ダウンロードしてきた前提で行きます
まずはプロジェクトを用意しましょう。TestPhoneApp1って今回はつけて作ります(ご自由にどうぞ)
次にこれに次の参照を追加します
・Microsoft.Phone.Reactive
・System.Observable
・(ReactiveOAuthを解凍したフォルダ/)Binary/ReactiveOAuth.WP7.dll

追加方法が分からないといわれてもどうしようもないのでそこは頑張ってください
Microsoft.Phone.Reactiveは標準で用意されています。参照の追加を押してフレームワークの部分にいますので探して追加してください

・Twitterの基本の情報を入れるクラスの作成
Twitterの基本の情報ってなんだー っていうととりあえずコンシューマーキーやアクセストークンなどがあります。
これらがないとツイートも何もできませんのでその情報を入れるだけのクラスを作りましょう
static(Shared)で宣言しちゃってもここは問題ないと思います。逆にインスタンスを複数生成できると複数アカウント対応できるかもしれませんがそこはまた別の話

Public Class TwitterInfoVB
    Public Shared ConsumerKey As String
    Public Shared ConsumerSecret As String

    Public Shared OAuthToken As String
    Public Shared OAuthTokenSecret As String
    Public Shared OAuthScreenName As String
    Public Shared OAuthUserId As ULong

    Public Shared TwitterUserAgent As String
    Public Shared ReadOnly Property IsNotAuthed As Boolean
        Get
            Return String.IsNullOrEmpty(OAuthToken) OrElse String.IsNullOrEmpty(OAuthTokenSecret)
        End Get
    End Property
End Class
public class TwitterInfoCS
{
    public static string ConsumerKey { get; set; }
    public static string ConsumerSecret { get; set; }

    public static string OAuthToken { get; set; }
    public static string OAuthTokenSecret { get; set; }
    public static string OAuthScreenName { get; set; }
    public static UInt64 OAuthUserId { get; set;}
    public static string TwitterUserAgent { get; set; }

    public static bool IsNotAuthed { get {
        return string.IsNullOrEmpty(OAuthToken) || string.IsNullOrEmpty(OAuthTokenSecret);
    }}
}

 今回の情報用のクラス(上VB.NET、下C#)
なぜかフィールドです。理由はよくわかりません
コメントは今回は省きました。名前からたぶん判断できると思います
え? 名前の付け方がひどい? それは申し訳ない

・通信する際に呼び出すメソッドの作成
続いてReactiveOAuthのメソッドを用いてレスポンスを取得するラッパーメソッドを作ります。この辺は公式にサンプルがあるのでちょっと細工してそのまま使っちゃいましょう(ぉ

Public Sub GetRequestText(url As String, param As ParameterCollection, argOperate As Action(Of String), exHandler As Action(Of Exception))
    If TwitterInfoVB.GetInstance.IsNotAuthed Then
         exHandler(New Exception("Twitterに認証されていません、認証してからもう一度やり直してください"))
    Else
         Dim _oauthClient = New OAuthClient(TwitterInfoVB.ConsumerKey, TwitterInfoVB.ConsumerSecret, TwitterInfoVB.OauthToken, TwitterInfoVB.OauthTokenSecret) With {.Url = url, .Parameters = param, .ApplyBeforeRequest = Sub(req) req.UserAgent = TwitterInfoVB.TwitterUserAgent}
        _oauthClient.GetResponseText().Subscribe(Sub(s) argOperate(s), Sub(x) exHandler(x))
    End If
End Sub
using Microsoft.Phone.Reactive;
using Codeplex.OAuth;
~~~~~
public delegate void resultTextCollection(string resTxt);
public delegate void resultResponseCollection(WebResponse res);
public delegate void exceptionHandler(Exception ex);

public void GetRequestText(string url,ParameterCollection param,resultTextCollection argOperate,exceptionHandler exHandler)
{
	if (TwitterInfo.GetInstance.IsNotAuthed)
	{
		exHandler(new Exception("Twitterに認証されていません、認証してからもう一度やり直してください"));
	}
	else
	{
		var oauthClient = new OAuthClient(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret, TwitterInfoCS.OAuthToken, TwitterInfoCS.OAuthTokenSecret)
		{
			Url = url,
			Parameters = param,
			ApplyBeforeRequest = req => { req.UserAgent = TwitterInfoCS.TwitterUserAgent; },
		};
		oauthClient.GetResponseText()
			.Subscribe(s => argOperate(s), x => exHandler(x));
	}
}

  GETリクエストで文字列を取得する(上VB.NET 下C#)
こんな感じです。Twitterに認証していない場合はその時点ではじくようにしています。たぶんないとは思いますが認証しないで通信したい場合は削除してください
(認証していない場合はすべて排除することで無駄なリクエストを避ける)

Subscribeとかはなんだー というのはRxについて調べるとわかります
Subscribe内でデリゲートを実行している理由は非同期でReturnする必要あるのかなとかそもそもちゃんと帰ってくるのかとかとりあえずReturnしたら非同期じゃないんじゃないかとかいろいろあったのでこのようにしました。
その辺は詳しいお方がたぶんいるので聞いてくださるといいと思いますよ

とりあえず何してるのかまったくわからない! って方はargOperateのデリゲートの引数に取得したレスポンス渡すからデリゲート内で処理してねって感じです。exHandlerは例外時に実行されるデリゲートとなります

まあなんか結局呼び出すメソッドとコールバックのメソッドが必要となってしまったのでとても手間が大きくなりますがそれでもBegin/Endペアでネスト関数しまくるよりはいいかなと思います(論点の転換)

こんな感じのを5種類用意するととてもはかどります(3種類でたぶん足りる)
・GetRequest(Text)
・PostRequest(Text)
・GetRequestStream
???ってなると思いますがとりあえずGETリクエストをするメソッド、POSTリクエストをするメソッド、ストリーミング用のメソッドの3つです。リクエストを引数で渡せば2つですがわかりやすく3つにしてみましょう。
コードはC#のみ置いときます。VBはメソッドチェーン長すぎてわけわかりません

using Microsoft.Phone.Reactive;
using Codeplex.OAuth;
~~~~~
public delegate void resultTextCollection(string resTxt);
public delegate void resultResponseCollection(WebResponse res);
public delegate void exceptionHandler(Exception ex);

/// <summary>
/// GETリクエストを行います
/// </summary>
/// <param name="url">通信先のurl</param>
/// <param name="param">ヘッダに追加する引数</param>
/// <param name="argOperate">レスポンスを処理するデリゲート</param>
/// <param name="exHandler">例外を処理するデリゲート</param>
public void GetRequest(string url, ParameterCollection param, resultResponseCollection argOperate,exceptionHandler exHandler)
{
	if (TwitterInfoCS.IsNotAuthed)
	{
		exHandler(new Exception("Twitterに認証されていません、認証してからもう一度やり直してください"));
	}
	else
	{
		var oauthClient = new OAuthClient(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret, TwitterInfoCS.OAuthToken, TwitterInfoCS.OAuthTokenSecret)
		{
			Url = url,
			Parameters = param,
			ApplyBeforeRequest = req => { req.UserAgent = TwitterInfoCS.TwitterUserAgent; },
		};
		oauthClient.GetResponse()
			.Subscribe(s => argOperate(s), x => exHandler(x));
	}
}

private IDisposable streamOAuthClient;

/// <summary>
/// StreamingAPIへの通信を行います
/// </summary>
/// <param name="url">通信先のurl</param>
/// <param name="param">ヘッダに追加する引数</param>
/// <param name="argOperate">レスポンスを処理するデリゲート</param>
/// <param name="exHandler">例外を処理するデリゲート</param>
public void GetRequestStream(string url, ParameterCollection param, resultTextCollection argOperate, exceptionHandler exHandler)
{
	if (TwitterInfoCS.IsNotAuthed)
	{
		exHandler(new Exception("Twitterに認証されていません、認証してからもう一度やり直してください"));
	}
	else
	{
		streamOAuthClient  = new OAuthClient(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret, TwitterInfoCS.OAuthToken, TwitterInfoCS.OAuthTokenSecret)
		{
			Url = url,
			Parameters = param,
			ApplyBeforeRequest = req => { req.UserAgent = TwitterInfoCS.TwitterUserAgent; },
		}.GetResponseLines()
			.Subscribe(s => argOperate(s), x => exHandler(x));
	}
}

/// <summary>
/// Streaming通信を終了します
/// </summary>
public void StreamClientDispose()
{
	streamOAuthClient.Dispose();
}

/// <summary>
/// POSTリクエストを行います
/// </summary>
/// <param name="url">通信先のurl</param>
/// <param name="param">ヘッダに追加する引数</param>
/// <param name="argOperate">レスポンスを処理するデリゲート</param>
/// <param name="exHandler">例外を処理するデリゲート</param>
public void PostRequest(string url, ParameterCollection param, resultResponseCollection argOperate, exceptionHandler exHandler)
{
	if (TwitterInfoCS.IsNotAuthed)
	{
		exHandler(new Exception("Twitterに認証されていません、認証してからもう一度やり直してください"));
	}
	else
	{
		var oauthClient = new OAuthClient(TwitterInfoCS.ConsumerKey, TwitterInfoCS.ConsumerSecret, TwitterInfoCS.OAuthToken, TwitterInfoCS.OAuthTokenSecret)
		{
			Url = url,
			MethodType = MethodType.Post,
			Parameters = param,
			ApplyBeforeRequest = req => { req.UserAgent = TwitterInfoCS.TwitterUserAgent; },
		}.GetResponse()
		.Subscribe(s => argOperate(s), x => exHandler(x));
	}
}

今回はWebResponseで扱いたいのでこんな感じにしました。WebExceptionが起きた場合にResponseをそのままデリゲートに入れることができるからです
そんなこんなで一応準備できたので次回はツイートしてみましょう。そこまで難しくはないです