8月 222015
 
Pocket

KanColleViewer プラグインの作り方などをダラダラと書きます。
(何か思いついたら適宜更新するかも)

※ KanColleViewer 4.2 現在のもの


チュートリアル

めとべや東京 #9 でLTしたやつ。


プラグイン開発用パッケージ一覧

NuGet Gallery から取得できる、プラグイン開発用のパッケージ郡です。
KanColleViewer.Composition のみ必須となります。

KanColleViewer.Composition

プラグイン向けのインターフェイス郡です。
これがないとプラグインが作れません。
詳細は「プラグイン インターフェイス一覧」を参照してください。

KanColleViewer.PluginAnalyzer

プラグイン開発を支援する Roslyn Analyzer & Fixer です。(要: VS 2015)
必要な実装方法や修正候補を提示してくれます。
インストール後、Visual Studio の再起動が必要な場合があります。

KanColleViewer.Controls

KanColleViewer のUI部品です。
Style, Theme も含みますので、UI があるプラグインならインストールした方が良いでしょう。
使用方法は KanColleViewer 本体で使用している部分を参照すると良いかと。

KanColleWrapper

KanColleViewer の通信部とデータ部です。
本体が保持するデータへのアクセスや、通信の読み取りに利用します。

大体のデータ構造
  • KanColleClient
    • KanColleProxy
      • SessionSource
      • ApiSessionSource
    • Master
      • Ships
      • SlotItemEquipTypes
      • SlotItems
      • UseItems
      • ShipTypes
      • Missions
      • MapAreas
      • MapInfos
      • MapCells
    • Homeport
      • Organization
        • Ships
        • Fleets
        • CombinedFleet
      • Materials
      • Itemyard
        • SlotItems
        • UseItems
      • Dockyard
      • Repairyard
      • Quests
      • Admiral
Grabacr07.KanColleWrapper.KanColleClient

KanColleWrapper のコア部品です。
基本的にはここに全て集約されています。
インスタンスは KanColleClient.Current で取得できます。

Grabacr07.KanColleWrapper.KanColleProxy

KanColleWrapper の通信部です。
SessionSource から全HTTP通信データが、ApiSessionSource から /kcsapi 以下の通信データが取得できます。
KanColleClient が解析していないデータは、これらを用いてプラグイン側で独自に解析することが可能です。

Grabacr07.KanColleWrapper.Master

api_start2 から取得された、艦これのマスターデータです。

Grabacr07.KanColleWrapper.Homeport

艦これのユーザーデータです。
api_start2 が降ってきて、KanColleClient.Current.IsStartedtrue になるまでは null なので注意。
プラグインコードで参照したい場合の変更通知購読方法は、Grabacr07.KanColleViewer.Models.NotifyServiceRegisterHomeportListener の登録が参考になります。

Grabacr07.KanColleWrapper.Models.SvData

svdata= から始まる艦これのjsonデータの解析に用います。
IObservable.TryParse<SvData<T>>() で解析する際に頻出します。
Data プロパティでレスポンスデータを、Request プロパティでリクエストパラメーターを取得可能です。

KanColleClient.Current.Proxy.ApiSessionSource
    .Where(x => x.Request.PathAndQuery == "/kcsapi/api_req_sortie/airbattle")
    .TryParse<sortie_airbattle>().Subscribe(x => this.Update(x.Data));

プラグイン インターフェイス一覧

KanColleViewer.Composition に含まれる各種プラグインインターフェイスです。
プラグインアセンブリには IPlugin インターフェイスを実装したクラスが最低1つは必要となります。

IPlugin

プラグインを表します。
画面や設定等が不要なプラグインは、これだけ実装すれば済むこともあるでしょう。

ITool

[ツール] タブに表示される画面を用意したい場合に実装します。

Member Description
string Name [ツール]タブに表示される名前
object View [ツール]タブ内に表示するUIのルート要素
  • Name プロパティは、長い名前をつけるとツールタブの幅が広がってしまうため、短めの名称をつけることをお勧めします。
  • View プロパティは、当該プラグインのタブが開かれるたびに呼び出されます。
    • 一見、毎度同一インスタンスを返せば良い用に思えますが、マルチウィンドウ時に正常に表示されなくなるため、今のところは毎回 new して返す必要があります。
    • ViewModel は使いまわすことをお勧めします。

ISettings

プラグインの設定画面を用意したい場合に実装します。

Member Description
object View プラグイン一覧画面の[設定]リンクをクリック時に表示されるUIのルート要素

INotifier

通知要求を受けて、ユーザーに通知を行いたい場合に実装します。

Member Description
void Notify(INotification notification) 本体からの通知要求時に呼び出されます。

IRequestNotify

プラグイン側から本体に通知を要求したい場合に実装します。

Member Description
event EventHandler‹NotifyEventArgs› NotifyRequested 本体に通知要求を送りたい時に Invoke します。

ILocalizable

多言語対応を行いたい場合に実装します。

Member Description
void ChangeCulture(string cultureName) 言語設定が変更された際に呼びだされ、言語が通知されます。

ITaskbarProgress

タスク バーのプログレス インジケーターに状態を報告したい場合に実装します。

Member Description
string Id この機能をシステムが識別するための ID を取得します。
string DisplayName この機能をユーザーが選択するときに識別するための名前を取得します。
TaskbarItemProgressState State プログレス インジケーターに報告する現在の状態を取得します。
double Value プログレス インジケーターに報告する現在の値を取得します。
event EventHandler Updated State または Value が変更されたときに発生します。


