提督業プラグインを作ってみよう(2)艦隊情報を出そう
1.艦これの情報を取ってこよう
KanColleViewer本家のソース を参考にして艦これの情報を取ってくるModelクラスを作ります。 ModelsフォルダにKanColleModel.csを作りましょう。 以下にクラスの中身を記載します。
public class KanColleModel : Livet.NotificationObject, IDisposableHolder { #region singleton public static KanColleModel Current { get; } = new KanColleModel(); #endregion #region Fleets変更通知プロパティ private IEnumerable<Fleet> _Fleets; public IEnumerable<Fleet> Fleets { get { return _Fleets; } set { if (_Fleets == value) return; _Fleets = value; RaisePropertyChanged(nameof(Fleets)); } } #endregion #region IsRegistered変更通知プロパティ private bool _IsRegistered = false; //念のためこうしとく public bool IsRegistered { get { return _IsRegistered; } set { if (_IsRegistered == value) return; _IsRegistered = value; RaisePropertyChanged(nameof(IsRegistered)); } } #endregion private readonly LivetCompositeDisposable compositeDisposable = new LivetCompositeDisposable(); public ICollection<IDisposable> CompositeDisposable => this.compositeDisposable; /// <summary> /// 外からインスタンスが作れないようにコンストラクタをprivateにする /// </summary> private KanColleModel() { KanColleClient.Current .Subscribe(nameof(KanColleClient.IsStarted), this.RegisterHomeportListener, false) .AddTo(this); } /// <summary> /// 艦これが始まったときに一度だけ呼ばれます /// </summary> private void RegisterHomeportListener() { if (this.IsRegistered) return; var client = KanColleClient.Current; //Organization.Fleetsをチェックして、変化があったらUpdateFleetsを呼ぶ client.Homeport.Organization .Subscribe(nameof(Organization.Fleets), () => this.UpdateFleets(client.Homeport.Organization)) .AddTo(this); this.IsRegistered = true; //開始フラグを立てます } /// <summary> /// 艦隊に変化があったときに呼ばれる /// </summary> /// <param name="organization">organization</param> private void UpdateFleets(Organization organization) { //艦隊に変化があったらKanColleModel.Fleetsを更新する this.Fleets = organization.Fleets.Values; } public void Dispose() { this.compositeDisposable.Dispose(); } }
名前空間やusingは自力で何とかしましょう
2.ViewModelを実装しよう
ViewModelsフォルダにInformationViewModel.cs、FleetViewModel.cs、ShipViewModel.csを追加します。実装は以下に記載します。
InformationViewModel.cs(メインのViewModel)
public class InformationViewModel : ViewModel { //艦これの情報はここからみんな持ってきます private KanColleModel Kancolle { get; } = KanColleModel.Current; private PropertyChangedEventListener listener; /// <summary> /// 艦隊情報 /// </summary> public FleetViewModel[] Fleets { get; private set; } public InformationViewModel() { listener = new PropertyChangedEventListener(Kancolle); //艦これの艦隊情報の変化を監視して、変化があったらUpdateFleets()を呼ぶ listener.RegisterHandler(() => Kancolle.Fleets, (s, e) => UpdateFleets()); this.CompositeDisposable.Add(listener); } /// <summary> /// 艦隊情報が更新されたときに呼ばれる /// </summary> private void UpdateFleets() { var fleets = this.Kancolle.Fleets; if (fleets == null) return; Fleets = fleets.Select(f => new FleetViewModel(f)).ToArray(); RaisePropertyChanged(nameof(Fleets)); } }
FleetViewModel.cs(艦隊情報のViewModel)
public class FleetViewModel : ViewModel { #region Name変更通知プロパティ private string _Name; public string Name { get { return _Name; } set { if (_Name == value) return; _Name = value; RaisePropertyChanged(nameof(Name)); } } #endregion //艦隊に所属する艦娘の情報 public ShipViewModel[] Ships { get; private set; } private PropertyChangedEventListener listener; /// <summary> /// コンストラクタ /// </summary> /// <param name="f">f</param> public FleetViewModel(Fleet f) { Name = f.Name; //艦隊名をセット Update(f.Ships); listener = new PropertyChangedEventListener(f); //艦隊の艦娘たちの変化を監視して、変化があったらUpdate()を呼ぶ listener.RegisterHandler(() => f.Ships, (s, e) => Update(f.Ships)); //艦隊名の変化を監視して、変化があったら艦隊名を更新する listener.RegisterHandler(() => f.Name, (s, e) => Name = f.Name); this.CompositeDisposable.Add(listener); } /// <summary> /// 艦隊に所属する艦娘の状態が変わったら呼ばれる /// </summary> /// <param name="ships">ships</param> protected virtual void Update(IEnumerable<Ship> ships) { this.Ships = ships.Select(s => new ShipViewModel(s)).ToArray(); RaisePropertyChanged(nameof(Ships)); } }
ShipViewModel.cs(単体の艦娘のViewModel) KanColleViewer本家のShipViewModel.cs を参考にします。
public class ShipViewModel : ViewModel { #region Ship変更通知プロパティ private Ship _Ship; public Ship Ship { get { return _Ship; } set { if (_Ship == value) return; _Ship = value; RaisePropertyChanged(nameof(Ship)); } } #endregion public ShipViewModel(Ship s) { this.Ship = s; } }
3.Viewにバインディングしよう
一度ソリューションをビルドして、コンパイルエラーが出たらつぶしましょう。このサイトではC#やWPFの基本的なことは解説しません。インターネットは広大なので、助けを求めれば、一人くらいは相談に乗ってくれる人がいるでしょう。
コンパイルエラーが出なくなったら、Information.xamlの中身を実装していきましょう。以下に記載したソースをコピペ改変して自分のプロジェクトに合わせていきましょう。 Information.xaml(メインのユーザーコントロール)
<UserControl x:Class="プロジェクト名.Views.Information" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:プロジェクト名.ViewModels" mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="400"> <UserControl.Resources> <!-- 艦隊データテンプレート --> <DataTemplate x:Key="Fleet" DataType="{x:Type vm:ShipViewModel}"> <StackPanel Orientation="Horizontal" DataContext="{Binding Ship}"> <TextBlock TextWrapping="Wrap"> <Run Text="{Binding Info.Name, Mode=OneWay}"/> <Run Text="{Binding Level, Mode=OneWay, StringFormat=Lv.\{0:D\}}" /> <Run Text="{Binding Condition, Mode=OneWay, StringFormat=(\{0:D\})}"/> </TextBlock> </StackPanel> </DataTemplate> </UserControl.Resources> <StackPanel> <ItemsControl ItemsSource="{Binding Fleets}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Background="DarkGray" Foreground="LightGray"> <Run Text="{Binding Name}" /> </TextBlock> <ItemsControl ItemsSource="{Binding Ships}" ItemTemplate="{DynamicResource Fleet}" > </ItemsControl> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </UserControl>
全部実装したらビルドして、生成されたdllをKanColleViewerのPluginsフォルダに上書きコピーしてKanColleViewerを起動すると、以下の画像のようなウインドウが出てくるはずです。
艦隊名と、艦隊に所属している艦娘の名前、レベル、Cond値が表示されます。 このままでもCond値チェックは出来ますが、デザインがイケてなかったり、ウインドウを閉じたらKanColleViewerを再起動しないといけなかったりと、まだまだですね。 これらの問題は次回以降解決していきます。