kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

月別アーカイブ: 6月 2012

an Quick Start for Roslyn (2)–IText と TextSpan


というわけで Roslyn でのパース結果の詳細な見方、主に位置情報について。

前回は SyntaxNode を列挙して軽く眺めてみただけですので、その先の詳細を見ていきましょう。

まず、SyntaxNode には全く文字列は保持されていません。SyntaxNode には Span と FullSpan というTextSpan型のプロパティがありこれらのプロパティには開始位置と長さが保持されていて SyntaxTree の IText 上の範囲の文字列が SyntaxNode の文字列を表現します。

IText の取得方法

まずダメな方法です。

各 SyntaxNode には GetFullTextAsIText() があります、このメソッドはその要素の内容を示す IText を返します。結果のIText の中での位置 0 はその要素の先頭を示しますので、もとになるITextとは別のものです SyntaxNode の位置を示すSpan/FullSpanプロパティの値は意味を持ちません。

続いてイケる方法です。

各SyntaxNodeはSyntaxTreeへの参照を持っています。SyntaxNode.SyntaxTree プロパティ、このSyntaxTreeは TryGetRoot メソッドでルート要素を取得したSyntaxTreeへの参照を保持しています。SyntaxTreeのGetTextはITextを返しますので、このITextを利用します。

また IText は StringText クラスで実装されていますので、 文字列を元に IText を構築することができます。IText をパラメータとして ParseCompirationUnit によってSyntaxTreeを取得することができます。このSyntaxTree のGetTextはもとになったStringTextを返します。

            var text = new StringText(@"using System;

namespace RoslynTest
{
    public class Program
    {
        public static void Main( string[] args )
        {
            Console.WriteLine( Console.ReadLine() );
        }
    }
}
");
            var tree = SyntaxTree.ParseCompilationUnit(text);
            var itxt= tree.GetText();
            Console.WriteLine(object.ReferenceEquals(text, itxt));
            Console.ReadLine();

結果: True

結果的に SyntaxNode の SyntaxTree プロパティは ParseCompilationUnit の返した SyntaxTree であり、SyntaxTreeから取得された IText は ParseCompilationUnit に渡された、または内部で生成されたITextで、これらは常に同一の参照を返します(imuttableであるといえます)ので元にしたIText を利用する、SyntaxTreeからITextを取得するのは同じ事で、SyntaxNodeからSyntaxTreeを参照してGetTextでITextを利用するのも同じことです。

僕は自分でStringTextで生成したIText (コード中のtext変数)を直接に参照するようにしました。

.NET Framework は純粋さを前提にできないので、各SyntaxNodeがSyntaxTreeの同一の参照を返すという仮定はできないはずだし、SyntaxTreeのGetTextが返すITextが常に同一という仮定もできないはずなので、同一の参照を使い続けるという仮定を実現させるにはできるだけ早い時期に取得したIText をそのまま使う必要があります。また new を明示的にしているのでnullではないという仮定も成立するはずなので null チェックを省略させる意味もあります。枝葉の最適化にコダワル所じゃないのですけど脳内で降りた判定を明かしておくと「同じこと」という物に微妙な効率差が有りそうと踏んで選んでることだけはなんか書きたかったので書いときます。

オプティマイザの観点でいうと仮定できないというと語弊があって、楽観的に仮定してみて仮定に危うさを感じる要素が一つでも見つかればその仮定を取りやめるのが普通だったりします。この楽観的仮定を最後まで通してあげるとオプティマイザが効率的に処理するコードに落としてくれるわけですので、危うさを臭わせないほうが早いコードが出るという事になります。

TextSpanとSyntaxNodeのSpan/FullSpanの違い

さて、ITextがあり、ITextと関連性のあるTextSpanがあればstring IText.GetText( TextSpan ) によって実際の文字列を取得する事ができます。SyntaxNode は Span と FullSpan の二つのTextSpan型プロパティを持っていてそれぞれ構文要素そのものと、その構文要素に付随するデリミタを保持します。

TextSpan は operator == / operator !=の適切な演算子オーバーロードを持っていますので、これらの違いが見える部分だけを抽出してみましょう。

        // 概要:
        //     Determines if two instances of TextSpan are different.
        public static bool operator !=(TextSpan left, TextSpan right);
        //
        // 概要:
        //     Determines if two instances of TextSpan are the same.
        public static bool operator ==(TextSpan left, TextSpan right);

前回のコードを元に Span と FullSpan が異なる要素だけテキストを取得して表示しているのが以下になります。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using Roslyn.Services.CSharp;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var text = new StringText(@"using System;

namespace RoslynTest
{
    public class Program
    {
        public static void Main( string[] args )
        {
            Console.WriteLine( Console.ReadLine() );
        }
    }
}
");
            var tree = SyntaxTree.ParseCompilationUnit(text);
            var itxt= tree.GetText();
            Console.WriteLine(object.ReferenceEquals(text, itxt));
            Console.ReadLine();

            Stack<SyntaxNode> nodes = new Stack<SyntaxNode>();
            SyntaxNode node;
            tree.TryGetRoot(out node);
            nodes.Push(node);

            while (nodes.Count != 0)
            {
                node = nodes.Pop();
                if (node.Span != node.FullSpan)
                {
                    Console.WriteLine("Full:\t"+node.FullSpan +"\'"+ text.GetText(node.FullSpan)+"\'");
                    Console.WriteLine("Span:\t"+node.Span +"\'"+ text.GetText(node.Span)+"\'");
                }

                foreach (var child in node.ChildNodes())
                {
                    nodes.Push(child);
                }
            }
            Console.ReadLine();
        }
    }
}

