ページ

2011年6月9日木曜日

[Silverlight] Silverlight 4 の ScrollViewer がマウスホイールでスクロールしない!(バグってる?)

確か記憶では、、、

Silverlight 2 のころはそもそもマウスホイールはサポートされてなかったので HtmlPage のホイールイベントをひろってどうのこうのと。。。

そして、Silverlight 3 では Silverlight Toolkit に ScrollViewer のマウスホイール対応クラスが入ってて scrollViewer.SetIsMouseWheelScrollingEnabled(true) って呼び出せばいいだけになってくれて。。。(ちなみに、SetIsMouseWheelScrollingEnabled は ScrollViewer の拡張メソッドとして実装されてるとのでこういうことができる)

それから、Silverlight 4 で標準でサポートされて、ScrollViewer は何もしなくてもマウスホイールでスクロール可能になった!

、、、ような気がするんだけど、Silverlight 4 でマウスホイールに反応しない。
検索してみたら Connect にこんなのあった
Mouse wheel does not work correctly with the Silverlight 4 ScrollViewer
うーむ、ブラウザーのせいなのか何のせいなのかわかんないけど、どうやらダメなときはダメみたい。
で、「回避策」 にあるように

<ScrollViewer Background="Transparent" ... > 

としてやれば OK だった。
うーむ。

Background を透明にしたくないってときは以下のような感じでいけるみたい。

private void LayoutRoot_MouseWheel(object sender, MouseWheelEventArgs e)
{
    var point = e.GetPosition(this.scrollViewer1);
    if (point.X < 0 || this.scrollViewer1.ActualWidth <= point.X || point.Y < 0 || this.scrollViewer1.ActualHeight <= point.Y)
    {
        // マウスが ScrollViewer の上で無い
        return;
    }

    if (!e.Handled)
    {
        double position = CoerceVerticalOffset(this.scrollViewer1, this.scrollViewer1.VerticalOffset - e.Delta);
        this.scrollViewer1.ScrollToVerticalOffset(position);
        e.Handled = true;
    }
}

private double CoerceVerticalOffset(ScrollViewer viewer, double offset)
{
    return Math.Max(Math.Min(offset, viewer.ExtentHeight), 0.0);
}

どういうわけかわかんないけど、Background が Transparent で無いときはなぜか ScrollViewer に MouseWheel イベントがこず、背面の LayoutRoot の方にいっちゃうみたい。なので、LayoutRoot の方で MouseWheel を受けて ScrollViewer をスクロールさせてやる、と。
ちなみに、スクロール部分のコードは Silverlight Toolkit の SetIsMouseWheelScrollingEnabled のあたりでやってることそのまんまです。

[WP7] 起動ページを MainPage.xaml 以外にする

Visual Studio のテンプレートで Windows Phone 7 のプロジェクトを作ると、起動ページとして MainPage.xaml が指定されています。
これを他の xaml に変更するには、Properties フォルダの下の WMAppManifest.xml にある

<Tasks>
    <DefaultTask  Name ="_default" NavigationPage="MainPage.xaml"/>
</Tasks>

ここを書き換えれば OK です。

まぁ、それなりに WP7 開発をやってる人にとっては常識なのかもしれませんが、私はちょっと悩んじゃいましたw
始めは、「App.xaml か App.xaml.cs あたりに書いてあるんだろう」 と思って見てみてもそれらしいものは無し。「じゃあ、プロジェクトの設定なのか?」 とプロジェクトのプロパティや、.csproj ファイルを直接テキストエディターで見てもそれらしいものは無し。結局、grep して見つけました。

2011年6月8日水曜日

[WP7] ListBox のデータの仮想化

Windows Phone 7 の ListBox はデフォルトでも VirtualizingStackPanel を使います。そのおかげで、見えている部分+α 程度の要素だけを生成します。
要するに、1万行ある ListBox でも最初にいきなり 1万行作るわけではなく、見えている部分の 10行ちょっと程度だけ作ることによって高速化してくれるわけです。
ただこれはビジュアル要素(TextBlock やら Border やらのこと)についてです。
データについては、1万行だったらあらかじめ 1万行分必要です。
それを、最初に 1万行分作らなくても済むようにしようというのが「データの仮想化」です。

「Virtualizing Data in Windows Phone 7 Silverlight Applications」
この記事によると IEnumerable では無く IList を継承したクラスを ItemsSource にセットすることで、必要になったデータのみを取得するようになってくれるようです。
ちなみに、必要なのは Count、IndexOf、this[] の get のみでそれ以外は NotImplementedException を投げるようにしとくだけでも構わないようです。

で、さっそく試してみみても常に全件取得されちゃってなぜかまったく仮想化されず悩みました。
ふと気付いて試してみてわかりましたが、ItemTemplate が無いと仮想化されないんですね。

<ListBox>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

このようにほとんど意味が無くても ItemTemplate は必須みたいです。

これで試してみると、最初にだいたい 100件程度分 this[] が呼び出されて、あとは ListBox をスクロールすると必要に応じて適当に this[] が呼び出されるという感じです。
もちろん、IList ではなく IList<T> を継承したクラスでも問題無く仮想化されました。

というか、IList と IEnumerable の両方を継承している場合はちゃんと仮想化されるようです(IList の方が優先されてる)。
なので、List<T>、ObservableCollection<T>、普通の配列など、いずれも大丈夫なんじゃないかと。
ダメなのは IEnumerable、もしくは、IEnumerable<T> しか無い場合です。
この場合は、GetEnumerator() が呼び出されて最初に全件取得されます。

というわけで、普通にコレクションクラスを使ってる時は自然とデータの仮想化も使ってることになりそうです。
IEnumerable しか無いっていうと、一番ありそうなのは LINQ 関連のことをやってる場合ですね。

