前回はテンプレートのせいでビルドできない問題を解消しました。

Problem

Xamarin.Formsで**Blank App (Portable)**でプロジェクトを作ります。
なんかGoogle先生で聞くと、PCLの方が良いよ?的な話を聞くので従います。

サンプルソースは https://github.com/takuya-takeuchi/Demo/tree/master/Xamarin.Forms.Portable1

で、PCLプロジェクトに App.cs ってあります。これがエントリポイントでしょう。
ソースを見ると、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;

namespace Xamarin.Forms.Portable1
{
public class App : Application
{
public App()
{
// The root page of your application
MainPage = new ContentPage
{
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
XAlign = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
}
}
}
};
}

protected override void OnStart()
{
// Handle when your app starts
}

protected override void OnSleep()
{
// Handle when your app sleeps
}

protected override void OnResume()
{
// Handle when your app resumes
}
}
}

ってあります。さっするにコードからUIを構築しているものと思われます。
でも、分離コードどころか、今日日こんなソースプロジェクトで使っていたら絶対に「うわぁ」って言われます。
いや会社のWinFormsのプロジェクトは諸般の事情でこうなってますけど。

Resolution

とりあえず、分離します。
どうもXAMLが使えるようです。

なので、PCLにXAMLを追加します。Viewsフォルダを作ってそこにViewとなるPageとかを追加します。

新しい項目の追加

3つあって迷う…

で、Forms ContentPageForms ContentViewForms Xaml Pageってあります。
ContentPage

A Page that displays a single view.

訳:単一のビューを表示するページです

ContentViewは、

An element that contains a single child element.

訳:単一の子要素を含む要素です

Pageは、

A VisualElement that occupies the entire screen.

訳:スクリーン全体を占有するVisualElementです

とのこと。それぞれ試しに作ってみると、

種類 内容
Forms ContentPage csファイル
Forms ContentView csファイル
Forms Xaml Page csファイル
Xamlファイル

となります。

ContentPage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

using Xamarin.Forms;

namespace Xamarin.Forms.Portable1.Views
{
public class MainPage : ContentPage
{
public MainPage()
{
Content = new StackLayout
{
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
}
}

ContentView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

using Xamarin.Forms;

namespace Xamarin.Forms.Portable1.Views
{
public class MainContentView : ContentView
{
public MainContentView()
{
Content = new Label { Text = "Hello ContentView" };
}
}
}

Xaml Page (cs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace Xamarin.Forms.Portable1.Views
{
public partial class Page : ContentPage
{
public Page()
{
InitializeComponent();
}
}
}

Xaml Page (xaml)

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Portable1.Views.Page">
<Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

目的を達成するには、Xaml Pageが正しいようです。
でも、Xamlを見るとBindingとかあります。DataContextを適用可能なのでしょう。
Prism.Mvvmが使えるようですので、NuGetで追加します。
ViewModelsフォルダを追加して、ViewModelを追加します。

MainPageViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using Microsoft.Practices.Prism.Mvvm;

namespace Xamarin.Forms.Portable1.ViewModels
{
public sealed class MainPageViewModel : BindableBase
{

public MainPageViewModel()
{
this.MainText = "Hello world from MVVM!!";
}

private string _MainText;

public string MainText
{
get
{
return this._MainText;
}
set
{
this._MainText = value;
this.SetProperty(ref this._MainText, value);
}
}
}
}

これをViewにバインディングします。

MainPage.xaml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="Xamarin.Forms.Portable1.Views.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:Xamarin.Forms.Portable1.ViewModels;assembly=Xamarin.Forms.Portable1">
<ContentPage.BindingContext>
<viewModels:MainPageViewModel />
</ContentPage.BindingContext>
<Label HorizontalOptions="Center"
Text="{Binding MainText}"
VerticalOptions="Center" />
</ContentPage>

DataContextではなく、BindingContextというのがミソです。
最後にエントリポイントである、App.csを変更します。

App.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;
using Xamarin.Forms.Portable1.Views;

namespace Xamarin.Forms.Portable1
{
public class App : Application
{
public App()
{
// The root page of your application
MainPage = new MainPage();
}

protected override void OnStart()
{
// Handle when your app starts
}

protected override void OnSleep()
{
// Handle when your app sleeps
}

protected override void OnResume()
{
// Handle when your app resumes
}
}
}

で、iPhone Simulatorでデバッグ実行です。

デバッグ結果

Hello MVVM!!!

iOS側のプロジェクトは一切いじっていません。
素晴らしい(´ω`)





Android?Windows Phone?次回やります(多分)

Conclusion

MVVMを使って、それらしいプロジェクトを使ってみました。今のところ各デバイス固有のプロジェクトの意味が見えてきませんが。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/Xamarin.Forms.Portable1