わかりやすい例としては以下の様な表示が含まれます。

Full:   [136..190)’            Console.WriteLine( Console.ReadLine() );

Span:   [148..188)’Console.WriteLine( Console.ReadLine() );’

FullSpan には前方のスペースおよび、後方の改行が含まれますが、Spanには含まれない事が解ります。

それって何行目?その行は?

IText には int GetLineNumberFromPosition(int position )があり、これにより指定の位置の行の番号を得る事ができます。

行番号、position ともに0始まりのようです。

            SyntaxNode node;
            tree.TryGetRoot(out node);
            Console.WriteLine(node.FullSpan.Start);
            Console.WriteLine(text.GetLineNumberFromPosition(node.FullSpan.Start));

の出力は 0, 0 です。

同様に IText には ITextLine GetLineFromPosition(int position ) があり、指定の位置を含む行を取得することができます。

ITextLine の実装である TextLine は ToString をオーバライドしていないので単純に ToString すると残念な結果である型名だけが出ます。GetText で内容を取得すると、行末の改行を含まない文字列が返されます

ITextLine はいくつかのプロパティを持っていて、これらで行の範囲を示します。

Extent は行の改行を含まない範囲を示すTextSpanを返します。ExtentIncludeLineBreakは同様に改行を含む範囲を返します。Start、Endはそれぞれ行頭の位置、行末の位置を示します。EndIncludeLineBreakは改行を含む行末です。

(2)のまとめ

シンタックスツリーの操作をやるって言っておきながら、書いてみたら長くなったので回を分けるというやつで、今回は SyntaxNode の位置情報である TextSpan と、位置情報と文字列の関係を示す IText 、位置と行の関係を示す ITextLineをもとに SyntaxNode の位置を解説してみました。

次回もシンタックスツリーの操作にはきっと入れずにシンタックスに付随する SyntaxTrivia についての話となると思います。

an Quick Start for Roslyn (1)


というわけで、プロジェクトテンプレートに並行して Roslyn を触り始めてみました。

Roslyn が何であるかについては ufcpp さんが既に書かれてた記事 があるのでそちらを見てもらうとして、Compiler 内部のコンポーネントをユーザーが利用することができる物という認識で良いと思います。追加してシンタックスツリーを作るとそれのテキスト化(ソースコード化)等、いわゆる言語サービスに類するもの、これまでは Visual Studio と C# コンパイラーの内部でしか利用できなかった物が比較的簡単に利用できるようになっています。

