Hatena::ブログ(Diary)

周回遅れのブルース

2012-02-16

Livet をカスタマイズする。

| 12:09 | Livet をカスタマイズする。を含むブックマーク

#以下の記事は旧いバージョンによる記事です。最新版はこちらを参考にしてください。

国産 MVVM インフラ「Livet」も徐々に普及しつつあるようで、MSDN フォーラムでもちょくちょく名前を聞くようになってきました。

私も業務で使い倒してますが、VB プロジェクトで Livet 使う場合、少し気が利いてない箇所があるのは事実です。*1 また実務で作業してると、スニペットを自分用に改造したい欲求にかられてきます。そこで今回は Livet の改造法を公開します。

以下 VB 向けの改造ですが、応用すれば C# でもいけると思いますよ。

f:id:hilapon:20120216120313p:image


スニペットの改造

別に既定で入ってる必要ないのですが、プロパティの先頭にコメントが入っていると何かと便利なので、スニペットに設定したくなりました。

Livet をインストールするとスニペットは

Visual Studio 2010 のインストールフォルダ/VB(C# ならC#フォルダ)/Snippets/1041/application


に展開されます。VB の場合、Livet用プロパティのスニペットは LivetProperty_VB.snippet。エディタで開いて、XMLコメントを加えます。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Livet プロパティ</Title>
      <Author>Livet Project</Author>
      <Description>プロパティを作成します</Description>
      <HelpUrl></HelpUrl>
      <SnippetTypes />
      <Keywords />
      <Shortcut>lprop</Shortcut>
    </Header>
    <Snippet>
      <References />
      <Imports>
        <Import>
          <Namespace>Livet</Namespace>
        </Import>
      </Imports>
      <Declarations>
        <Literal Editable="true">
          <ID>name</ID>
          <Type />
          <ToolTip>プロパティ名</ToolTip>
          <Default>MyProperty</Default>
          <Function />
        </Literal>
        <Literal Editable="true">
          <ID>type</ID>
          <Type />
          <ToolTip>プロパティの型</ToolTip>
          <Default>String</Default>
          <Function />
        </Literal>
      </Declarations>
      <Code Language="VB" Kind="" Delimiter="$">
        <![CDATA[
#Region "$name$変更通知プロパティ"
Private _$name$ As $type$

''' <summary>
''' を取得・設定します。
''' </summary>
Public Property $name$() As $type$
    Get
        Return _$name$
    End Get
    Set(ByVal value As $type$)
        If (_$name$ = value) Then Return
        _$name$ = value
        RaisePropertyChanged("$name$")
    End Set
End Property
#End Region
]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

このファイルの

''' <summary>
''' を取得・設定します。
''' </summary>

が、自分で追加した箇所です。保存して lprop スニペットを実行するとこうなります。

f:id:hilapon:20120216120313p:image


今回スニペットの中身に初めて触れる機会ができたのですが、スニペットの構造が判ってくると、標準スニペットも改造したい欲求にかられますねw



ViewModelCommand のスニペット改造

#2012/02/21 追加

あと ViewModelCommand ですが、これの CenExecute 用ファンクション。スニペットでは戻り値がないためビルドエラーになります。そこでとりあえず Return True を追加しておきましょう。ファイルは「LivetViewModelCommand_VB.snippet」です。これをエディタで開き、編集したのち保存します。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Livet ViewModelCommand</Title>
      <Author>Livet Project</Author>
      <Description>ViewModelCommandを作成します</Description>
      <HelpUrl></HelpUrl>
      <SnippetTypes />
      <Keywords />
      <Shortcut>lvcom</Shortcut>
    </Header>
    <Snippet>
      <References />
      <Imports>
        <Import>
          <Namespace>Livet.Commands</Namespace>
        </Import>
      </Imports>
      <Declarations>
        <Literal Editable="true">
          <ID>name</ID>
          <Type />
          <ToolTip>コマンド名</ToolTip>
          <Default>MyCommand</Default>
          <Function />
        </Literal>
      </Declarations>
      <Code Language="VB" Kind="" Delimiter="$"><![CDATA[
#Region "$name$Command"
    Private _$name$Command As ViewModelCommand

    ''' <summary>
    ''' コマンドです。
    ''' </summary>
    Public ReadOnly Property $name$Command() As ViewModelCommand
        Get
            If _$name$Command Is Nothing Then
                _$name$Command = New ViewModelCommand(AddressOf $name$, AddressOf Can$name$)
            End If
            Return _$name$Command
        End Get
    End Property

    Private Function Can$name$() As Boolean
        Return True ' ← これを追加
    End Function

    Private Sub $name$()
        
    End Sub
#End Region
]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>


プロジェクトテンプレートの変更

お次はテンプレートの変更です。VB のテンプレートには Livet のインポートが宣言されてないんですよね。そこで追加しちゃいます。

あと、個人的に Option ステートメントの宣言をするようしてますので、これもついでに加えます。

Option Explicit On
Option Strict On

Imports Livet

まずプロジェクトテンプレートの編集。テンプレートは以下のフォルダにある Livet_WPF4_VB.zip です。

Visual Studio 2010 のインストールフォルダ/Common7/IDE/ProjectTemplatesCache/VisualBasic/Windows/1041


この ZIP フォルダに含まれる以下の三つのファイルを変更します。

  • Models/Model.vb
  • ViewModels/MainWindowViewModel.vb
  • Views/MainWindow.xaml.vb

この三つのファイルに Option ステートメントの宣言と、Livet のインポートを加えます。

あと作業していて思ったのが、ViewModel.vb には Livet.Commands と Livet.Messaging.Windows のインポートが必要だということ。そこで MainWindowViewModel.vb の内容を以下のように変えちゃいます。


Option Explicit On
Option Strict On

Imports Livet
Imports Livet.Commands
Imports Livet.Messaging.Windows

Public Class MainWindowViewModel
    Inherits ViewModel

    'コマンド、プロパティの定義にはそれぞれ 
    ' 
    '  lvcom   : ViewModelCommand
    '  lvcomn  : ViewModelCommand(CanExecute無)
    '  llcom   : ListenerCommand(パラメータ有のコマンド)
    '  llcomn  : ListenerCommand(パラメータ有のコマンド・CanExecute無)
    '  lprop   : 変更通知プロパティ
    '  
    'を使用してください。

    'ViewModelからViewを操作したい場合は、
    'Messengerプロパティからメッセージ(各種InteractionMessage)を発信してください。

    'UIDispatcherを操作する場合は、DispatcherHelperのメソッドを操作してください。
    'UIDispatcher自体はApplication.xaml.vbでインスタンスを確保してあります。

    'Modelからの変更通知などの各種イベントをそのままViewModelで購読する事はメモリリークの
    '原因となりやすく推奨できません。ViewModelHelperの各静的メソッドの利用を検討してください。

End Class

VB の ViewModel クラスファイルに Import が足りないのは、開発者のugaya40 さんも認識しておられるので、たぶん次期バージョンには対応してもらえると思います。あと、個人的に Model を Friend から Public に変更しちゃいました。Friend じゃテストしづらいですからね。



項目テンプレートの変更

でもこんだけじゃ足りなくて、整合性を保つため、項目テンプレートの変更も必要になってきました。「新しい項目の追加」で出てくるアレですね。Livet が提供する五つのアイテムは、以下のフォルダにインストールされてます。


Visual Studio 2010 のインストールフォルダ\Common7\IDE\ItemTemplatesCache\VisualBasic\WPF\1041

f:id:hilapon:20120216120347p:image

これらのファイルを同様に編集し保存します。これで次回から項目を追加する際、変更が反映されてます。


#なにかのお役に立てば幸いです。


 

*1:まぁ仕方ないんですがねェ

hilaponhilapon 2012/02/21 19:24 ViewModelCommand のスニペット改造を追加しときました

masa-kmasa-k 2012/02/27 12:51 こうやってフレームワークの質を相互的に高めていけるのは oss の良いところだなと思います♪
Livet 使ってみなきゃなぁ。。。(汗

さて、重箱の隅をつつく様なお話しを少々。。。

> プロパティのコメント
「$propname$を取得、設定します。」とかにして、プロパティ名を入力する様に促してあげるとより親切かな、と。

> Return True ' ← これを追加
常に実行可能 (Return True) なコマンドは本来 "lvcomn" や "llcomn" で生成すべきなので、
"lvcom" や "llcom" のテンプレートとしては
「 Throw New NotImplementedException() 」
の方が良いのではないかと思います。

# ただ、これだと実行時にエラーになるのでむしろコンパイル時エラーの方がいい気もしなくもありませんが、
# 標準のテンプレートはみんな NotImplementedException なので云々(ry

hilaponhilapon 2012/02/27 16:04 コメント有難うございます。

> 「$propname$を取得、設定します。」とかにして、プロパティ名を入力する様に促してあげるとより親切かな、と。

そうですね。いま改造して試したら、確かにこちらの方が使いやすいです。

> Throw New NotImplementedException()

ですね。コンパイルエラーや既定で値を返すより、確かにこちらの方が良さ気な感じがします。でもビルドエラーはいやんw

Connection: close