プラグイン識別用属性(Export, ExportMetadata)のルール

この項については、基本的には KanColleViewer.PluginAnalyzer を入れていればそれが全部指摘してくれます。

  • プラグイン インターフェイスを実装したら、対応する Export を記述しなければなりません。
    • MEF の仕組み上、Export 属性を手がかりにプラグインが Import されるためです。
  • IPlugin インターフェイスを実装したクラスは、ExportMetadataGuid, Title, Description, Version, Author を記述しなければなりません。
  • その他のプラグイン インターフェイスを実装したクラスは、ExportMetadataGuid を記述しなければなりません。
  • ExportMetadataGuid が同一なクラスは、同一プラグインとみなされます。
    • つまりもし機能別にクラスを分割した場合、機能側のクラスには、機能を付与したい IPlugin のクラスの Guid と同じ Guid を記述する必要があります。
    • 逆に1クラスに全て集約する場合は GuidExportMetadata の記述は1つで済みます。

画面系プラグインのデザイナ表示に必要な Styles

画面系プラグインを実装する場合、そのままではXAMLデザイナ上で実行時の表示を確認することができません。
デザイナで確認したい場合は、以下のような ResourceDictionary を読み込むようにしてください。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Colors.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.Button.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.CheckBox.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.Expander.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.FocusVisualStyle.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.PasswordBox.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.RadioButton.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.Scrollbar.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Controls.Tooltip.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Styles/Icons.xaml" />

        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Themes/Dark.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MetroRadiance;component/Themes/Accents/Purple.xaml" />

        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Colors.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.HorizontalFlatListBox.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.ListView.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.PinButton.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.TabControl.xaml" />
        <ResourceDictionary Source="pack://application:,,,/KanColleViewer.Controls;component/Styles/Controls.Text.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

加えて、画面のルート要素に以下の設定を行ってください。

  • Background="{DynamicResource ThemeBrushKey}"
  • Foreground="{DynamicResource ActiveForegroundBrushKey}"

なお MetroRadiance のアクセントカラーですが、プラグイン側で読み込んでしまうと本体側のアクセントカラーの変更に追従できなくなるため、追従したい場合は現状では読み込まないようにする必要があります。
→ MetroRadiance が修正され、追従できるようになりました。

また上記で Controls.xaml の内容を全て展開しているのは、ネストした MergedDictionaries をデザイナがうまく解釈してくれないためです。


その他利用できるライブラリ

本体が使っているライブラリに関しては、プラグイン側で導入しても特別なにもしないでも動作するはずです。
それ以外のライブラリについては、プラグイン インストール時にパスの通るフォルダに DLL を配置する必要があるでしょう。

以下は本体でも利用しており、プラグイン開発にも便利なライブラリ群です。

Livet

参照 : Livet – ProjectHome – the sea of fertility

StatefulModel

参照 : StatefulModelについて – the sea of fertility

Rx (Reactive Extensions)

参照 : Rx入門 – xin9le.net

MetroRadiance

参照 : ウィンドウの枠を簡単に光らせるライブラリを公開しました | grabacr.nét

MetroTrilithon

参照 : Grabacr07/MetroTrilithon

設定ファイル保存に使える SerializablePropertyFileSettingsProvider や、クリック時に VM の指定メソッドを呼び出せる CallMethodButton、各種 Converter 等を含みます。
使い方は KanColleViewer.exe のソースを参照すると良いでしょう。
設定関連は Grabacr07.KanColleViewer.Models.Settings 名前空間にあります。


小ネタ

プラグイン読み込みタイミング

プラグインは、アプリケーション起動後、通信開始前に読み込まれます
KanColleViewer.exe の Grabacr07.KanColleViewer.Composition.PluginService にてプラグインを読み込んでいますので、詳細が知りたい場合はそちらを参照してください。

プラグインのデバッグ実行

プロセスにアタッチするか、本体付属のプラグイン同様、ソリューションの Plugins フォルダに追加してしまう手があります。
Plugins フォルダに入れる場合は、他のプラグイン同様ビルド イベントを用いてバイナリが自動的にコピーされるようにすると便利でしょう。

mkdir "$(SolutionDir)Grabacr07.KanColleViewer\bin\$(ConfigurationName)\Plugins"
xcopy /Y "$(TargetDir)*.*" "$(SolutionDir)Grabacr07.KanColleViewer\bin\$(ConfigurationName)"
move "$(SolutionDir)Grabacr07.KanColleViewer\bin\$(ConfigurationName)\$(TargetName).*" "$(SolutionDir)Grabacr07.KanColleViewer\bin\$(ConfigurationName)\Plugins"
プラグイン プロジェクトの新規作成

「クラス ライブラリ」テンプレートでも良いのですが、画面系の場合はどうせユーザー コントロールを作ることになるため、「WPF ユーザー コントロール ライブラリ」で作成することをお勧めします。

プラグインでリソースの後始末とかしたい場合

IDisposable を実装しておけば、MEF が勝手に Dispose() 呼んでくれるっぽい。

Managed Extensibility Framework – Documentation

Parts that implement IDisposable will have the Dispose method called

  • IPlugin の生存期間はアプリの起動時~終了時
  • アプリ終了時に CompositionContainer のデストラクタが Dispose() 呼んでくれてる気がする
    • 今のところ(ver.4.2)クラッシュした時はダメかもしれんね

 Leave a Reply