自分としてはこれは言語内DSL等に使える他結構色々と使いでのあるツールなのですが、何しろコンパイラーという巨大な仕組みの内部が公開されたという事は大量のクラス、メソッドが表に出てきたわけで見るのも大変である事は事実で、とっかかりを得るのは結構苦労しそうだなと。根が優しい人なので、この辺のとっかかりについて書いておく事で後の人にもっと奥まで進んで貰えるかな等というわけで書いてみます。

disclaimer

この記事は Roslyn の製品版が出る以前に書かれていますので、RTM版では一部の手順や実装が異なる事が十分にあり得ます。blog記事なんて生ものだと思ってる人なので追補とかする気は全然ありませんので、遠い未来から突っ込みされても困ります。「あー、困った困った。」で流しちゃうと思いますけどご了承ください。

インストール

2012/06/16 現在で最新の Roslyn は以下からダウンロードできます。(2012/6/5 リリースのものです)

Microsoft “Roslyn” CTP 

普通にインストールすれば入ると思います。

インストールすると Roslyn のプロジェクトテンプレートが利用可能になります。

image

含まれているプロジェクトテンプレート

Console Application は普通のコンソールアプリケーションですが、それ以降は vsixmanifest が含まれている事からお察しの通り(お察しできないかもしれませんが)、Visual Studio 拡張のプロジェクトです。

以下プロジェクト作成直後のソリューションエクスプローラーの表示をペタペタと。

imageimageimageimageimage

まぁとっかかりとしての選択肢は Console Application 一択でしょう。 VS拡張を実装するのは追加の知識が大量に必要になってしまうので別個の知識として身に着けてからでないと必要知識の山の前に簡単に遭難できますので。

コンパイラーパイプライン

最初に Roslyn をダウンロードしたページの「Related resources」にある Microsoft “Roslyn” Project Overview› を見ると最初の方にある図を引用させてもらいます。これを読みながら一緒に見てもらうとわかりやすいかも。

CompilerPipeline

コンパイラーの内部処理は Parser で文字列なソースから構文木への変換がかかり、該当ソースで定義された要素が Symbols 、参照しているアセンブリが Metadata Importで取り込まれ、Binder で構文木から実際のコードに相当するモデルが構築され、IL Emitter がそれをPEアセンブリとして書き出すわけで、この各段階と各段階をまたぐデータ構造(構文木、シンボルストア…etc)が Roslyn で API 化されています。

Parserの呼び出しと簡易的なシンタックスツリーの表示

Parser には SyntaxTree の公開するスタティックメソッド、ParseCompilationUnit を介してアクセスします。構文木をスタックに積みながら要素の種別とテキストでの内容を表示するサンプルを以下に示します。

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; using Roslyn.Services; using Roslyn.Services.CSharp; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var tree = SyntaxTree.ParseCompilationUnit( @"using System; namespace RoslynTest { public class Program { public static void Main( string[] args ) { Console.WriteLine( Console.ReadLine() ); } } } "); stopWatch.Stop(); Console.WriteLine(stopWatch.Elapsed); Console.ReadLine(); Stack<SyntaxNode> nodes = new Stack<SyntaxNode>(); SyntaxNode node; tree.TryGetRoot(out node); nodes.Push( node ); while (nodes.Count != 0) { node = nodes.Pop(); Console.WriteLine(node.Kind); Console.WriteLine(node.GetText()); foreach (var child in node.ChildNodes()) { nodes.Push(child); } } Console.ReadLine(); } } }

 

表示としては以下の様になりました。

image

パースに21ms ぐらい要してる様に見えますが初回実行コスト(Roslyn のアセンブリの遅延ロードやJITコンパイル)が含まれていてこの速度は十分に早いと言っていいと思います。