public IEnumerable<Data> GetItemsSource()
{
    for (var i = 0; i < 10000; ++i)
    {
        yield return new Data(i);    // ←このデータを作るのが重い処理
    }
}

こんなメソッドがあって、これを ListBox.ItemsSource に渡してるとか、
もっと直接的に

this.ListBox.ItemsSource = from i in Enumerable.Range(0, 10000)
                           select new Data(i);    // ←このデータを作るのが重い処理

こんな風なことをしてると、1万個の Data クラスのインスタンスが出来上がるまで UI が固まってしまいます。(Data クラスの中身が string がいくつかあるだけとかなら、1万個くらい一瞬で出来上がっちゃうかな。そういう場合はほとんど気にする必要は無いと思いますが)
LINQ を使ってると、便利なんでついつい 「from なんちゃら~」 で済ませてしまおうとしちゃいがちなのでちょっと気をつけた方がいいかも。(と、自分に対して言っておくw)

2011年6月6日月曜日

[Silverlight][WP7] DependencyProperty.RegisterReadOnly が無い

すごく今さらですが、Silverlight・Windows Phone 7 には DependencyProperty.RegisterReadOnly() メソッドが無いんですね。
うーむ。
探してみたら
Sometimes you just gotta do the best you can [Tip: Read-only custom DependencyProperties don't exist in Silverlight, but can be closely approximated]
こんな記事はありましたが。。。
SetValue されたときに元の値に戻して例外を発生させるという力業。。。つか、ちっとも ReadOnly じゃないじゃん。
RegisterReadOnly() メソッドくらい用意しておいてくれてもよかったような。

2011年5月30日月曜日

[WP7] ScrollViewer のスクロールに反応する

Windows Phone 7 の ScrollViewer (確認してませんが、Silverlight や WPF でも同じかも) にはスクロールしたことを伝えるイベントがありません。
けど、スクロール量を表す HorizontalOffset、VerticalOffset プロパテイがあります。
ならば、これらにデータバインディングしてやればスクロールしたときにあわせて処理をできるんじゃないかと。

public static readonly DependencyProperty MyVerticalOffsetProperty =
    DependencyProperty.Register("MyVerticalOffset", typeof(double), typeof(MyView), new PropertyMetadata(0.0, MyVerticalOffset_PropertyChanged));

private static void MyVerticalOffset_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    // ScrollViewer.VerticalOffset が変わったときにここが呼ばれる
}

こんなのを用意しておいて、あとは

XAML で書くなら
<ScrollViewer VerticalOffset="{Binding MyVerticalOffset, Mode=TwoWay}" />

コードで書くなら
var binding = new Binding("MyVerticalOffset") { Source = this, Mode = BindingMode.TwoWay };
this.ScrollViewer.SetBinding(ScrollViewer.VerticalOffsetProperty, binding);

こんな感じでいいのかなぁ?と。

がっ!これはできないんですね。
ScrollViewer.VerticalOffset プロパティなんかは readonly なので、実行時に例外が発生してしまいます。
BindingMode.OneWayToSource にしてやればよさそうにも思えますが、Silverlight には OneWayToSource がありません。
というわけで、どうもこのアプローチはダメみたいです。

しかし、添付プロパテイであれば相手が readonly でも添付できるようです。

private static int verticalOffsetAttachedPropertyCounter = 0;

// コンストラクタ
public MyView()
{
    InitializeComponent();

    // ScrollViewer.VerticalOffset に添付プロパティをアタッチして変化を検出する
    var property = DependencyProperty.RegisterAttached("MyVerticalOffset" + verticalOffsetAttachedPropertyCounter, typeof(double), typeof(MyView), new PropertyMetadata(0.0, MyVerticalOffset_PropertyChanged));
    ++verticalOffsetAttachedPropertyCounter;
    var binding = new Binding("VerticalOffset") { Source = this.ScrollViewer };
    this.ScrollViewer.SetBinding(property, binding);
}

private void MyVerticalOffset_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    // ScrollViewer.VerticalOffset が変わったときにここが呼ばれる
}

こんな感じでうまくいきました。
上記ではインスタンス上で RegisterAttached してますが、もちろん、普段の依存関係プロパティのように static に RegisterAtached しても構いません。
その場合、当然 MyVerticalOffset_PropertyChanged() メソッドも static にすることになります。
今回、static ではなくインスタンスでやっているのは MyVerticalOffset_PropertyChanged() メソッドに渡される DependencyObject が添付先(今回の場合だと ScrollViewer)になっちゃうからです。依存関係プロパティのときはここに自分が渡されるのでキャストすれば自分のインスタンス変数にアクセスできますが、添付プロパティだと自分にアクセスするすべがなくなっちゃいます。そんなわけで static ではなくインスタンスにしています。

ただ、これ、インスタンスが作られるたびに RegisterAttached してるわけですが、いいのかな?まぁ、UnregisterAttached みたいな解除するための API が無いのでどうしようも無いんですが。

2011年5月28日土曜日

[Silverlight] 明日(いや、もう今日か)、「Silverlight を囲む会 in 大阪 #18」 に参加してきます

明日(いや、もう今日か)、 Silverlight を囲む会 in 大阪#18 に参加してきます。
テーマは LightSwitch だそうです。
Visual Studio 風のデザイナーで、Access みたいにちょこちょこと作れちゃう、しかも作ったものは Silverlight アプリに自動生成されちゃう、という 「ちょっとしたビジネスアプリはこれで作ればあっという間にできちゃうんじゃね?」 というものらしいです。いや、私自身はまったく触ってないので、間違ってるかもしれませんが(笑)

