[Xamarin.CrossPlatform] マスタ/詳細アプリのプロジェクトを理解する2

前回の「[Xamarin.CrossPlatform] マスタ/詳細アプリのプロジェクトを理解する1」では、マスタ/詳細型アプリの要素技術について取り上げました。

今回は、実行時のアプリ画面と新規プロジェクト作成後のプロジェクト構成、共通プロジェクトのApp.xaml中身を見ていきます。

とりあえず実行してみる

新規プロジェクトを作成して実行すると、以下の画面が表示されます。
これはアプリのメインとなる画面で、項目が一覧表示されます。

メイン画面の右上にある「Add」をタップすると、以下のように項目を追加するための画面が表示されます。

画面下の「About」をタップすると、アプリの情報画面が表示されます。

上記のように、大きく3つの画面で構成されていることがわかります。

プロジェクト構成

続いてプロジェクトの構成をみてみましょう。

以下はプロジェクト名を「MasterDetailSample」として作成した例です。スクショはVS for Macのものですが、構成自体はWindows版も同じです。

ソリューションには3つのプロジェクトがあります。

  1. MasterDetailSample
    iOSとAndroidの共通プロジェクトです。
  2. MasterDetailSample.Droid
    Android用のプロジェクトです
  3. MasterDetailSample.iOS
    iOS用のプロジェクトです。

共通プロジェクトの構成

共通プロジェクトを展開すると、以下のようになります。

前回説明した通り、マスタ/詳細型はMVVMパターンを採用していますので、Models, ViewModels, Viewsというようにフォルダを分けてファイルが作成されています。Servicesについては、後ほど見ていくこととします。そのほか、App.xamlというファイルがあります。

App.xaml

App.xamlのコードは以下のようになっています。

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MasterDetailSample.App">
	<Application.Resources>
		<ResourceDictionary>
			<Color x:Key="Primary">#2196F3</Color>
			<Color x:Key="PrimaryDark">#1976D2</Color>
			<Color x:Key="Accent">#96d1ff</Color>
			<Color x:Key="LightBackgroundColor">#FAFAFA</Color>
			<Color x:Key="DarkBackgroundColor">#C0C0C0</Color>
			<Color x:Key="MediumGrayTextColor">#4d4d4d</Color>
			<Color x:Key="LightTextColor">#999999</Color>
			<Style TargetType="NavigationPage">
				<Setter Property="BarBackgroundColor" Value="{StaticResource Primary}" />
				<Setter Property="BarTextColor" Value="White" />
			</Style>
		</ResourceDictionary>
	</Application.Resources>
</Application>

<Application.Resources>や<ResourceDictionary>という名前から推測できるように、アプリケーション内で使用するリソースを定義しているファイルです。

例えば、5行目は、Primaryという名前で#2196F3の色を定義しています。このようにして定義された色は、データバインディングの機構を使用してViewから使用することができます。

<Color x:Key="Primary">#2196F3</Color>

[Xamarin.CrossPlatform] マスタ/詳細アプリのプロジェクトを理解する1

数回に分けて、Xamarinのマスタ/詳細型アプリのプロジェクトについて理解を含めていきます。

今回はマスタ/詳細型プロジェクトの技術要素について見ていきます。

WindowsのVSはEnterprise 2017 Ver.15.5.2
MacのVSは7.3.2

を使用しています。

マスタ/詳細型プロジェクトの作成方法

クロスプラットフォームのアプリを作成する場合、WindowsのVisual StudioでもMacのVisual Studioでも、2つのテンプレートから作成することになります。

1つは空白のフォームのテンプレート、もう1つはマスター/詳細型のテンプレートです。

以下にプロジェクト作成時のダイアログを示します。上がWindows版で下がMac版です。

Windows版は、Master Detailを選択するとマスタ/詳細型のアプリを作成することができます。ちなみにBlank Appを選択すると空白のフォームアプリを作成することができます。

Mac版は、フォームアプリを選択することでマスタ/詳細型のアプリを作成することができます。

Windows版、Mac版とも、作成されるファイルは同じです。以降スクショはMac版を掲載します。

MVVM

マスタ/詳細型のプロジェクトは、MVVMパターンです。

MVVMはModel-View-ViewModelの略で、GUIを持つアプリケーションをModel、View、ViewModelの3つに分割して設計、実装を行います。

