他人が書いたコードへの依存率を高めて作成するUWPアプリ

2匹目のどじょう

ComponentOne Studio for Xamarin」のサンプルにWPFのコードを転用したことを紹介する記事がありました。

今回も、同じ趣旨で「SPREAD (スプレッド)」用に作成されたMVVMのサンプルコードを転用して、「ComponentOne Studio」のデータグリッドコンポーネントFlexGrid for UWP」にデータを表示します。
転用元サンプルコードはこちらで公開している内容です。
 

結論から書きますと、2匹目のどじょうにも関わらず無事に表示されました。

f:id:ComponentOne_JP:20170921181721p:plain

以下にその手順を解説します。

サンプル作成開始

まずはVisual Studio 2017を利用し、UWPアプリの作成です。
普通に新規アプリとして作成せずに、ここではMicrosoftが提供している「Windows Template Studio」を利用します。

Announcing Windows Template Studio – Building Apps for WindowsBuilding Apps for Windows

“Studio”なので独立した開発ツールを想像しますが、これはVisual Studio拡張機能です。利用するとウィザード形式でUWPアプリケーションのひな型が作成できます。
画面のタイプやナビゲーションを指定したひな型を作ることができるので、1から作るよりも簡単です。

f:id:ComponentOne_JP:20170921180924p:plain

新規にプロジェクトを作成し「Windows Template Studio」を選択すると、ウィザード画面が起動して作成スタートです。

f:id:ComponentOne_JP:20170922100204p:plain

この中から開発するアプリにあったものを選択します。タイプの異なる複数画面を選択できるので、ひととおりのひな型が完成します。

今回はグリッドを表示するだけですが、せっかくなのでナビゲーションを表示する「Navigation Pane」を選択しました。 下段ではフレームワークを選択できます。MVVMパターンを適用しやすくするための便利なフレームワークはいくつかあります。この画面に記載のある MVVM Light の他に Prism などが有名です。
元があるMVVMのサンプルを移植するので、シンプルな「MVVM Basic」を選択しました。

次の画面では、他の形のページを追加できます。アプリケーションの機能に応じて、各種作成することが可能です。そのままMapが表示されるページなどもあり興味をそそられますがますが、ナビゲーションで表示するためのページをもうひとつ作るのでBlankのページを追加します。 そのほかにもアプリのインタラクションとして利用可能な、トーストなどの各種通知機能を追加することもできます。

f:id:ComponentOne_JP:20170921181025p:plain

ひな型が作成すると、Model、View、ViewModel用のフォルダが作成され、ベースになるファイルもあわせて作成されます。 さらに、Navigation Paneを選択したので基本的なナビゲーションの動作に必要なコードも実装済みです。

f:id:ComponentOne_JP:20170922100314p:plain

コードを移植する

SPREADのサンプルとして提供されているコードを引用します。
まずはデータモデルです。
Modelsフォルダに新規にC#のコードファイルを作成し、内容を記述します。名前空間namespace)以外のコードをコピーして、そのまま貼り付けるだけです。

using System;
using System.Collections.ObjectModel;
namespace UWP_C1FlexGrid.Models
{
public class CustomerRequest
{
public string ID { get; set; }
public string Title { get; set; }
public DateTime OpenDate { get; set; }
public string Note { get; set; }
}
public class CustomerRequestCollection
{
private ObservableCollection<CustomerRequest> _requests;
public CustomerRequestCollection()
{
_requests = new ObservableCollection<CustomerRequest>
{
new CustomerRequest(){Title="新機能の使用方法についてご質問",OpenDate=DateTime.Today.AddDays(0) },
// 記事では省略:データの生成部分
};
}
public ObservableCollection<CustomerRequest> GetCustomerRequests()
{
return _requests;
}
}
}

ViewModelのコードは、MVVM Basicフレームワークにあわせた改造が必要です。
元のサンプルコードを引用し、作成済みのMainViewModel.csを修正します。
規定で作成されたコードは、View Modelを簡素化して記述できるようになっています。別途ヘルパーとして提供されているObservableクラスを継承しているクラスなので、それに合わせます。