余裕のある会場みたいなので当日受付も OK みたいですよ。
(行ってみようという方は上記ページにある主催者連絡先メールなり Twitter なりでコンタクトを取ってみてください)

2011年5月27日金曜日

[Silverlight] OOB のブラウザーコントロールでローカルファイルをブラウズできない?

Silverlight のブラウザー外実行 (Out Of Browser) では WebBrowser コントロールが使えますが、これってどうやらローカルにあるファイルは表示できないんですね。

Silverlight 自身は OOB のときは分離ストレージ (IsolatedStorage)、「昇格された信頼」 な OOB のときはそれに加えて MyDocuments、MyMusic、MyPictures、MyVideos フォルダーにアクセスできます。なので WebBrowser コントロールも少なくともそれらの場所にあるファイルにはアクセス出来るもんだと思ってました。
しかし、”file:///c:/.../index.htm” とか “c:\...\index.htm” とかいろいろ試してみましたがどうやってもアクセスできません。

うーむ、これってできないんですかねぇ?

検索してみると、ファイルを String に読み込んで WebBrowser.NavigateToString() で表示することなら出来る、という記事はみつかるんですが、やっぱりみんな普通には表示できてないみたいな感じ。
完全にオフラインで動かすために、最初に HTML やらなんやらの必要なものをローカルにコピーしておいて以後はそれを参照する、ってな需要ってあると思うんだけどなぁ。(というか、それをやろうとして出来ないことに気付いたんだけど)

Silverlight 5 で何か変わってるんだろうか?(まだ 5 のノーチェック)

2011年5月25日水曜日

[WP7] ListBox の SelectionChanged イベントって、、、選択項目が変わらないと発生しないのね。いや、そりゃそうなのはわかるけど

Windows Phone 7 で ListBox で項目を選択すると、それに対応するページに遷移する。こういう動きってよくありますよね?
で、戻るボタンで戻ってくる。
そして、同じ項目をクリックする。。。と、うんともすんとも言いません。ListBox の SelectionChanged イベントが発生して欲しいんですが発生しません。

なぜ?と思いつつ検索してみるとすぐ見つかりました。
Reselect same item in listbox - did not fire "SelectionChanged" event
Microsoft の Peter Torr さんから 「選択項目が変わってないんだから SelectionChanged イベントは発生しないよ」
えぇぇぇぇぇ。いや、そりゃ確かにそうなんですが。。。
で、「SelectionChanged イベントが来たときに SelectedIndex に –1 を入れれば次もイベントが発生するよ」

なるほどぉ

私はこれで解決できました。
スレでは 「-1 入れるとハイライト表示が消えちゃう。選択されていることを示すハイライトを出したいときはどうすりゃいい?」 と言う感じでもうちょい続いてます。これといった結論は無いような感じですが。
まじめにやるなら、ListBox の DataTemplete に Button を仕込めばいいんじゃないかな?試してないからうまくいくかわからんけど。
ちょっと強引な方法としては、SelectedIndex = –1 にしたあと、Dispatcher.BeginInvoke で元の SelectedIndex に戻しちゃうとか?(この場合、たぶん SelectionChanged イベントが来ちゃうからそれは無視するとか結構強引な感じになると思うけど)

スレの最後に 「ListBox にも Click イベントがあればいいんじゃね?」 とかあるけど、確かにそうだな。

[WP7] LongListSelector でちょっとはまった

Silverlight for Windows Phone Toolkit に入っている LongListSelector はお手軽にグループ化したリストが作れて便利ですが、ちょっとはまったので記録として。

もっとも単純な形だと、

public class Book
{
    public string Category { get; set; }
    public string Name { get; set; }
}

こんなクラスがあったとすると、

var books = new Book[] {
    new Book() { Category = "Cat1", Name = "Book1" },
    new Book() { Category = "Cat1", Name = "Book2" },
    new Book() { Category = "Cat2", Name = "Book3" },
};

this.LongListSelector1.ItemsSource = from book in books
                                     group book by book.Category into c
                                     select c;

こんな風にすればグループ化して表示できます。

と思ってたんですが、これじゃダメなんですね。これだとグループヘッダーのところに何も表示されません。
この場合、ItemsSource には System.Linq.IGouping<TKey, TElement> のコレクションを渡していて、Key がグループヘッダー、グループの中身は GetEnumerator() で取得することになります。実際に group by で返ってくるのは IGrouping を実装した System.Linq.Lookup.Grouping クラス(リファレンスには載ってない)なんですが、どうやらこれの Key プロパティとうまくデータバインドできない様子。

Toolkit のサンプルを見ると

public class PublicGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
    private readonly IGrouping<TKey, TElement> _internalGrouping;

    public PublicGrouping(IGrouping<TKey, TElement> internalGrouping)
    {
        _internalGrouping = internalGrouping;
    }

    public override bool Equals(object obj)
    {
        PublicGrouping<TKey, TElement> that = obj as PublicGrouping<TKey, TElement>;

        return (that != null) && (this.Key.Equals(that.Key));
    }

    public override int GetHashCode()
    {
        return Key.GetHashCode();
    }

    #region IGrouping<TKey,TElement> Members

    public TKey Key
    {
        get { return _internalGrouping.Key; }
    }

    #endregion

    #region IEnumerable<TElement> Members

    public IEnumerator<TElement> GetEnumerator()
    {
        return _internalGrouping.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _internalGrouping.GetEnumerator();
    }

    #endregion
}

こんなクラスを自前で用意して

this.LongListSelector1.ItemsSource = from book in books
                                     group book by book.Category into c
                                     select new PublicGrouping<string, Book>(c);

と言う風に包んであげています。
なぜそんな事に?

var q = from book in books
        group book by book.Category into c
        select c;