これはMVC(Model-View-Controller)の派生パターンになっています。

Model

Modelはビジネスロジック(アプリケーションのデータと手続き)を担う部分です。このため、表示機能については関与しませんが、背景色や文字色といった装飾部分のデータについてはModelで管理します。

View

Viewはデータを見える形にして表示したり、ユーザーからの入力を受け取る部分を担当します。Viewには複雑なロジックを持たせることはしません。基本的にはデータバインディング(後述)と呼ばれる機構を使用して、データの表示や入力の受け取りを行うようにします。

ViewModel

ViewModelはViewから受け取ったデータをModelに伝達したり、Viewの状態保持を担当します。このようにViewModelはViewとModelの仲介役となります。

データバインディング

データバインディングはUIとデータを関連付ける機構です。XamarinのCrossPlatform開発においては、Xamlの中にバインディング用の属性を記述することで、Modelが持つデータを自動で表示したり、ユーザーが入力したデータを受け取ることができるようになります。

 

今回は、マスタ/詳細型のプロジェクトの基本技術要素について触れてきました。文字ベースですし、説明がわかりにくいとことろもあるかと思います。

次回以降は、実際のコードも掲載しながら理解を進めていきます。

[Xamarin.Mac] View Controller間でデータを受け渡すには

前回の記事では、View Controllerをコードから表示する方法について紹介しました。

今回は、View Controller間でのデータの受け渡し方法について見ていきましょう。

1.メインウィンドウの作成

はじめに新規でプロジェクトを作成して、メインウィンドウにButtonを1つ、その下にText Fieldを1つ貼り付けます。

続いて、貼り付けたButtonのアクション接続を作成します。アクション名は「showSubView」とします。

次に、貼り付けたText Fieldのアウトレット接続を作成します。接続名はfirstTextとしてください。

アクション接続とアウトレット接続の作成方法については「シンプルなアプリを作ってみよう 〜アクションとアウトレット〜」を参照ください。

作成したアクション接続とアウトレット接続のコードを以下に示します。

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>


@interface ViewController : NSViewController {
    NSTextField *firstText;
}
- (IBAction)showSubView:(id)sender;
@property (assign) IBOutlet NSTextField *firstText;

@end

2.サブウィンドウの作成

続いて、もう1つView Controllerを配置します。

このView ControllerにもButtonとText Fieldを配置します。

次にView Controllerを選択してCustom Classに「SecondViewController」と入力して[return]キーを押し、[command]+[s]で上書き保存をします。この操作によってサブウィンドウ用のクラスが作成されます。このクラスはNSViewControllerを継承しています。

次にアシスタントエディタでSecondViewController.hを開いておき、今配置したText Fieldのアウトレット接続を作成します。アウトレット接続の名前はsecondTextとしてください。

またButtonのアクション接続も作成します。アクション接続名はsubViewCloseとします。

SeconViewController.hのコードは以下のようになります。

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

@interface SecondViewController : NSView {
    NSTextField *secondText;
}
@property (assign) IBOutlet NSTextField *secondText;
- (IBAction)subViewClose:(id)sender;

@end

ここまでできたら[command]+[s]を押して、Visual Studioへ戻ります。

3.セグエの作成

続いてセグエを作成しましょう。メインウィンドウからサブウィンドウに向かってドラッグ&ドロップし、セグエを作成します。表示されたManual SegueのダイアログではModalを選択することとします。

次に作成したSegueを選択して「MySegue」という名前を付けます。あとで、この名前でコードからサブウィンドウを開けるように作成します。

4.メインウィンドウのコードを作成する

続いてメインウィンドウのコードを作成するのですが、その前にサブビュー側でメインウィンドウから渡された値を受け取ることができるプロパティを作成しておきましょう。

Visual StudioでSecondViewController.csを開いて、以下のように、TextValueというプロパティを追加します。

// This file has been autogenerated from a class added in the UI designer.

using System;

using Foundation;
using AppKit;

namespace WindowSample5
{
  public partial class SecondViewController : NSViewController
  {
    // メインウィンドウから受け取る値用のプロパティ
    public string TextValue { get; set; }

    public SecondViewController (IntPtr handle) : base (handle)
    {
    }
  }
}

次に、ViewController.csを開いてメインウィンドウのコードを以下のように入力します。

using System;

