2008年7月5日

 いつぞやに つぶやいていた Windows Live Writer SmartContentSource プラグインのVisual Studio用のテンプレートを作りました。無地のサイドバーエディターとプラグイン固有のオプション設定用ウィンドウ付き。今のところ全くコメントはない。x64の環境に依存しているところがあるので(DLL参照のパスとビルド後イベントのコマンド)そこの部分は修正しないといけません。

 インストーラ形式になってます。署名がないので警告がでます。インストールするとVisual BasicのWindowsのマイテンプレートに表示されます。C#はないです。

新しいプロジェクト

 x86ならC:\Program Files、x64ならC:\Program Files (x86) を表すテンプレート用のパラメータってないんでしょうか。

posted @ 23:45 | Feedback (0)

 Windows Live Writer SDK関連のための話しです。WLWのプラグイン作成時に IProperties というインタフェースが出てきます。これは次のようにプラグイン固有のまたはプラグインにより挿入したコンテンツ固有の情報を取得・設定するために用意されています。

Dim foo = Options.GetString("Foo", "foo")
Options.SetString("Foo", "bar")

 OptionsがIProperties型。情報を取得する場合、GetString(名前, デフォルト値)と呼び出します。設定する場合は、SetString(名前, 設定値)となります。基本的な型についてはそれぞれGetIntなどのメソッドが用意されてます。

 このIPropertiesを上のようにそのまま使用すると大量に文字列リテラルが出てきます。リテラルじゃないのは設定値となる"bar"のところだけ。単純に解決するには全部定数に置き換えます。

Const Foo As String = "Foo"
Const FooDefaultValue As String = "foo"

 するとこうなる。単純で良いのかもしれないけれど、Fooという名前にFooと値をいれてる、この例ではFooひとつだけだけど複数の設定項目が出た場合にグループとして扱えない、FooとFooDefaultValueは意味的にはペアとして扱いたいなど気になる点が出てきます。

 文字列の列挙体があればいいのだけど、それは言語的にできません。ただし、じゃんぬさんが昔に文字列の列挙体というエントリを投稿されてます。詳しくはそちらで。私もこれを参考にして、ペアの部分についても考えて出たコードが以下。

 まずは、値の設定・取得するところは次のようになって、

Dim foo = Options.GetString(FooPluginOptions.Foo.Name, FooPluginOptions.Foo.DefaultValue)
Options.SetString(FooPluginOptions.Foo.Name, "bar")

設定項目ごとに記述する部分は次のようになりました。

Public NotInheritable Class FooPluginOptions
    Public Shared ReadOnly Foo As New Item(Of String)("foo")
    Public Shared ReadOnly Bar As New Item(Of Integer)(100)

    ' (略 見てはいけない部分)

End Class

設定項目ごとに項目の型とデフォルト値を指定した、Itemというオブジェクトを作ってます。

 隠された部分は以下。FooPluginOptionsの中にあるコードです。うーむ。設定項目のグループごとに書くとなると、どんだけーって感じですね。Itemクラスは外に出しても問題ありませんが、コンストラクタ内のコードは必要になり親クラスとして切り出すと動作しなくなってしまいます。

 設定値の名前とそのデフォルト値のペアとしてItemクラスを作っています。プロパティとしてReadOnlyのNameとDefaultValueを持ってます。ToStringをオーバーライドしてNameを返すようにしているので Strict Offの場合はNameプロパティ要らず(私は常にStrict Onだけど)。Sharedなコンストラクタ内で自分自身のフィールドを取得してItemオブジェクトのNameプロパティにフィールド名そのままを設定してます。Private変数を書き変えてます。

Public NotInheritable Class Item(Of T)
    Private _name As String
    Public ReadOnly Property Name() As String
        Get
            Return _name
        End Get
    End Property

    Private _defaultValue As T
    Public ReadOnly Property DefaultValue() As T
        Get
            Return _defaultValue
        End Get
    End Property

    Public Sub New(ByVal defaultValue As T)
        _defaultValue = defaultValue
    End Sub

    Public Overrides Function ToString() As String
        Return _name
    End Function
End Class

Shared Sub New()
    Dim fieldsInfo = GetType(BasePropertyItems).GetFields( _
        BindingFlags.DeclaredOnly Or _
        BindingFlags.GetField Or _
        BindingFlags.Instance Or _
        BindingFlags.Public Or _
        BindingFlags.Static Or _
        BindingFlags.FlattenHierarchy)

    For Each info In fieldsInfo
        Dim item = info.GetValue(Nothing)
        item.GetType.InvokeMember( _
            "_name", _
            BindingFlags.SetField Or BindingFlags.NonPublic Or BindingFlags.Instance, _
            Nothing, _
            item, _
            New Object() {info.Name}, _
            Nothing, Nothing, Nothing)
    Next
End Sub

 どんな感じですかねぇ。ナイスな方法があれば教えてください。

posted @ 23:39 | Feedback (39)