Visual Studio 2019 での Xamarin.Forms のデザイン時支援機能
Published Jun 24 2019 08:34 PM 8,304 Views
Microsoft

Xamarin.Forms にプレビューワーが表示されて結構な時間が経ちました。

プロパティウィンドウ対応もしたので、XAML でインテリセンスが効いた状態で画面プレビューを見ながら、プロパティウィンドウを使ってプロパティの設定も出来るという、一通り手書きで XAML を書くために必要な機能はそろってきてる感じがあります。

コメント 2019-06-25 113043.jpg

 

ここでは、いくつかデザイン時の支援機能で便利なものをいくつか紹介していこうと思います。

 

デザイン時の支援機能の有効化

有効化といっても名前空間を定義するだけです。以下のようにデザイン時の支援機能を使いたい XAML ファイルのルートの要素に d 名前空間と mc 名前空間を定義して mc:Ignorable="d" を追加します。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="App3.MainPage">

d 名前空間に色々なデザイン時の機能が詰まっていて、mc:Ignorable="d" で実際にアプリが動くときには無視するように設定しています。

これで、デザイン時の各種機能が使うための準備が整いました。

 

デザイン時のデータ設定

データーは本番にならないと設定されない。でもデザイナーでデータが入った時の見た目を確認したい。そんなときは d:プロパティ名="デザイン時の値" を指定することでデザイン時のプロパティ値を設定できます。

 

例えば以下の例では Label の Text プロパティに Welcome to Xamarin.Forms! が設定されてデザイナーにも実際に、その値が表示されています。

コメント 2019-06-25 113732.jpg

これをデザイン時は別のテキストが表示されるようにする場合は Label に d:Text 属性を設定します。

コメント 2019-06-25 113823.jpg

このデザイン時データの設定は、単純なプロパティだけではなく ListView などの ItemsSource にも設定できます。例えば以下の例では、デザイン時の ListView の ItemSource に文字列の配列を設定しています。

コメント 2019-06-25 114610.jpg

 

データバインディングのパスの補完

デザイン時のデータを使って BindingContext に ViewModel を設定するとデザイナーで Binding のパスに対してインテリセンスが効くようになります。もちろん、普通にデザイン時のデータではなく XAML から BindingContext を設定するだけでインテリセンスは効くようになりますが、コードから BindingContext を指定したり、MVVM フレームワークにより BindingContext が設定される場合にはデザイン時の BindingContext へ ViewModel を設定することでインテリセンスが効くようになるのでお勧めです。

 

デザイン時の BindingContext に設定したい場合には、デフォルトのコンストラクターが必須になります。少し残念ですが、デザイン時の支援機能の恩恵を受ける場合には不要でも定義しましょう…。例えば以下のような ViewModel クラスがあるとします。

namespace App3
{
    public class MainPageViewModel
    {
        private readonly ISomeRepository _someRepository;

        // for designer
        public MainPageViewModel()
        {
        }

        // for production
        public MainPageViewModel(ISomeRepository someRepository)
        {
            _someRepository = someRepository;
        }

        public string Input { get; set; }
        public SomeItem SomeItem { get; set; }
    }

    public class SomeItem
    {
        public string Value { get; set; }
    }
}

これを d:ContentPage.BindingContext に設定すると以下のように Binding のパスに対してインテリセンスが働くようになります。

コメント 2019-06-25 115547.jpg

ただ、残念ながらネストしたプロパティにはインテリセンスが効かないみたいです。

コメント 2019-06-25 115627.jpg

これとデザイン時のデータを組み合わせることで Binding のパスの補完をしつつ、プレビューワーでは適当なデータを表示したときの結果を確認できます。

コメント 2019-06-25 115821.jpg

 

デザイン時の判別方法

デザイン時以外で呼ばれたくない処理(デザイン時の BindingContext に設定するオブジェクトのコンストラクターなど)は、実際の処理で呼ばれて動くとバグの原因になりかねません。

いくつか対処方法はあるので紹介したいと思います。

 

まず、#if DESIGNTIME ~ #endif で括ると、デザイン時のみコンパイル対象に含まれるコードが書けます。なので、デフォルトコンストラクターのようにデザイン時以外に呼ばれるとまずい機能自体をくくってしまうとデザイン時以外では、そもそも呼び出せない処理になります。

using System;
using Xamarin.Forms;

namespace App3
{
    public class MainPageViewModel
    {
        private readonly ISomeRepository _someRepository;

#if DESIGNMODE
        // for designer
        public MainPageViewModel()
        {
        }
#endif

        // for production
        public MainPageViewModel(ISomeRepository someRepository)
        {
            _someRepository = someRepository;
        }

        public string Input { get; set; }
        public SomeItem SomeItem { get; set; }
    }

    public class SomeItem
    {
        public string Value { get; set; }
    }
}

ただ、この方法はプレビューワーにはいいのですが XAML エディターには効果がないみたいでエラーになってしまいます。

コメント 2019-06-25 122821.jpg

その他に実行時にデザインモードか判断する方法として DesignMode.IsDesignModeEnabled というプロパティが容易されています。これを使ってデザインモード以外から呼ばれた場合には例外を出すようなコードを書くことも出来ます。

// for designer
public MainPageViewModel()
{
    if (!Xamarin.Forms.DesignMode.IsDesignModeEnabled)
    {
        throw new InvalidOperationException();
    }
}

恐らく、こちらのほうが現実的です。

 

まとめ

プレビューワーを使わないにしても Binding のパスの補完は便利なのでデザイン時データに ViewModel を設定するのは個人的にお勧めです。typo による表示されない問題などが劇的に減ると思います。

 

それでは、楽しい Xamarin.Forms 開発を!

Version history
Last update:
‎Jun 24 2019 08:37 PM
Updated by: