飴屋

WPF/日記11

POST送信

前回、Application.GetRemoteStreamを使用して、やっと自分のサイト(site of origin)に設置したXMLファイルを読み込むことができるようになったのですが、Uriオブジェクトにクエリ情報をつけてGETメソッドでチャットの文字入力内容を送信することになってしまい、Uriの最大長をオーバーするようなメッセージは送れなくなってしまうなと、ちょっと心配になった私は、しつこくPOSTメソッドでデータを送信する方法を探っていました。

そして、「pack://siteoforigin:,,,/〜」といったUriがメソッドによっては受け付けられないことに気付きました。「URIプレフィックスが認識されません。」とかいうよくわからないエラーメッセージが出てきてしまうわけです。なんでそんなメッセージが出るのかといった根っこから調査した方が確実な知識につながるのはわかっていますが、興味がわかないのでそこは保留にしつつ、トライアンドエラーで何ができて何ができないのかを体当たりで探しました。

結果的にWebClientオブジェクトを使ってデータのPOST送信も受信も可能になりました。それと同時に自分を悩ませていた問題の原因の尻尾もつかめました。自分で作成したXBAPは、「デバッグ開始」ボタンをポチッと押して動作を確認していた私は、WebClientを使ったデータの送受信によって、今までSecurityExceptionをthrowされてきたため、WebClientは使えないものに勝手に分類してしまっていたのですが、いざ、プロジェクトを発行してFTPでサーバに上げてみると、セキュリティ関係の例外が出ることなく無事データの送受信に成功しました。

UploadStringはHTTP通信時にはデフォルトでPOST送信してくれるとのこと。

string message = "m=" + Uri.EscapeUriString(Input.Text) + "&a=myavatar&color=" + Uri.EscapeUriString(currentColor);
WebClient wc = new WebClient();
wc.UploadString(new Uri("http://soft.candychip.net/xml/test.cgi"), message);

指定のUri「http://soft.candychip.net/xml/test.cgi」は、動作確認用に作ったperlのスクリプトです。POSTデータの内容をファイルに書き出すだけのものですが、POST送信された内容がきっちり確認できました。XBAPでどこまでできるのかいまだグレーのままですが、とりあえずこれがあれば大抵のことはできるという確信のもと、この話はこれ以上混ぜ返さないことにします。

むしろ問題は、デバッグの方法でしょう。前回、「アプリケーションのデバッグ時の挙動に関する設定」をしたはずですが、結局Visual Studio上でデバッグするのと、サイト上に発行されたものをClickOnceでインストールして実行するのとで挙動が異なるのなら、なんとも不便です。きっと設定でなんとかできるのだと思いますが、自力で設定を探るのは今回は辞めておきます。

とりあえず例外処理を加えて、デバッグが途中で停止してしまわないようにだけしておきました。

try
{
//WebClientでPOST送信
}
catch (System.Security.SecurityException se)
{
//例外処理
}

(追記)やっぱり気になったので、プロジェクトのプロパティの「セキュリティ」タグで「ローカル イントラネット」の「WebPermission」の設定を「必要」に変更してしまいました。これでデバッグ時の挙動も同じようになる・・・のかな。

フキダシの表示

会話データの取得もできたことなので、そろそろアバタの頭からフキダシでも表示してみようと思います。フキダシは漫画なんかで使われているので、しゃべってる感の演出には丁度いいと思います。テキストボックスにログがドンドンたまっていく形態が一般的ではありますが、フキダシの場合はドンドン古い情報が消えていくことになりますから、よりリアルタイムな対話が強制されることになりそうです。スリリングというか、不便というか、リアルというか・・・フキダシを消さずにたくさん出しっぱなしにするわけにもいかないですもんね。

リソースの追加

<ModelVisual3D x:Key="TalkingBox">
<ModelVisual3D.Transform>
<ScaleTransform3D ScaleX="0.3" ScaleY="0.3" ScaleZ="0.3"/>
</ModelVisual3D.Transform>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D>
<MeshGeometry3D.Positions>
<Point3DCollection>
-5,5,0 -5,0,0 5,0,0
5,0,0 5,5,0 -5,5,0
</Point3DCollection>
</MeshGeometry3D.Positions>
<MeshGeometry3D.TextureCoordinates>
0,0 0,1 1,1
1,1 1,0 0,0
</MeshGeometry3D.TextureCoordinates>
</MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush Stretch="Fill">
<VisualBrush.Visual>
<Canvas>
<Path Canvas.Top="0" Canvas.Left="0" Fill="LightYellow" StrokeThickness="2" Stroke="Chocolate">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="90,97" IsClosed="False">
<LineSegment Point="100,147" />
<LineSegment Point="110,97" />
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry RadiusX="10" RadiusY="10" Rect="0,0,200,100"></RectangleGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
<TextBlock Margin="10" Width="200" Height="80" Canvas.Left="0" Canvas.Top="0">TEST</TextBlock>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</MaterialGroup>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>

フキダシに必要な要素は「テキスト」「枠」「ポインタ」の3つです。会話の内容が「テキスト」に対応し、そのコンテンツへ注意をひきつけたり表示範囲を確定させたりするビジュアル要素が「枠」となります。「ポインタ」は話者を示すもので、三角形の尖がりの先っぽが話者を指し示します。

テキストの内容は動的に変化しますが、とりあえずTextBlockだけ用意しておき、後で中身を変更できるようにしておきます。漫画を見る限り「枠」と「ポインタ」は連続したセグメントでできていることが多いので、CombinedGeometryを使って両者をUnion結合してあります。「枠」はRectangleGeometryの角を丸めたものを使い、「ポインタ」はPathGeometryで簡単な三角形を描いてみました。現在、「枠」の中央から「ポインタ」が出ていますが、アバタが画面の端に移動した場合などに備えてポインタの位置も動的に調節する必要が出てくるような気がします。

上記の3要素をCanvasに描き込んだものをブラシに設定して、GeometryModel3DのMaterialに設定します。フキダシもカキワリと同じく2枚の三角形で作った薄っぺらい四角の板です。VisualBrushを使うとこの板に自由にお絵描きができるというのだからすごいですね。WPFの表現能力を少しは引き出せた気がします。

この板は「TalkingBox」というキーをつけてリソースに追加しておきました。

フキダシの呼び出し

次はコードからリソースのフキダシを呼び出します。これはアバタの呼び出しと同じ要領なので、特に悩むこともありませんでした。

ModelVisual3D talkingbox;
private void EnterInput(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
talkingbox = (ModelVisual3D)ChatWorld.FindResource("TalkingBox");
ChatWorld.Children.Add(talkingbox);
}
}

とりあえず、メッセージの入力を受け付けたタイミングで表示してみました。

ストローク色を「Chocolate」、背景色を「LightYellow」に設定したわりにくすんだ色で表示されました。これは3D空間内のライトの設定がまずいということなんでしょう。アバタにも濃い影が落ちてますもんね。次はフキダシの位置をアバタの上に設定してみましょう。

talkingbox = (ModelVisual3D)ChatWorld.FindResource("TalkingBox");
TranslateTransform3D translate = new TranslateTransform3D(pos.X*0.0333333,0,pos.Z*0.0333333);
talkingbox.Content.Transform = translate;
ChatWorld.Children.Add(talkingbox);

TranslateTransform3Dにアバタの現在位置(pos)のX座標とZ座標を渡して、それをフキダシのTransformに設定してやりました。座標を0.0333333で割っているのは、何の設計もせずにアバタとフキダシを作ってしまったため、それぞれのScaleTransform3Dでスケールを「0.01」や「0.3」に調整することになったツケです。

WPF