プロパティとして提供するコレクションの定義とコンストラクタはコピー元そのままで利用できますが、CustomerRequestsプロパティは、ObservableクラスのSetメソッドを利用した記述にしています。
最初の引数(ref _requests)がプロパティの戻り値になる値、2番目((new CustomerRequestCollection()).GetCustomerRequests())が戻り値の取得元、最後(”CustomerRequests”)が値の変更を通知するプロパティ名です。

using System.Collections.ObjectModel;
using UWP_C1FlexGrid.Helpers;
using UWP_C1FlexGrid.Models;
namespace UWP_C1FlexGrid.ViewModels
{
public class MainViewModel : Observable
{
// 問合せのコレクション
private ObservableCollection<CustomerRequest> _requests;
public MainViewModel()
{
_requests = (new CustomerRequestCollection()).GetCustomerRequests();
}
public ObservableCollection<CustomerRequest> CustomerRequests
{
get { return _requests; }
set
{
if (_requests != value)
{
// Set メソッド 
this.Set<ObservableCollection<CustomerRequest>>
(ref _requests,
(new CustomerRequestCollection()).GetCustomerRequests(),
"CustomerRequests");
}
}
}
}
}

以下がコピー元のコードの該当部分です。プロパティの変更を通知するNotifyPropertyChangedメソッドの部分がなくなり、簡略化できていることがわかります。’Set’メソッドで一括処理しています。
このサンプルではプロパティが1つですが、View Modelに記述するプロパティが複数ある場合に、このメリットを実感できます。