IEnumerable<SyntaxNode> の foreach だけで実装できている事からわかる通りで簡単に LINQ の餌食になりそうに見えます。ただし、LINQ は再帰的なツリー構造に必ずしも強くないので多少の補完ライブラリが必要でしょうかね。

(1)のまとめ

内容としては Roslyn の ParseCompilationUnit を回して構文木を得た、軽く内容を表示してみて解析ができていることを確認した段階です。

少なくともコードは完全にSyntaxTreeに分解されていますので、既存コードに含まれがちな foreach での多段ネストループを検出するとかはこれで十分にできます。「コード的にはバグじゃないけどこのコードの実装者の頭ちとおかしいんじゃない?」を検出する検出器は検出条件が構文的に決まってしまえばこの段階で作れますのでぜひ作ってみてください。

アセンブリを参照設定していれば使えるわけですので msbuild のカスタムタスクで検出器を回すことができればあなたのソースのビルド時にチェックするとかもきっとできるでしょう。

 

この構文木を編集するなどを(2)でやってみたいと思います。単純な構文変換であればこの処理で十分ですが、意味を維持するという場合には相応の処置が必要なので意味を含む変換は(3)以降になると思います。

Metro Style Application 開発の為の Visual Studio Project Template(1)


Visual Studio 2012 RC には Metro Style Application のためのプロジェクトテンプレートが幾つか含まれています。

これらのプロジェクトテンプレートからプロジェクトを起こしてみての解説を2回ぐらい書きましたが、今回はこのプロジェクトテンプレートの実体がどのように定義されているか、その内容を確認するとともに今後の開発で使うプロジェクトテンプレートの準備などに焦点を当ててみたいと思います。

標準のプロジェクトテンプレートのインストール場所

Visual Studio 2010 RC においてプロジェクトテンプレートの標準のインストールパスは以下の通りです。(Windows 8 RP x64にインストールした場合)

C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ProjectTemplates\CSharp\Windows Metro style

このフォルダを開くと1041フォルダ配下に実際のプロジェクトテンプレート毎にフォルダがあり、プロジェクトテンプレートの実体が格納されています。

imageimage

このファイル群をベースに、プロジェクトテンプレートを修正してもいいですし、プロジェクトテンプレートを新規に作成してもいいでしょう。

Visual Studio SDK によるプロジェクトテンプレートの作成

プロジェクトテンプレートを作成するには簡単な方法としてはある程度作りこんだプロジェクトを元に Visual Studio から [ファイル]-[テンプレートのエクスポート]があるのですが。根がマゾいので簡単な方法は使わずにプロジェクトテンプレートを一から作成する事にしましょう。

この作業を行うには Visual Studio SDK が必要になります。

Download: Visual Studio 2012 RC SDK – Microsoft Download Center – Download Details

をインストールしてください。インストールを完了すると Visual Studio からプロジェクトの作成時に機能拡張配下からプロジェクトテンプレートが作成できるようになります。

image

今回はMetro Style Application の為のクラスライブラリを単体テストプロジェクトと共に生成し、ビルドのカスタマイズ等も(比較的)簡単にできるようビルドのカスタマイズプロジェクトも設定するという形でちょっと規模の大きいプロジェクトテンプレートを作成してみます。

Metro Style Application の為のプロジェクト要件の確認

ここで既存のプロジェクトテンプレートの内容を確認しましょう。プロジェクトテンプレートの中核を担うのは vstemplate という拡張子がついたファイルで、このファイルをもとに関連ファイルが取り込まれてプロジェクトが生成されますので Metro Style Application としての基本的な設定事項を把握しなければ話が始まりませんが、歴代の vstemplate はその詳細まで解説された事は殆ど皆無じゃないかな程度に情報量が少ないので、自分達の手元にあるVSにインストールされた vstemplateを見るのを最初にするのをおすすめです。

標準のプロジェクトテンプレートの ClassLibrary の vstemplate の内容とプロジェクトテンプレートを作成する場合の vstemplate には以下の要素に違いがある事がわかります。