var group = q.First();
var key = group.Key;                                    // ちゃんと Key の内容を取り出せる
var propertyInfo = group.GetType().GetProperty("Key");    // null が返る!

こんな風に試してみました。
System.Linq.Lookup.Grouping クラスは DependectyObject ではありませんから、LongListSelector は内部でリフレクションを使ってデータバインドしてると思います。なので、GetProperty(“Key”) で PropertyInfo が取れないとバインドしようが無いと思いますが、実際に試してみると null が返ってきちゃいます。どうやら Grouping クラスは internal なのでリフレクションでアクセスできない様子。
ちなみに、まったく同じコードを .NET Framework 4 のコンソールアプリケーションと Silverlight 4 で試すとまったく問題なく PropertyInfo が取得できます。Silverlight と WP7 は null になるのかと思ったら、WP7 だけが動作が違うんですね。

分かってしまえばどうということは無いんですけど、ちょっとはまりました。
(というか、最初から public に Grouping クラスを用意しておいてくれたらよかったのに)

2011年5月24日火曜日

[WP7] App Hub のアカウントを作ってみた

HTC 7 Trophy を入手したので App Hub アカウントを作ってみました。
(会社の法人アカウントを作ってみようかと思ってたんですが、とりあえず個人にしました)

最近、公式で手順書が公開されたそうですが、私は今のところ特に問題なく進めることができました。

公式の手順書はこちら
App Hub のアカウント作成手順と注意事項

一部、手順書とは違う(メールが日本語だったり)ところもあるので私の場合を書いておきます。

  1. App Hub でアカウントを作成。私は以前から使っていた Live ID で登録。聞いたところによると、Live ID 作成時の国が「日本」になってないとダメとか、誕生日が 18才以上でないとダメとか、注意点があるそうです(あとから変更してもダメで作成時の情報がチェックされるらしい?)。Live ID 作成時のことなんて覚えてませんでしたが、私の場合は特に問題なく作成できました。
  2. ちょっとしたら App Hub から確認のメールが来ました。これはリンクをクリックしてメールアドレスの確認をするだけ。
  3. それから 1日半後くらいの土曜日の早朝に GeoTrust より「Windows Marketplace for Mobile ID XXXXXX」のメールが到着。私のところには日本語のメールが来ました。身分証明書確認のシートもちゃんと日本語です。

    確認シートの部分をプリントアウト
    ・身分証明書には運転免許証のコピーを使用
    ・「ID 番号」欄には運転免許証の番号を記入。
    ・「有効期限」欄には運転免許証の期限を「201X年X月X日」形式で記入。(文面が日本語になってるんだから日本語表記にしてみた)
    ・「発行場所」欄には「日本」と記入。
    ・「署名」欄には氏名、「日付」欄には当日の日付を「2011年X月X日」形式で記入。
    (以上手書き)

    これをスキャンしてメールに添付して送信。送ったのは GeoTrust からメールが来てから数時間後の土曜日の昼くらいです。
  4. 月曜日の昼に GeoTrust より認証手続き完了のメールが来ました。これもちゃんと日本語です。(締めの言葉は Thank you でしたがw)
  5. その翌日、Microsoft から「Windows Marketplace account notification」のメールが来て無事 App Hub アカウントが作れたようです。

結局、6日間でアカウントの作成はできました。途中に土日を挟まなければもっと早くできるのかもしれませんね。

さっそく、HTC 7 Trophy を繋いで Windows Phone Developer Registration を起動。
App Hub アカウントで Register すると無事登録されました。
Visual Studio から実機に繋いでのデバッグも問題なし!

ところで、今のところ有料アプリを作る予定はないんだけど、EIN の取得と W-8BEN の申請はしておいたほうがいいのかな?

2011年2月14日月曜日

[Silverlight] Silverlight を囲む会 in 大阪 #16 に参加してきた

この間も書いたように Silverlight を囲む会 in 大阪 #16 に参加してきました。
Windows Phone 7 がテーマということで、とてもおもしろかったです。
プログラミング生放送の 5zj さんがニコ生でも生放送してくれていました。2011/02/18(金) 23:59:59 までタイムシフトで見ることができます。(タイムシフトはプレミアム会員じゃないとダメなのかな?)
しょっぱなの私のセッションは Visual Studio とエミュレーターのみですが、他の皆さんのセッションは Windows Phone 7 実機を使ってのセッションです。ぬるぬると気持ちよく動くさまが見れます。

もちろん懇親会にも参加。あー楽しかった。

2011年2月7日月曜日

[Silverlight] Silverlight を囲む会 in 大阪 #16

Silverlightを囲む会 in大阪 #16 に参加します。
今回のテーマは 「Windows Phone 7 のための Silverlight 開発講座」 です。
入門のセッションのスピーカーを担当させてもらいます。
Visual Studio と Windows Phone エミュレーターでどんな感じで開発ができるのかといった、入門的なところを主にして話させてもらうつもりです。

まだ、申し込み受付中のようですので興味のある方はぜひ。

2011年2月5日土曜日

[ブログ] FC2 から Blogspot へデータを移行したときの覚え書き

FC2 にあった記事をここ Blogspot へ移行したときの覚え書きです。