public ObservableCollection<CustomerRequest> CustomerRequests
{
get { return _requests; }
set
{
if (_requests != value)
{
_requests = value;
NotifyPropertyChanged("CustomerRequests");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

View(ビュー)の作成とFlexGrid for UWPの設定

ViewsフォルダにMainPage.xamlが作成済みですので、これを改造してビューを作成します。
ビューにはデータを表示するFlexGrid for UWPを配置します。

ComponentOne Studioをインストールした環境であれば、Visual Studioツールボックスに利用可能なコンポーネントが設定済みです。そこからC1FlexGridを選択し、MainPage.xamlにドラッグ&ドロップします。

f:id:ComponentOne_JP:20170921182355p:plain

次にFlexGridの内容です。
データグリッドに表示するデータのソースは’MainViewModel.cs’にあります。このページのコードビハインドにViewModelオブジェクトを生成する処理が記述済みです。XAMLではこのViewModelオブジェクトを参照します。

public MainViewModel ViewModel { get; } = new MainViewModel();

ItemsSourceプロパティに、’ObservableCollection’クラスのリストデータセットであるCustomerRequestsを接続するには、前述の’ViewModel’オブジェクトを使います。

ItemsSource="{x:Bind ViewModel.CustomerRequests}"

データの構造に合わせて列を自動的に作ることもできますが、ヘッダー部分に任意の文字列を表示した場合は、各列(Columnクラス)を定義して、データ項目をBindingに記述します。

また各列の幅は以下です。表示領域の幅に応じて各列の幅を比率で自動調整しています。

列名 Width指定 内容
件名 * 表示幅を自動計算
受付日 Auto セル内容に応じて幅を算出
備考 * 表示幅を自動計算

以下のXAMLコードは、FlexGridに3つの列を定義したXAMLコードです。 規定で作成された<Grid>タグに囲まれた部分に記述します。

<c1:C1FlexGrid x:Name="flexgrid"
ItemsSource="{x:Bind ViewModel.CustomerRequests}"
HeadersVisibility="Column"
HorizontalContentAlignment="Stretch"
AutoGenerateColumns="False" >
<c1:C1FlexGrid.Columns>
<c1:Column Header="件名" Binding="{Binding Title}"
AllowResizing="True"
Width="*"/>
<c1:Column Header="受付日" Binding="{Binding OpenDate}"
Format="yyyy/MM/dd"
Width="Auto"/>
<c1:Column Header="備考" Binding="{Binding Note}"
Width="*"/>
</c1:C1FlexGrid.Columns>
</c1:C1FlexGrid>
ライセンスの設定

FlexGrid for UWPなどComponentOne Studioのコンポーネントを使用してUWPアプリを開発するには、ライセンスキーを設定する必要があります。

製品版またはトライアル版をご利用の方はWebサイトで、ライセンスキーの生成が可能です。詳細はWebサイトに記載しています。

ComponentOne Studio:UWPでのライセンス

取得したライセンスキーはLicense.csファイルを作成して記述し、App.xaml.cs内のコンストラクタでライセンスマネージャに設定します。

C1.UWP.LicenseManager.Key = License.Key;

View(ビュー)の作成とC1TileListBox for UWPの設定

ここまででも動作しますが、テンプレート作成時に追加したサブ画面も作成します。
FlexGridのデータ表示で目的は達成しましたが、ナビゲーション部分を表示するためにもう一つの画面を作成します。

サブ画面には、同じデータソースを利用して各データ項目をボックス形式で表示します。利用するコンポーネントはListBoxです。項目をタイル状に表示できるC1TileListBoxの形で利用します。

ComponentOne Studio:C1ListBox

C1TileListBoxはタイル状にボックスを並べることができます。FlexGridで表示したデータの1行を、1つのボックスに置き換えるイメージです。
実際にはボックスの中にテキストの表示領域を作成したデータテンプレートをタイル状に並べることになります。
以下がそのXAMLコードです。
FlexGridを記述した場合と同じように、規定で作成された<Grid>タグに囲まれた部分に記述します。 ListBoxのItemsSourceには、FlexGridと同じViewModelからデータセットを設定しています。

<c1:C1TileListBox ItemsSource="{x:Bind ViewModel.CustomerRequests}"  >
<c1:C1TileListBox.PreviewItemTemplate>
<DataTemplate>
<Grid Background="Gray"/>
</DataTemplate>
</c1:C1TileListBox.PreviewItemTemplate>
<c1:C1TileListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical"
Background="DeepSkyBlue"
Width="300" Height="200" >
<TextBlock Text="{Binding Title}"
Foreground="LightGoldenrodYellow"/>
<TextBlock Text="{Binding OpenDate}"
Foreground="White"/>
</StackPanel>
</DataTemplate>
</c1:C1TileListBox.ItemTemplate>
</c1:C1TileListBox>

コードビハインドではViewModelを設定しています。規定で作成されたコードを置き換えて、MainPageと同じViewModelを指定します。

public sealed partial class SubPage : Page
{
// public SubViewModel ViewModel { get; } = new SubViewModel();
public MainViewModel ViewModel { get; } = new MainViewModel();
public SubPage()
{
InitializeComponent();
}
}

以上で完成です。ナビゲーション部分はテンプレートで作成された状態に追加することなく2つの画面切り替えが可能になりました。
左上のハンバーガーメニューで、ナビゲーションの展開と折りたたみもできます。

f:id:ComponentOne_JP:20170922100637p:plain

このように、ViewModelを共用してViewを変更すれば異なるユーザーインタフェースでデータを表示できます。

なお、ナビゲーションにページのタイトルとして表示される「Main」「Sub」は、リソースとして定義されています。Resources.reswファイルを変更して日本語表示に変更可能です。また、各言語用のリソースファイルを作成して切り替えることで多言語対応のUIも作成できます。

f:id:ComponentOne_JP:20170922101002p:plain

まとめ

公開済みのサンプルを移植する例なので、シンプルな内容です。
そのためプロジェクト作成の段階で、Windows Template Studioを利用することを選択しました。
テンプレートの中には、チャートやグリッドを作成するものもあります。
ツールボックスの画像でお見せしたように、ComponentOne StudioにはUWPで利用可能な数々のコンポーネントが揃っています。
これらもテンプレートと組み合わせてアプリケーション開発にご利用ください。コンポーネントで提供している各機能は、Windows ストアで公開しているサンプルで体験できます。

オンラインデモ – ComponentOne | グレープシティ コンポーネント製品


作成したソリューションはこちらで公開しています。
サンプルで使ったComponentOne Studio のライセンスも含まれますので実行可能です。

ダウンロード(zipファイル:44.8KB)


XAML技術(WPF、UWP、Xamarin)のイベント開催

WPF、UWP、Xamarinと、XAMLを利用する各アプリ開発技術のエキスパートをお迎えし、開発事例を中心としたセッション主体のイベントを行います。

※この記事の投稿時点で、大変ありがたいことに既に満席(100名)となっています。申し訳ありませんが、参加ご希望の方はキャンセル待ちへのご登録をお願いいたします。