<TemplateGroupID>WinRT-Managed</TemplateGroupID>
<RequiredFrameworkVersion>4.5</RequiredFrameworkVersion>
<TargetPlatformName>Windows</TargetPlatformName>
<RequiredPlatformVersion>8</RequiredPlatformVersion>
<CreateInPlace>true</CreateInPlace>

実際には当然に TemplateID などは違うのですが、そりゃテンプレートが違えばちがうわなで今回はスルーします。

TargetPlatformName や RequiredPlatformVersion 等が明らかに増えていますので、これを持つ vstemplate を作ります。

Metro Style Application の為の csproj 設定

いやー増えてますねー、Any CPU, x86, x64, ARM それぞれに Debug/Release でビルドするので当然ですが。全部ビルドしようとかすると単純に時間は20~30%増しって計算にちょっとgkbr

さて、大きな変更がありますね、vs2010までの csproj では Microsoft.CSharp.targets を見ていた物が $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets を見ています。

XAMLのコンパイルタスクが必要なのはわかるのですが、このターゲットの解決に Visual Studio Version が入るってことは本気に Visual Studio インストールしないと(SDKだけだと)ビルドもできないのかも。ビルドサーバを用意する上でこれはちょっと難点ですね。最低限のExpressでも入れとけばビルドできるといえばそうなのかもしれませんけど。(ビルドしかできないかもしれませんけどね)

細かい詳細は基本的には丸コピー以外の選択肢は殆ど無い気がします、各 Configuration 毎に最低限のビルドオプションを設定したうえでソースはCompile ItemGroupにというのが基本的なクラスライブラリプロジェクトの構造です。

次の段階として、関連するテストを収容するための単体テスト用プロジェクトもこのテンプレートに取り込むのですが、順番にやるのが良いでしょう、ここで一旦 (1) として区切りをつけようと思います。

(1) までのまとめ

とりあえず github に push しときました。

リポジトリはこちら https://github.com/kazuk/MetroAppBase

ここまでの内容のコミットはこちら https://github.com/kazuk/MetroAppBase/commit/28e89e2623d54991cf9bcb57ea69dc5d270f0a29

予習したい人の為に (2) で何をするかという事で MSDN ドキュメントの参照リンクなど

方法 : 複数プロジェクトのテンプレートを作成する

(2)ではMetroスタイルアプリケーションのテストプロジェクトについて調べた上で、このプロジェクトテンプレートに組み込む事でクラスライブラリを作成と同時に単体テストを活用した開発ができる様にしようと思います。

まとめでは無いこと

MS的には Visual Studio 2012から TFS Express を使ってねとか色々あるみたいなんですが、少なくともこれまで経験から 「TFS は肌に馴染みませんでした。」(いいなーと思うところも無いわけではないけど、VSが落ちるとか落ちるとか落ちるとかで寧ろ足を引っ張られる印象の方が強く)というわけでVS2012での自分の開発環境での採用は無い感じです。

VS2012世代で自分の目論む開発環境ですが、基本的には git / github をソース管理のメインに据えてこれをビルドするビルドサーバは TeamCity か Jenkins か検討中、現状は VS2012のUltimateを90日試用期間で使ってますが(OSがCP/RP/RTMと行く流れで再インストールになりながらそれぞれ最大90日使えればまぁお得!)、最終的には Premium 辺りに落ち着くんじゃないかと思っています。

ソース管理の基本が TFS から git/github になる事で自分の開発環境整備(包丁を研ぐ段階)の物が表に出せるし出てしまうので晒しでやっていこうかなとか。git/github に出してる物は MIT ライセンスのつもり(どこに書くんだろう)なのでお好きにパクッテって頂戴。

githubでの pull request とか貰った事ないので、なんか楽しい変更があれば pull request も遠慮なく。