using AppKit;
using Foundation;

namespace WindowSample5
{
  public partial class ViewController : NSViewController
  {
    public ViewController(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
      base.ViewDidLoad();

      // Do any additional setup after loading the view.
    }

    public override NSObject RepresentedObject
    {
      get
      {
          return base.RepresentedObject;
      }
      set
      {
          base.RepresentedObject = value;
          // Update the view, if already loaded.
      }
    }

    // ここから追加コード

    // Buttonクリック時の処理
    partial void showSubView(NSObject sender)
    {
      // Segueでサブビューを表示
      PerformSegue("MySegue", this);
    }

    // Segueでサブウィンドウが開かれる前に実行されるメソッド
    public override void PrepareForSegue(NSStoryboardSegue segue, NSObject sender)
    {
      base.PrepareForSegue(segue, sender);

      // サブウィンドウのインスタンスを取得
      SecondViewController sv = segue.DestinationController as SecondViewController;
      // サブウィンドウのプロパティに値をセット
      sv.TextValue = this.firstText.StringValue;
    }

    // サブウィンドウから呼び出してメインウィンドウのText Fieldに値をセットするメソッド
    public void SetTextValue(string textValue)
    {
        this.firstText.StringValue = textValue;
    }
  }
}

Buttonには、showSubView というアクション接続を作成したので、partial void showSubView というメソッドを追加しています。このめそっどの中では PerformForSegue メソッドを使用してサブウィンドウを開きます。

サブウィンドウを開く前に値を渡したいので、PrepareForSegue というメソッドをオーバーライドして追加しています。このメソッドは、セグエが実行される前に起動して処理が行われます。よってメソッドの中では、サブウィンドウのインスタンスを取得して、サブウィンドウのプロパティである TextValue にメインウィンドウの Text Field の値を設定しています。

最後に、サブウィンドウからメインウィンドウへ値をセットできるようにするために SetTextValue というメソッドを追加しています。引数で受け取った値をメインウィンドウのText Fieldに設定しています。

5.サブビューのコードを作成する

最後ににサブビュー側のコードを作成しましょう。

ソリューションエクスプローラーで SecondViewController.cs をダブルクリックして開き以下のようにコードを入力します。

// This file has been autogenerated from a class added in the UI designer.

using System;

using Foundation;
using AppKit;

namespace WindowSample5
{
  public partial class SecondViewController : NSViewController
  {
    // メインウィンドウから受け取る値用のプロパティ
    public string TextValue { get; set; }

	public SecondViewController (IntPtr handle) : base (handle)
	{
	}

    // ここから追加コード
    public override void ViewDidLoad()
    {
      base.ViewDidLoad();

      // Text Fieldにメインウィンドウから受け取った値をセット
      this.secondText.StringValue = this.TextValue;
    }

    partial void subViewClose(NSObject sender)
    {
      // メインウィンドウのインスタンスを取得
      ViewController vc = this.PresentingViewController as ViewController;
      // メインウィンドウのメソッドを呼び出して、サブウィンドウのText Fieldの値を渡す
      vc.SetTextValue(this.secondText.StringValue);
      // サブウィンドウを閉じる
      this.View.Window.Close();
    }
  }
}

オーバーライドした ViewDidLoad は、画面が開かれたときに初期化をするメソッドです。ここでは、メインウィンドウから受け取った値が入っている TextValue プロパティを、Text Fieldに設定しています。これによりサブウィンドウが開いたときに、メインウィンドウから受け取った値が Text Field に表示されます。

subViewClose というメソッドは Button がクリックされたときに実行されるメソッドです。この中ではメインウィンドウのインスタンスを取得して、SetTextValue というメソッドを使用して、サブウィンドウのText Fieldの値を渡しています。最後に Close メソッドを実行してサブウィンドウを閉じます。

6.実行してみよう

それでは実行して確認をしてみましょう。

メインウィンドに「メインウィンドウの値」と入力してButtoonをクリックすると、サブウィンドウが表示されText Fieldには「メインウィンドウの値」と表示されます(上がメインウィンドウ、下がサブウィンドウの図です)。

次にサブウィンドウのText Fieldに「終了」と入力してButtonをクリックすると、サブウィンドウが閉じられてメインウィンドウのText Fieldに「終了」が表示されます(上がサブウィンドウ、下がメインウィンドウの図です)。