■ FC2 → Blogger
まずは、FC2 ブログの管理ページにある「エクスポート」で記事データをエクスポートします。
このデータはどうやら MT 形式みたいです。MT 形式であれば http://movabletype2blogger.appspot.com/ で Blogger 形式に変換できます。
FC2 からエクスポートしたデータは euc-jp になってるので utf-8 にしてやります。
続いて http://movabletype2blogger.appspot.com/ で Blogger 形式に変換、と思ったらちょっと問題が。

  • 日時がすべて変換した時のものになってしまう。
    これは movabletype2blogger が “02/12/2008 09:03:25 PM” という AM/PM 付きの形式を前提としているのに対して FC2 のデータは “02/12/2008 21:03:25” と AM/PM が無いためです。ちなみに、MT 形式のドキュメントを見ると AM/PM を省略したときは 24時間表記とあったので movabletype2blogger の実装が手抜きなんでしょう。本当ならは movabletype2blogger の方を修正するのが筋ですが、手っ取り早く DATE: のところを AM/PM 形式に変換するプログラムを C# でちょこっと作って対応しました。
  • タイトルがおかしくなったり、コメントにゴミが入る。
    これは MT 形式ファイルのコメントのところにある TITLE、SECRET、PASS のせいで、Blogger 形式にしたときにタイトルがコメントのものに置き換わってしまったり、コメントの中に SECRET や PASS の文字列がそのまま残ってしまったりします。MT 形式の仕様上これらがどういうものなのかは調べてませんが、消してしまえばいいようだったので手作業で削除しました。
  • 記事やコメントの日時がずれる。
    FC2 からエクスポートしたデータは JST なのに GMT として扱われるためです。これは Blogger 形式にしたあとで、<ns0:published> と <ns0:updated> の Z を +09:00 に置換して JST にしてやりました。

これで Blogspot にインポートすることができました。

[ブログ] .Text から Blogspot へデータを移行したときの覚え書き

.Text にあった記事をここ Blogspot へ移行したときの覚え書きです。

■ .Text → BlogML
まずは http://www.divakk.co.jp/blog/aoyagi/ で動いていた .Text (dotText : ASP.NET C# で書かれたブログエンジン。.NET Framework 1.1 のころのもの) を BlogML 形式に変換。
BlogML というのはブログデータの形式のことです (ファイル自体は XML)。
.Text、Community Server、Subtext、BlogEngine.NET、DasBlog といった .NET 系のブログエンジンでサポートされていることが多いみたいです。(私は今回初めて知ったので詳しいことはわかりません)

変換には http://www.johnsample.com/ のツールを使わせてもらいました。ソースも載っていますしバイナリもダウンロードできます。このソースでは BlogML 名前空間のクラスを使っていますが、それは http://blogml.codeplex.com/ にあります。

■ BlogML → WXR
続いて BlogML 形式を WXR 形式にします。
WXR 形式は Wordpress が使っている形式です。
BlogML から WXR への変換は Importing BlogML into WordPress こちらにある XSLT を使わせていただきました。

まず、XSLT に合わせて BlogML の最初の方にある xmlns=”http://www.blogml.com/2006/09/BlogML” を消します。
次に、もとの .Text の日付が JST を前提にしてあるため XSLT の <xsl:template name=”topubdate”> のところの +0000 を +0900 にして JST に合わせます。
あとは BlogML のファイルにこの XSLT を適用してやるだけ。

私は、BlogML の 2行目に <?xml-stylesheet href="BlogML2WXR.xsl" type="text/xsl"?> と書いて IE で読み込みました。
もちろん、単に IE で読み込むと XSLT を適用した結果を HTML として解釈したものが表示されるだけです。
XSLT を適用した結果の XML を取り出すのに、私は
Internet Explorer Tools for Validating XML and Viewing XSLT Output
を使ってます。
こいつを実行して解凍すると msxmlvw.htm と msxmlvw.inf が入ってますので、これらを適当なフォルダに入れておいて msxmlvw.inf を右クリックして「インストール」
これで IE を立ち上げなおして BlogML を読み込んだ状態で右クリックすると 「View XSL Output」 が増えてます。
これで XSLT を適用した結果の XML を表示させることができますので、コピー&ペーストして utf-8 で保存してやれば OK です。

こうして作った WXR 形式のファイルを wordpress.com のブログにインポートしてみたら一応きちんとインポートできてました。(ただし試したのは記事 1つ分だけ)

■ WXR → Blogger
WXR 形式を Blogger 形式にします。
wordpress2blogger というツールが http://code.google.com/p/google-blog-converters-appengine/ にあります。このツールは Python で書かれてるみたいです。このツールが http://wordpress2blogger.appspot.com/ で動いてますので、私はこれを使わせてもらいました。(ただし、一度に変換できるのは 1M バイトまで)

ただ、上記の手順で作った WXR を変換しようとしたらエラーになりました。
pubDate が fmt=%a, %d %b %Y %H:%M:%S になっていないと言われます。
とりあえずエラーが出ないように XSLT の <xsl:template name=”topubdate”> のところの <xsl:param name="date" /> の後ろに “Mon, “ と書き足して変換しなおしました。
これだとすべての日付が Mon (月曜日) になってしまいますが、きっと曜日なんて読み飛ばしてるだけでしょうから問題ないでしょう。

■ Blogger 形式のデータを Blogspot にインポート
これで .Text のデータが Blogger 形式になったので Blogspot の設定ページにある 「ブログをインポート」 でインポートします。
ただ、以下の修正が必要でした。

  • ”<ns0:link href=”001” ... ”  のようになっているところがあるので “<ns0:link href="http://www.blogger.com/" ... “ に変更。(いつの間にか href の値がおかしくなっていた。これがおかしいとインポート時にエラーが出て読み込めない)
  • “<ns0:published>2003-07-05T18:18:00Z</ns0:published>” を “<ns0:published>2003-07-05T18:18:00+09:00</ns0:published>” に変更。(JST なのに “Z” となっているためインポートすると時間がずれる。なので “+09:00” としてやる)

これで無事 Blogspot にインポートできました。

2010年12月20日月曜日

[お知らせ] 過去に他のブログに書いた記事をすべてこのブログに持ってきました

今まで使ってきたブログ(技術系)