個人事業主になりまして昼間は他のお仕事しながら夜のお勉強タイム確保+お子ちゃま台風上陸という私事もありまして一日一時間も勉強に取れないので遅々として進まずの可能性もなきにしもですが今後うん十年仕事する上での準備なので着実に書いていければなーという感じです。

Metro Style Application Code Samples–XAML Data Binding Samples


Windows 8 Code Samples and Examples in C#, VB.NET, C++, JavaScript

とりあえずインストールした Visual Studio 2012 RC で Metro Style App のプロジェクトを作ると

// The Split App template is documented at http://go.microsoft.com/fwlink/?LinkId=234228

って感じでリンクされている LinkId 234228 でたどり着くサンプルギャラリー。

質はわからないけど量は十分と言っていい気がするし、一覧を眺めてみた限りでは内容も多岐にわたっている気がする。このあたりは Microsoft本気だなと思わないでもない。

日本語での情報提供は翻訳の都合で遅れるのが常なので世界についていく気なら英語だろうがコードだろうが気合で読むのみで色々見るのをおすすめ。龍に乗るなら首根っこ、尻尾掴んでも振り落とされるのが落ちだ。

というわけで、もっとも基本っぽい XAML Data Binding sample をチェックしてみるなど。コンパイル&実行は全く問題なく完了、普通に実行しながらソースを確認することをお勧めです。

チェックの目的は WPF や Silverlight との相違点等を確認しておきたいということなので、 WPF / Silverlight のソースをあまり見たことが無い人はもっとチュートリアル的な物から見たほうが良いかも。

 

Scenario1 ~ Scenario7 までの xaml と xaml.cs が本体と言っていい感じ。

Scenario1.xaml XAMLのデータバインディングの { Binding … Mode=[バインディングモード] } のバインディングモードの TwoWay, OneWay, OneTime の違いを見せようというもの。 WPF / Silverlight との違いは特に見えず。

簡単にさっくり解説すると以下の通り。

TwoWay の場合、テキストボックスに入力した内容はスライダーに反映され、スライダーの内容はテキストボックスに反映される。このような双方向のバインディングを行う場合には TwoWay を使う。もっともよく使うモードだと思う。

OneWay の場合、スライダーの操作はテキストボックスに反映されるけど、テキストボックスの修正はスライダーに反映されない。このような一方向のバインディングを行う場合には OneWay を使う。テキストボックスのような入力を受けるコントロールと結びつけると変に感じるが表示専用の物につなぎ合わせるにはこっちの方がお得というもの。

OneTime の場合、コントロールの初期化時に一発反映したら後はやらないよという物。変更前の値を表示しておきたいという用途には便利だけどそれ以外での局面では全く役に立たない系。

 

Scenario2 は IValueConverter によってスライダー値を F から SUPER STAR! の段階を示す文字列に変換してテキストボックスに変換するもの。 E への変換が無いねとか余計。

この変換は S2Formatter.cs にて宣言されている Windows.UI.Xaml.Data.IValueConverter の実装によって変換されている。

<StackPanel.Resources>
    <local:S2Formatter x:Key=”GradeConverter”/>
</StackPanel.Resources>

で S2Formatter を GradeConverter で宣言、

Text=”{Binding ElementName=sliderValueConverter, Path=Value, Mode=OneWay,
              Converter={StaticResource GradeConverter}}”

で GradeConverter を利用している。 Mode が OneWay なので S2Formatter での変換も一方向しか実装されていない。

S2Formatter.cs の実装内容はあまりマネしてほしくないところ、メソッド内のローカル変数に _ でプリフィックスをつけるとか基本として見たことのないネーミングスタイルなので注意。

 

Scenario3 は Emploee というクラスにデータバインディングするというもの。 Emploee は Scenario3.xaml.cs からMainPageのDataContextとして渡されています。

単純には DataContext に渡したオブジェクトとデータバインディングされますよという内容とみればいいでしょう。

 

Scenario4 は Team.cs で定義された Teams リストの3番目の要素にデータバインディングを行うという物。