2003年7月 BlogX を立ち上げてブログ開始。
(BlogX っていうのは C# で作られたブログエンジンです。2004年くらいで更新はストップしてます)

↑
.Text 時代
↓

2004年1月 .Text を立ち上げてブログ開始。
(.Text も C# で作られたブログエンジンです。こちらもずいぶん前に開発は終了してるようです)

BlogX のデータをすべて移行して旧ブログは停止。
2008年4月ごろ 技術系以外の趣味のことを書いていた FC2 のブログに技術系のことも書くようになってきた。
(使い分けが面倒になった)
↑
FC2 時代
↓
2009年1月 .Text のブログの更新を停止して FC2 の方に一本化。
2010年7月 やっぱり技術系と趣味系を分離。
blogspot に技術系のためのブログを作成。それがここ。

(最初は独自ドメインを使った URL で開始したけど 2010/12/14 に普通の blogspot の URL に変更)
↑
blogspot 時代
↓
現在  

と、こんな感じで今までブログを移り変わってきたんですが、特にデータを移行するってことはしてきませんでした。(BlogX → .Text はしましたが)
それを今回、過去に書いた技術系の記事をすべてこのブログに持ってきました。

.Text に書いた記事はすべてここに移行しました。ちょっと面倒なところもありましたが、コメントも含めて無事すべてのデータを移行できました。移行手順については別の記事にまとめるつもりです。
で、この .Text のブログは停止しました。
.Text は開発時期が古いですし、コメントやトラックバックのスパム防止機能がありません。自分でソースをいじってスパムを弾いたり、CAPTCHA を導入したりしてましたが、それでも今でもスパムがやってきます。なので、たまにスパムを消してやったりしないといけないんですが、さすがに面倒になりました。
今回、過去の記事をここに移行してきたのは、この 「.Text にやってくるスパムがうざいから .Text を停止したい」 というのが一番の理由です。

FC2 では技術系のことと趣味系のことがまぜこぜになってますが、今回、技術系の記事をすべてここに移行しました。こちらもコメントも含めて移行できました。この手順についても別の記事にまとめるつもりです。
FC2 の方を消したりはしていないので、技術系の記事に関してはここと FC2 とに同じものが 2つあります。消すのもなんなので、これはこのままにしておくつもりです。

なお、単純に記事を移行しただけで修正などはしていません。なので、リンクが切れたり画像が表示されていないようなところがあるかもしれません。また、スタイルが違うせいで見た目がおかしいところもあるかもしれません。これらはとりあえずそのままにしておくつもりです。あまりにひどいところがあったら修正するかもしれませんけど。

というわけで、

という使い分けで今後もやっていきます。
(最近、どちらもあまり更新してませんが)

2010年12月14日火曜日

[お知らせ] このブログの URL が変わりました

下で書いたとおり、このブログの URL が変わりました。新しい URL は http://shinichiaoyagi.blogspot.com/ です。
RSS の URL も変わりました。 http://shinichiaoyagi.blogspot.com/feeds/posts/default です。

すでに以前の URL ではアクセスできません。
あらためて検索してみたら、あまり多くはありませんが、はてブ、.NET Clips、MSDN フォーラムなどからリンクされていました。これらは完全にリンク切れになってしまいます。申し訳ないです。
なお、URL が変わっただけでブログ内の記事自体は以前とまったく同じです。(各記事の URL もドメイン名部分を “shinichiaoyagi.blogspot.com” に置き換えてもらえばそれ以外は同じままでアクセスできるはず)

2010年12月13日月曜日

[お知らせ] このブログの URL が変わります

このブログの URL が http://shinichiaoyagi.blogspot.com/ に変わります。
あわせて RSS の URL も変わります。( http://shinichiaoyagi.blogspot.com/feeds/posts/default )
URL の変更は 2010年12月14日の午前中を予定しています。(って、もう明日ですが)

なお、変更後は今の URL でのアクセスはできなくなります。リダイレクトなどもされません。
ですので、このブログへのリンクはみんな切れちゃうことになります。すみません。

2010年10月28日木曜日

[VS2010] Help Viewer 1.1 (Visual Studio 2010 SP1 といっしょにリリース予定)

Help Viewer 1.1 Preview Video より。
Visual Studio 2010 SP1 といっしょに Help Viewer 1.1 がリリース予定だそうです。
紹介ビデオはこちら Help Viewer Updates in Visual Studio 2010 SP1

VS2010 では、ローカルのヘルプを見るのにもブラウザ(IE)が使われるようになってました。それが SP1 で専用のヘルプビューワーアプリケーションが追加されるようです。
すでに GrapeCity ヘルプビューワ とかいくつかヘルプビューワーがありますから、とってもイマサラ感がしないでもないですが。

ちなみに、VS2010 の「ヘルプ」-「ヘルプ設定の管理」メニューでオンライン・ローカルのどちらのヘルプを参照するかや、オンライン上のヘルプをローカルにインストールしたりできます。

ところで、”Help Viewer 1.0” というのは、ヘルプシステム全体の名称として使われていたと思います。Microsoft Help 2 の次のバージョンの名前が Help Viewer 1.0 だったはずです。
ということは、今回のアプリは「Help Viewer 1.1 という名前の Help Viewer 1.0 用ビューワーアプリ」ってこと?
上記の記事にも「Q: 既存のヘルプコンテンツに変更が必要なの?」 「A: 必要ない」とか何とかあるので、やっぱりそういうことだよなぁ。
うーむ

2010年10月27日水曜日

[.NET] C# でミニダンプを書き出す方法

Writing Minidumps in C# より。
C# でミニダンプを書き出すコードが紹介されてたので覚え書きとして。

ミニダンプを書き出しておけばあとでそのときの状態(呼び出し履歴とか変数の内容とか)を確認することができます。
特に Visual Studio 2010 ではマネージコードのダンプに対応しているのでちゃんと C# や VB での行番号とかがわかります。(VS2008 とかだと JIT 後のアセンブラレベルのものしか見れないんじゃないかと思います。確認してませんが)

[Silverlight][WP7] PhysicsHelper 4.0 Alpha

PhysicsHelper の 4.0 Alpha がリリースされてました。
http://physicshelper.codeplex.com/

PhysicsHelper とは、C# で実装された物理演算エンジンである Farseer Physics Engine をビヘイビアーで包みこんだものです。おかげでとってもお手軽に XAML の要素に対して物理演算をすることができます。すでに Windows Phone 7 にも対応してるそうです。
とりあえず http://www.andybeaulieu.com/video/PhysicsHelper4Intro.wmv このビデオを見ればどういうものかはわかるんじゃないかと。
(英語ですが、Blend を使ったデモなので見てるだけでもやってることはわかりました)

ただ、まだ Alpha なのでバグもあるし、未実装の部分もあるとのこと。

2010年9月28日火曜日

[Silverlight] Silverlight は Web のためのものでは無くなった

Silverlight…not just for the Web… より。

おぉ、ほんとだ! http://msdn.microsoft.com/en-us/library/default.aspx を見ると

msdnsilverlight_en_us.jpg

このように Silverlight が 「.NET Development」 の下に移動してます。
今までは 「Web Development」 の下でした。
上記記事にあるとおり、out of browser もあるし、Windows Phone 7 もあるんだから Web の下よりは .NET の下の方がいいんじゃないか、っていうことですね。

けど、日本語版 http://msdn.microsoft.com/ja-jp/library/default.aspx の方を見ると

msdnsilverlight_ja_jp.jpg

まだ 「Web 開発」 の下ですね。
きっとそのうち英語版と同じように移動するんでしょう。

2010年9月22日水曜日

[Silverlight] Effect がパフォーマンスに与える影響

Silverlight Performance Tip: Understanding the impact of Effects on performance より。
なるほどぉ。
正確なことは上記記事を見てもらうとして要点のみ。

<Grid x:Name="LayoutRoot" Background="White">
    <Border Width="200" Height="100" Background="LightGray">
        <Border.Effect>
            <DropShadowEffect/>
        </Border.Effect>
        <Grid Width="200" Height="100">
            <TextBox Width="100" Height="30"></TextBox>
        </Grid>
    </Border>
</Grid>

こんな XAML があるとします。表示させると以下のような感じです。

DropShadowEffectPerformance.jpg

DropShadowEffect を使って影を落としていて、真ん中にテキストボックスがあるというごく単純なものです。
で、DropShadowEffect などのエフェクトがかかっていると、その内側の要素に再描画が必要になった場合に DropShadowEffect の部分ごと再描画されるそうです。
この例では中にテキストボックスがあるわけですが、テキストボックスの点滅するキャレットも再描画によって描かれてます。なので、キャレットが点滅するたびに回りの Border ごと再描画されることになります。もちろん、キャレットだけでなく、マウスホバーで色を変えたりするようなボタンとか、進捗にあわせて動くプログレスバーとか、再描画が発生するものはみんな同じことになります。
この例では Border の下にはテキストボックスがあるだけなので特に問題にはなりませんが、コントロールがたくさんあるような場合にはそれらがみんな再描画されることになるので CPU 負荷が高くなったりといった問題が発生するかもしれません。
で、どうすればいいかというと

<Grid x:Name="LayoutRoot" Background="White">
    <Border Width="200" Height="100" Background="LightGray">
        <Border.Effect>
            <DropShadowEffect/>
        </Border.Effect>
    </Border>
    <Border>
        <Grid Width="200" Height="100">
            <TextBox Width="100" Height="30"></TextBox>
        </Grid>
    </Border>
</Grid>

こんな風に Border を 2つに分けてしまえばいいそうです。
実際やってみると確かに見た目は同じになります。
そして、テキストボックスが含まれている方の Border には何のエフェクトも無いことになるので無駄な再描画は発生しないということですね。
なるほどなぁ。

なお、再描画が発生する範囲は Silverlight を貼り付けている HTML の方に

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
  <param name="enableRedrawRegions" value="true"/>
:
:

と enableRedrawRegions を指定すれば可視化することができます。

2010年9月14日火曜日

[Silverlight][Twitter] Seesmic Desktop 2 って MEF で機能追加できるようになってるのか

Twitter クライアントの一つ、Seesmic Desktop 2 ですが、これって Silverlight 4 なんですね。Silverlight 4 ですが、ブラウザ上で動かすのではなくクライアント PC にインストールして実行する形態になっています。(out-of-browser です)

そして、
Writing plugins for Seesmic Desktop
(Microsoft の Silverlight チームのプログラムマネージャーの Tim Heuer 氏のブログ)
によると Seesmic Desktop 2 は Managed Extensibility Framework(MEF)によって拡張可能になっているそうです。Visual Studio 2010 用のテンプレート Seesmic Desktop Platform Developer Templates なんてのもありますし、Seesmic Desktop Platform Plugins には Tim 氏が作ったプラグインが紹介されています。
結構拡張性も高そうで、なかなかおもしろそうです。

# といいつつ、インストールしてちょっと使ってみたけど、イマイチ自分には合わず。
# 結局、TweetDeck に戻っちゃいました。
# 一時期は Seesmic Web を使ってたけど、最近 TL が更新されない病が発生してて使い物にならず。

2010年9月13日月曜日

[.NET] MTA では OpenFileDialog が動かない?

たまたま見かけてちょっと気になったので覚え書きとして。。。

「Current thread must be set to single thread apartment (STA) mode before OLE calls can be made」 より。
Visual Studio セットアッププロジェクトでインストーラーを作るときにインストーラークラスのカスタムアクションで System.Windows.Forms.OpenFileDialog を使うとエラーが出ちゃうそうです。Win XP や 2003 ではちゃんと動くけど、Vista やそれ以降の Windows で発生するとのこと。
デバッガーにアタッチしておくと、"Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process." というエラーメッセージが出るそうです。原因はこのメッセージのまんまで、MSI が MTA で動いてるからで、OpenFileDialog なんかは STA じゃないと動かないかららしい。

へぇ、OpenFileDialog とかって MTA じゃ動かなかったんだ。知らんかった。

上記の記事では対策方法も紹介されています。
別のスレッドを作って、そいつを Thread.SetApartmentState(ApartmentState.STA) してから OpenFileDialog を ShowDialog() してやればいいようです。

2010年9月10日金曜日

[C#] yield return はスレッドセーフなのか? 追記

昨日の 「[C#] yield return はスレッドセーフなのか?」 に匿名さんからコメントを頂きました。
昨日の記事の最後のところにまとめとして 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と書きましたが、ちょっと乱暴なまとめだと思ったので追記します。
(最初は昨日の記事に追記するつもりだったんですが、長くなったので別記事にしました)

えーと、もともと 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と私が思ったのは 「ロックが解除されるタイミングが呼び出し元に依存するから」 というのが理由です。
お仕事で lock { ~ } の中で yield return を使うコードを書いていたときに、ふと、「これってちゃんとロックかかるの?」 と疑問に思ったのが発端で今回のことを調べたんです。このとき書いていたコードは 「なるべく早くロックを解除して他のスレッドが動けるようにしたい」 というものだったので、自然とロック解除のタイミングが呼び出し元に依存するのは避けたいと思ってました。
そういう前提があったので 「lock { ~ } の中で yield return を使うのはやめておいた方がよさげ」 と、まとめのところに書きました。けど、そういう前提であることをあまり明確にせずにこうまとめちゃうのはちょっとまずかったですね。

-- 9/10 13時追記 ここから
前の記事にもちょこっと追記しましたが、指摘を頂いたのでこちらにも追記しておきます。
すっかりロックが解除されるのは Enumerator の Dispose() が呼び出されたときだけかのように書いちゃってますが、実際には MoveNext() が false を返すときにも Dispose() と同じ処理が行われるようになっています。なので、Enumerator の Dispose() メソッドを呼び出したときか、MoveNext() が false を返して列挙が終わるときにロックが解除されることになります。
もちろん、以下に書いたファイルの例でも同じです。
-- ここまで追記

ということで、また他の例を。

コメントで頂いたように using { ~ } や try ~ finally でも lock { ~ } と同じことが起こります。
たとえば、以下のようなコード。

private IEnumerable<string> GetLines()
{
    using (var stream = new FileStream("test.txt", FileMode.Open, FileAccess.Read, FileShare.None))
    using (var reader = new StreamReader(stream))
    {
        var text = reader.ReadToEnd();
        var lines = text.Split('\n');
        foreach (var line in lines)
        {
            yield return line;
        }
    }
}

これはファイルをオープンするときに FileShare.None を指定してファイルをアクセス禁止にしています。
yield return が無ければ using のスコープを抜けるときに stream.Dispose() と reader.Dispose() が呼び出されてファイルがクローズされます。もちろん、このときにファイルのアクセス禁止も解除されます。
しかし、yield return によって出来上がるコードは lock { ~ } のときに見たのと同じようなものですから、stream.Dispose() と reader.Dispose() が呼び出されるのは Enumerator の Dispose() メソッドからになります。
呼び出し元が foreach を使っているときは、以下のような感じになるわけです。

foreach (var line in GetLines())
{

    ... line を使って何か処理 ...

}
// foreach を抜けたときに Enumerator の Dispose() が呼ばれて test.txt のアクセス禁止が解除される

このように GetLines() から返ってきた Enumerator での列挙が終わったあとに、foreach によって作られた Enumerator.Dispose() の呼び出しが行われて、ファイルクローズおよびアクセス禁止解除ということになります。

では、yield return を使わない場合はどうなるでしょうか?
以下のようなコードです。

private IEnumerable<string> GetLines()
{
    using (var stream = new FileStream("test.txt", FileMode.Open, FileAccess.Read, FileShare.None))
    using (var reader = new StreamReader(stream))
    {
        var text = reader.ReadToEnd();
        var lines = text.Split('\n');
        return lines;
    }
}

この場合は、GetLines() メソッドから return する時点で using のスコープを抜け、stream.Dispose() と reader.Dispose() が呼び出されてファイルがクローズされ、アクセス禁止が解除されます。
ですから、呼び出し元が上記と同じ foreach を使ったものであっても、foreach の列挙が始まるときにはすでにファイルがクローズされている状態になっています。

で、これはどちらが正しいとか言えるようなたぐいのものではありません。
前者は 「列挙が終わって Enumerator の Dispose() メソッドを呼び出すまでファイルがアクセス禁止になる」、後者は 「Enumerator を作成する間だけファイルがアクセス禁止になる」 ということになるわけですが、どちらにしたいかなんてことはケースバイケースです。
ただ、今回取り上げたような yield return の動作を知らないと 「yield return のところで using のスコープを抜けてファイルはクローズされるんじゃないの?」 と勘違いしやすいとは言えると思います。

というわけで、結局のところ、lock { ~ }、using { ~ }、try ~ finally などスコープに重要な意味があるものの中に yield return を書くときはちょっと注意が必要ではあると思うけど、yield return でいいかどうかなんてことはケースバイケース、という感じですね。

それにしても、yield return についてちゃんと考えたのなんて今回が初めてなんですが、なかなか興味深かったです。