Binding のPathシンタックスでの ‘[‘ + インデックス + ‘]’がインデックスが整数(配列ないしはリスト)、その他(コレクション)でバインディングができるという事を示しています。

 

Scenario5 は同じく Team.cs で定義された Teams リストにバインディングしますが、 Border の Background を Team のColor にバインディングすることで背景色を変えるというもの。 Text や Value 以外でもバインディングできるので、見た目をバインディングで変えられるということを示しているだけですね。

 

Scenario6 は同じく Teams にバインディングしますが、ListItem のGroupStyleを介してHeaderTemplate を与えることで、Key 要素ごとにヘッダーを表示するというもの。

var result = from t in teams
             group t by t.City into g
             orderby g.Key
             select new { Key = g.Key, Items = g };

のLINQクエリによってグループ化された物のKeyをグループヘッダーとして表示しています。LINQクエリとグループヘッダテンプレートの合わせ技でそこそこ簡単にできますよってところです。

 

Scenario7は同じくTeams にバインディングしますが、選択要素の削除を実装しています。ハンドラの実装は以下のもの。

void BtnRemoveTeam_Click(object sender, RoutedEventArgs e)
{
    if (ocTeams.Count > 0)
    {
        int index=0;
        if (lbTeams.SelectedItem != null)
            index = lbTeams.SelectedIndex;
        ocTeams.RemoveAt(index);
    }
}

データバインディングですべてが行われていますので単純にコレクションから要素を削除すれば要素が消えますってもの。

選択されてなかったら最初の要素を消すという乱暴な実装に見えますが良いんでしょうか?コピペしないでね?なサンプルは勘弁してほしいですね。

気になるところとしては

<Button x:Name=”btnRemoveTeam” Content=”Remove team”/>

と Click ハンドラの結び付け、コンストラクタで以下を書いてますが、Commandパターンとかは入ってない感じですかね?コードビハインドにコードを書きたくないタイプの人なのでこういうのがずらずらしちゃうのはちょっといやんな感じです。

btnRemoveTeam.Click += BtnRemoveTeam_Click;

 

まとめ

そんなに大きく WPF / Silverlight と異なる印象はありません。名前空間の配置など実装は当然に変わっていますが大きくは変わっていないということが解っただけでした。

テクノロジの特性なのかもしれませんが、周辺がいろいろと冗長です。XAMLなどは説明したい部分が埋没していると思われるぐらいです。何を説明しようとしているのか、それを読み取って見ないと迷子になりますのでポイントをかいつまんで読みましょう。かいつまんで読むことができる人(ポイントのわかってる人)はこのレベルのサンプルは見ないというか必要ないかもな気もしないでもないですけど。

Scenario2 で現れる IValueConverter はお察しの通り大量に書くことになるでしょう。データ値と見た目のフォーマットの変換処理はほとんどの場合 IValueConverter で実装することになります。これの実装の為の項目テンプレートが標準のプロジェクトテンプレートにはありませんので、項目テンプレートを切り出ししておく事をお勧めです。ただしこのサンプルに含まれる IValueConverter の実装は変数名のネーミングがいまいちで普通のコーディング規約にはマッチしないはずなのでもう少し良い物をベースに選ぶか納得いく形に書き換えてから、コーディングのヒントをコメントにふんだんに盛り込んだ項目テンプレートを作るのが良いでしょう。

Visual Studio 2012 にはネーミングルールによる変数名の警告や補正の仕組みは入らなかった模様、必要な人は ReSharper を入れるとルールに沿ってない場合には警告してもらえるし QuickFix で大抵はいい名前に直してもらえますのでこの機会に投資する気ならした方がいいかもしれません。(ちょっとReSharper以外に浮気しようかと思ってVSギャラリー見回してみたのですがグッとくる物は見当たらなかったです)

良いサンプルだとは思いますが、コレクションからの削除の実装が選択されていなければ先頭を消すとか鵜呑みにしてはいけない内容を多分に含んでいますのでご注意を。