モデル-ビュー-ビューモデル パターンThe Model-View-ViewModel Pattern

Xamarin.Forms の開発者エクスペリエンスには、通常、XAML でのユーザー インターフェイスを作成し、ユーザー インターフェイスで動作する分離コードが含まれます。The Xamarin.Forms developer experience typically involves creating a user interface in XAML, and then adding code-behind that operates on the user interface. アプリでは、変更すると、し、規模と範囲が大きくなったり、複雑なメンテナンスの問題が発生することができます。As apps are modified, and grow in size and scope, complex maintenance issues can arise. これらの問題には、UI コントロールと UI の変更、およびそのようなコードの単体テストの難しさのためのコストが増加するビジネス ロジックの間の密結合が含まれます。These issues include the tight coupling between the UI controls and the business logic, which increases the cost of making UI modifications, and the difficulty of unit testing such code.

モデル-ビュー-ビューモデル (MVVM) パターンは、そのユーザー インターフェイス (UI) からアプリケーションのビジネスとプレゼンテーション ロジックを明確に分離するのに役立ちます。The Model-View-ViewModel (MVVM) pattern helps to cleanly separate the business and presentation logic of an application from its user interface (UI). アプリケーション ロジックと UI の明確な分離を維持し、開発のさまざまな問題に対処するのに役立つ、し、アプリケーションのテスト、保守、および進化を簡単にします。Maintaining a clean separation between application logic and the UI helps to address numerous development issues and can make an application easier to test, maintain, and evolve. コードの再利用機会も大幅に向上し、により、開発者とより UI デザイナーは、それぞれの部品のアプリを開発するときに簡単に共同作業します。It can also greatly improve code re-use opportunities and allows developers and UI designers to more easily collaborate when developing their respective parts of an app.

MVVM パターンThe MVVM Pattern

MVVM パターンでは次の 3 つのコア コンポーネントがある: モデル、ビュー、およびビュー モデルです。There are three core components in the MVVM pattern: the model, the view, and the view model. それぞれには、個別の目的が機能します。Each serves a distinct purpose. 図 2-1 は、次の 3 つのコンポーネント間の関係を示しています。Figure 2-1 shows the relationships between the three components.

図 2-1:MVVM パターンFigure 2-1: The MVVM pattern

各コンポーネントの役割を理解するだけでなく、相互作用する方法を理解しておく必要がもします。In addition to understanding the responsibilities of each component, it's also important to understand how they interact with each other. 大まかに言えば、ビュー「認識」ビュー モデル、ビュー モデル「認識」のモデルがモデルはビュー モデルを認識しないとビュー モデルはビューの認識しません。At a high level, the view "knows about" the view model, and the view model "knows about" the model, but the model is unaware of the view model, and the view model is unaware of the view. したがって、ビュー モデルは、モデルからビューを分離し、によりモデル、ビューに独立して進化を。Therefore, the view model isolates the view from the model, and allows the model to evolve independently of the view.

MVVM パターンを使用する利点は次のとおりです。The benefits of using the MVVM pattern are as follows:

  • 既存のビジネス ロジックをカプセル化する既存のモデルの実装がある場合は、困難または変更するリスクの高い、できます。If there's an existing model implementation that encapsulates existing business logic, it can be difficult or risky to change it. このシナリオでは、ビュー モデルは、モデル クラスのアダプターとして機能し、モデルのコードに主要な変更を加えるを回避することができます。In this scenario, the view model acts as an adapter for the model classes and enables you to avoid making any major changes to the model code.
  • 開発者は、ビューを使用せず、ビュー モデルとモデルの単体テストを作成できます。Developers can create unit tests for the view model and the model, without using the view. ビュー モデルの単体テストでは、ビューで使用したのとまったく同じ機能を実行できます。The unit tests for the view model can exercise exactly the same functionality as used by the view.
  • アプリの UI は、全体を XAML でビューが実装されていること、コードを変更することがなく再設計することができます。The app UI can be redesigned without touching the code, provided that the view is implemented entirely in XAML. そのため、ビューの新しいバージョンは、既存のビュー モデルを使用する必要があります。Therefore, a new version of the view should work with the existing view model.
  • 設計者や開発者で作業できるとは独立して、同時に、コンポーネント開発プロセス中に。Designers and developers can work independently and concurrently on their components during the development process. デザイナーは、開発者は、ビュー モデルとモデルのコンポーネントで作業できるときに、ビューに専念できます。Designers can focus on the view, while developers can work on the view model and model components.

正しいクラスにアプリのコードをファクタリングする方法を理解するうえで、クラスの相互作用を理解することで、MVVM を効果的に使用するキーがあります。The key to using MVVM effectively lies in understanding how to factor app code into the correct classes, and in understanding how the classes interact. 次のセクションでは、MVVM パターンのクラスのそれぞれの役割について説明します。The following sections discuss the responsibilities of each of the classes in the MVVM pattern.

表示View

ビューは、構造、レイアウト、およびユーザーが画面に表示の外観を定義する責任を負います。The view is responsible for defining the structure, layout, and appearance of what the user sees on screen. 理想的には、それぞれのビューは、限られた分離コードを含むビジネス ロジックを含まない、XAML で定義されます。Ideally, each view is defined in XAML, with a limited code-behind that does not contain business logic. ただし、場合によっては、分離コードがアニメーションなど、XAML で表現するは困難を視覚的な動作を実装する UI ロジックを含めることがあります。However, in some cases, the code-behind might contain UI logic that implements visual behavior that is difficult to express in XAML, such as animations.

Xamarin.Forms アプリケーションのビューは通常、 Page -派生または ContentView -クラスを派生します。In a Xamarin.Forms application, a view is typically a Page-derived or ContentView-derived class. ただし、ビューの視覚的に表示されているオブジェクトを表すために使用する UI 要素を指定するデータ テンプレートでは、表すこともできます。However, views can also be represented by a data template, which specifies the UI elements to be used to visually represent an object when it's displayed. ビューとして、データ テンプレートでは、任意コード分離を持たないし、特定のビュー モデルの種類にバインドしています。A data template as a view does not have any code-behind, and is designed to bind to a specific view model type.

ヒント

有効にして、分離コードで UI 要素を無効化しないでください。Avoid enabling and disabling UI elements in the code-behind. モデルの表示が、ビューの表示するかどうか、コマンドは、使用、または保留中の操作を示す値などのいくつかの側面に影響する論理状態の変更を定義する責任を負いますであることを確認します。Ensure that view models are responsible for defining logical state changes that affect some aspects of the view's display, such as whether a command is available, or an indication that an operation is pending. そのため、有効にし、モデルのプロパティではなく有効にして、分離コードで無効にすることを表示するバインドによって UI 要素を無効にします。Therefore, enable and disable UI elements by binding to view model properties, rather than enabling and disabling them in code-behind.

ボタンのクリックしてなど、対話は、ビューへの応答にビュー モデルでコードを実行または項目の選択のいくつかのオプションがあります。There are several options for executing code on the view model in response to interactions on the view, such as a button click or item selection. コントロールは、コマンド、コントロールをサポートしている場合Commandプロパティがデータにバインドできます、ICommandビュー モデルのプロパティ。If a control supports commands, the control's Command property can be data-bound to an ICommand property on the view model. コントロールのコマンドが呼び出されたときに、ビュー モデル内のコードが実行されます。When the control's command is invoked, the code in the view model will be executed. コマンドだけでなく動作は、ビュー内のオブジェクトに接続できるし、呼び出されるコマンドまたはイベントが発生するをリッスンできます。In addition to commands, behaviors can be attached to an object in the view and can listen for either a command to be invoked or event to be raised. 応答して、動作は呼び出すことができますし、ICommandビュー モデルまたはモデルの表示のメソッドにします。In response, the behavior can then invoke an ICommand on the view model or a method on the view model.

ViewModelViewModel

ビュー モデルでは、プロパティやコマンド、ビューは、データ バインドを実装し、ビューに変更通知イベントを通じて状態変更を通知します。The view model implements properties and commands to which the view can data bind to, and notifies the view of any state changes through change notification events. プロパティとビュー モデルを提供するコマンドは、UI によって提供される機能を定義しますが、ビューは、その機能が表示される方法を決定します。The properties and commands that the view model provides define the functionality to be offered by the UI, but the view determines how that functionality is to be displayed.

ヒント

UI の非同期操作の応答性を維持します。Keep the UI responsive with asynchronous operations. モバイル アプリでは、ユーザーのパフォーマンスに対する認識を向上させるためにブロック解除、UI スレッドを保持する必要があります。Mobile apps should keep the UI thread unblocked to improve the user's perception of performance. したがって、ビュー モデルで I/O 操作の非同期メソッドを使用して、プロパティの変更のビューに非同期的に通知するイベントを発生させます。Therefore, in the view model, use asynchronous methods for I/O operations and raise events to asynchronously notify views of property changes.

ビュー モデルは、ビューのやり取りに必要なすべてのモデル クラスを調整するためも担当します。The view model is also responsible for coordinating the view's interactions with any model classes that are required. 通常はビュー モデルとモデル クラス間の一対多リレーションシップです。There's typically a one-to-many relationship between the view model and the model classes. ビュー モデルは、それらに直接データをバインドできるようにすること、ビュー内のコントロール ビューに直接モデル クラスを公開することができます。The view model might choose to expose model classes directly to the view so that controls in the view can data bind directly to them. この場合、モデル クラスは、データ バインディングをサポートし、通知イベントを変更するよう設計する必要があります。In this case, the model classes will need to be designed to support data binding and change notification events.

各ビュー モデルは、ビューを簡単に利用できる形式でのモデルからデータを提供します。Each view model provides data from a model in a form that the view can easily consume. これを行うことがあります、ビュー モデルがデータ変換を実行します。To accomplish this, the view model sometimes performs data conversion. ビューにバインドできるプロパティを提供するためは、ビュー モデルでこのデータの変換を配置することをお勧めします。Placing this data conversion in the view model is a good idea because it provides properties that the view can bind to. たとえば、ビュー モデルはビューで表示を容易にできるように 2 つのプロパティの値を組み合わせることがあります。For example, the view model might combine the values of two properties to make it easier for display by the view.

ヒント

変換レイヤーのデータ変換を一元化します。Centralize data conversions in a conversion layer. ビュー モデルとビューの間に位置する別のデータ変換レイヤーとしてコンバーターを使用することもできます。It's also possible to use converters as a separate data conversion layer that sits between the view model and the view. ときにできます必要に応じて、たとえば、データは、ビュー モデルを提供しない特殊な書式設定が必要です。This can be necessary, for example, when data requires special formatting that the view model doesn't provide.

ビュー モデル、ビューとの双方向データ バインドに参加するためには、そのプロパティを上げる必要があります、PropertyChangedイベント。In order for the view model to participate in two-way data binding with the view, its properties must raise the PropertyChanged event. ビュー モデルが実装することによってこの要件を満たす、INotifyPropertyChangedインターフェイス、および発生、PropertyChangedプロパティが変更されたときにイベント。View models satisfy this requirement by implementing the INotifyPropertyChanged interface, and raising the PropertyChanged event when a property is changed.

コレクションの場合、ビューに適したObservableCollection<T>提供されます。For collections, the view-friendly ObservableCollection<T> is provided. このコレクションには、コレクション変更通知、軽減を実装することから、開発者が実装して、INotifyCollectionChangedコレクション上のインターフェイス。This collection implements collection changed notification, relieving the developer from having to implement the INotifyCollectionChanged interface on collections.

モデルModel

モデル クラスは、アプリのデータをカプセル化する非ビジュアル クラスです。Model classes are non-visual classes that encapsulate the app's data. そのため、モデルは、通常、ビジネスおよび検証ロジックとデータ モデルが含まれるアプリのドメイン モデルを表すとして考えることができます。Therefore, the model can be thought of as representing the app's domain model, which usually includes a data model along with business and validation logic. モデル オブジェクトの例には、データ転送オブジェクト (Dto)、Plain Old CLR Object (Poco)、および生成されたエンティティおよびプロキシ オブジェクトが含まれます。Examples of model objects include data transfer objects (DTOs), Plain Old CLR Objects (POCOs), and generated entity and proxy objects.

モデル クラスは通常、サービスまたはデータ アクセスとキャッシュをカプセル化するリポジトリと組み合わせて使用されます。Model classes are typically used in conjunction with services or repositories that encapsulate data access and caching.

ビュー モデルをビューに接続します。Connecting View Models to Views

ビュー モデルは、Xamarin.Forms のデータ バインディング機能を使用して、ビューに接続できます。View models can be connected to views by using the data-binding capabilities of Xamarin.Forms. ビューを構築し、モデルの表示し、実行時に関連付けるに使用できる多くの方法はあります。There are many approaches that can be used to construct views and view models and associate them at runtime. これらの方法は、ビューの最初の構成とビュー モデルの最初の構成と呼ばれる 2 つのカテゴリに分類されます。These approaches fall into two categories, known as view first composition, and view model first composition. ビューの最初の構成とモデルの最初の合成 基本設定と複雑さの問題は、ビューの選択。Choosing between view first composition and view model first composition is an issue of preference and complexity. ただし、すべての方法は、その BindingContext プロパティに割り当てられているビュー モデルがビューには同じという目的を共有します。However, all approaches share the same aim, which is for the view to have a view model assigned to its BindingContext property.

ビュー最初コンポジション、アプリは概念的にはビューに依存しているビュー モデルに接続するので構成されます。With view first composition the app is conceptually composed of views that connect to the view models they depend on. このアプローチの主な利点ことですがの疎結合を作成する簡単な単体テストが容易なアプリのビュー モデルはビュー自体への依存を持たないためです。The primary benefit of this approach is that it makes it easy to construct loosely coupled, unit testable apps because the view models have no dependence on the views themselves. アプリの構造を理解するには、クラスを作成し、関連付けられている方法を理解するコードが実行を追跡する必要がなく、その視覚的な構造を次に簡単です。It's also easy to understand the structure of the app by following its visual structure, rather than having to track code execution to understand how classes are created and associated. さらに、ビューの最初の構築は複雑で、プラットフォームとのずれは、ビュー モデルの最初の構成をナビゲーションが発生したときにページの構築を担当する Xamarin.Forms ナビゲーション システムを配置します。In addition, view first construction aligns with the Xamarin.Forms navigation system that's responsible for constructing pages when navigation occurs, which makes a view model first composition complex and misaligned with the platform.

ビュー モデルの最初の合成、アプリは概念的にはで構成されますビュー モデル、ビュー モデルのビューを検索するために縛られるサービスと。With view model first composition the app is conceptually composed of view models, with a service being responsible for locating the view for a view model. ビュー モデルの最初の構成は、ビューを作成できますが抽象、アプリの UI 以外の論理構造に重点を置くようにするため、一部の開発者にとってより自然なと考えています。View model first composition feels more natural to some developers, since the view creation can be abstracted away, allowing them to focus on the logical non-UI structure of the app. さらに、他のビュー モデルによって作成されるモデルの表示を許可します。In addition, it allows view models to be created by other view models. ただし、この方法は複雑な多くの場合、アプリのさまざまな部分の作成し、関連付けられている方法を理解するが困難になることができます。However, this approach is often complex and it can become difficult to understand how the various parts of the app are created and associated.

ヒント

ビュー モデルとビューを独立して保持します。Keep view models and views independent. データ ソースのプロパティをビューのバインドで、対応するビュー モデル、ビューのプリンシパルの依存関係があります。The binding of views to a property in a data source should be the view's principal dependency on its corresponding view model. など、ビューの種類を参照しない具体的には、 Button ListView 、ビュー モデルから。Specifically, don't reference view types, such as Button and ListView, from view models. ここで説明した原則に従うと、分離、スコープを制限することで、ソフトウェアの不具合が発生する可能性を軽減するためにビュー モデルをテストできます。By following the principles outlined here, view models can be tested in isolation, therefore reducing the likelihood of software defects by limiting scope.

次のセクションでは、ビュー モデルをビューに接続するための主な方法について説明します。The following sections discuss the main approaches to connecting view models to views.

宣言によって、ビュー モデルを作成します。Creating a View Model Declaratively

最も簡単な方法は、宣言的 XAML 内の対応するビュー モデルをインスタンス化するビュー用です。The simplest approach is for the view to declaratively instantiate its corresponding view model in XAML. ビューが作成されるときに、対応するビュー モデル オブジェクトに構築することもできます。When the view is constructed, the corresponding view model object will also be constructed. このアプローチは、次のコード例について説明します。This approach is demonstrated in the following code example:

<ContentPage ... xmlns:local="clr-namespace:eShop">  
    <ContentPage.BindingContext>  
        <local:LoginViewModel />  
    </ContentPage.BindingContext>  
    ...  
</ContentPage>

ときに、 ContentPage は、インスタンスを作成、LoginViewModelが自動的に構築され、ビューの設定 BindingContextです。When the ContentPage is created, an instance of the LoginViewModel is automatically constructed and set as the view's BindingContext.

この宣言型の構築と割り当てビューでビュー モデルの言うは単純ですが、ビュー モデル内の既定 (パラメーターのない) コンス トラクターが必要であるという欠点ことの利点があります。This declarative construction and assignment of the view model by the view has the advantage that it's simple, but has the disadvantage that it requires a default (parameter-less) constructor in the view model.

ビュー モデルをプログラムで作成します。Creating a View Model Programmatically

ビューに割り当てられているモデルの表示になる分離コード ファイルでコードを持つことができます、 BindingContext プロパティ。A view can have code in the code-behind file that results in the view model being assigned to its BindingContext property. 多くの場合、これは、ビューのコンス トラクターで次のコード例に示すように。This is often accomplished in the view's constructor, as shown in the following code example:

public LoginView()  
{  
    InitializeComponent();  
    BindingContext = new LoginViewModel(navigationService);  
}

プログラムによる構築と、ビューの分離コード内でビュー モデルの割り当てには、単純であるという利点があります。The programmatic construction and assignment of the view model within the view's code-behind has the advantage that it's simple. ただし、このアプローチの主な欠点は、ビューが必要な依存関係にビュー モデルを提供する必要があることにします。However, the main disadvantage of this approach is that the view needs to provide the view model with any required dependencies. 依存関係の注入コンテナーを使用して疎結合、ビューとビュー モデルを維持するために役立ちます。Using a dependency injection container can help to maintain loose coupling between the view and view model. 詳細については、次を参照してください。依存関係の注入します。For more information, see Dependency Injection.

データ テンプレートとして定義されているビューを作成します。Creating a View Defined as a Data Template

ビューは、データ テンプレートとして定義されているし、ビュー モデルの種類に関連付けられていることができます。A view can be defined as a data template and associated with a view model type. データ テンプレートは、リソースとして定義できますか、ビュー モデルを表示するコントロール内にインラインで定義されている可能性があります。Data templates can be defined as resources, or they can be defined inline within the control that will display the view model. コントロールのコンテンツは、ビュー モデルのインスタンスと、データ テンプレートを視覚的に表現するために使用します。The content of the control is the view model instance, and the data template is used to visually represent it. この手法をビュー モデルがインスタンス化される最初に、ビューの作成後に状況の例に示します。This technique is an example of a situation in which the view model is instantiated first, followed by the creation of the view.

ビュー モデルのロケーターを自動的にビュー モデルを作成します。Automatically Creating a View Model with a View Model Locator

ビュー モデルのロケーターは、ビュー モデルとビューへの関連付けのインスタンス化を管理するカスタム クラスです。A view model locator is a custom class that manages the instantiation of view models and their association to views. EShopOnContainers のモバイル アプリで、ViewModelLocatorクラスには、添付プロパティAutoWireViewModel、ビュー モデルをビューに関連付けるために使用されます。In the eShopOnContainers mobile app, the ViewModelLocator class has an attached property, AutoWireViewModel, that's used to associate view models with views. ビューの XAML では、この添付プロパティは次のコード例で示すように、ビュー モデルが、ビューに自動的に接続されているを示す場合は true に設定します。In the view's XAML, this attached property is set to true to indicate that the view model should be automatically connected to the view, as shown in the following code example:

viewModelBase:ViewModelLocator.AutoWireViewModel="true"

AutoWireViewModelプロパティは、バインド可能なプロパティを false に初期化して、その値が変更されたとき、OnAutoWireViewModelChangedイベント ハンドラーが呼び出されます。The AutoWireViewModel property is a bindable property that's initialized to false, and when its value changes the OnAutoWireViewModelChanged event handler is called. このメソッドは、ビューのビュー モデルを解決します。This method resolves the view model for the view. 次のコード例では、これを実現する方法を示します。The following code example shows how this is achieved:

private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)  
{  
    var view = bindable as Element;  
    if (view == null)  
    {  
        return;  
    }  

    var viewType = view.GetType();  
    var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");  
    var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;  
    var viewModelName = string.Format(  
        CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);  

    var viewModelType = Type.GetType(viewModelName);  
    if (viewModelType == null)  
    {  
        return;  
    }  
    var viewModel = _container.Resolve(viewModelType);  
    view.BindingContext = viewModel;  
}

OnAutoWireViewModelChangedメソッドは規約ベースのアプローチを使用して、ビュー モデルを解決しようとしています。The OnAutoWireViewModelChanged method attempts to resolve the view model using a convention-based approach. この規則には、ことが前提とします。This convention assumes that:

  • ビュー モデルは、ビューの種類と同じアセンブリでです。View models are in the same assembly as view types.
  • ビューは、します。ビューの子名前空間。Views are in a .Views child namespace.
  • ビュー モデルは、します。Viewmodel の子名前空間。View models are in a .ViewModels child namespace.
  • ビュー モデルの名前は、ビューの名前と対応して、"ViewModel"で終わります。View model names correspond with view names and end with "ViewModel".

最後に、OnAutoWireViewModelChangedメソッドのセット、 BindingContext 解決済みのビュー モデルの種類にビューの種類の。Finally, the OnAutoWireViewModelChanged method sets the BindingContext of the view type to the resolved view model type. ビュー モデルの型を解決する方法の詳細については、次を参照してください。解決します。For more information about resolving the view model type, see Resolution.

このアプローチでは、アプリは、ビュー モデルとビューへの接続のインスタンス化を担当する 1 つのクラスであるという利点があります。This approach has the advantage that an app has a single class that is responsible for the instantiation of view models and their connection to views.

ヒント

置換の容易にするためには、ビュー モデルのロケーターを使用します。Use a view model locator for ease of substitution. ビュー モデル ロケーターこともできます、依存関係の代替実装のための置換のポイントとしてなどの単体テストまたはデザイン時のデータ。A view model locator can also be used as a point of substitution for alternate implementations of dependencies, such as for unit testing or design time data.

モデルまたはモデルを表示する、基になる変更に応じて更新ビューUpdating Views in Response to Changes in the Underlying View Model or Model

すべてのビュー モデルとビューにアクセス可能であるモデル クラスを実装する必要があります、INotifyPropertyChangedインターフェイス。All view model and model classes that are accessible to a view should implement the INotifyPropertyChanged interface. 基になるプロパティの値が変更されたときに、ビューでデータ バインド コントロールに変更通知を提供するクラスは、ビュー モデルまたはモデルのクラスでこのインターフェイスを実装できます。Implementing this interface in a view model or model class allows the class to provide change notifications to any data-bound controls in the view when the underlying property value changes.

次の要件を満たすことによってプロパティの変更通知の正しい使用のアプリを設計する必要があります。Apps should be architected for the correct use of property change notification, by meeting the following requirements:

  • 常に発生させる、PropertyChangedイベント パブリック プロパティの値が変更された場合。Always raising a PropertyChanged event if a public property's value changes. 発生すると想定しないで、PropertyChangedイベントは、サポート技術情報の XAML バインドが発生したため無視できます。Do not assume that raising the PropertyChanged event can be ignored because of knowledge of how XAML binding occurs.
  • 常に発生させる、PropertyChanged計算される値をビュー内の他のプロパティを使用するプロパティのいずれかのイベント モデルまたはモデル。Always raising a PropertyChanged event for any calculated properties whose values are used by other properties in the view model or model.
  • 常に発生させる、PropertyChangedにより、プロパティの変更、または安全な状態にあるオブジェクトを認識すると、メソッドの最後のイベント。Always raising the PropertyChanged event at the end of the method that makes a property change, or when the object is known to be in a safe state. イベントを発生させるイベントのハンドラーを同期的に呼び出すことによって、操作を中断します。Raising the event interrupts the operation by invoking the event's handlers synchronously. 操作の途中でこのような場合、安全でない、部分的に更新された状態である場合に、コールバック関数へのオブジェクトを公開可能性があります。If this happens in the middle of an operation, it might expose the object to callback functions when it is in an unsafe, partially updated state. カスケード型の変更によってトリガーされる可能性がさらに、PropertyChangedイベント。In addition, it's possible for cascading changes to be triggered by PropertyChanged events. カスケード変更は、カスケード型の変更を安全に実行する前に完了する更新プログラムを一般に必要です。Cascading changes generally require updates to be complete before the cascading change is safe to execute.
  • 発生させることはありません、PropertyChangedイベント、プロパティが変更されない場合。Never raising a PropertyChanged event if the property does not change. つまり、発生する前に新旧の値を比較する必要があります、PropertyChangedイベント。This means that you must compare the old and new values before raising the PropertyChanged event.
  • 発生させることはありません、PropertyChangedプロパティを初期化する場合、ビュー モデルのコンス トラクターの中にイベント。Never raising the PropertyChanged event during a view model's constructor if you are initializing a property. ビューのデータ バインド コントロールはこの時点で変更通知の受信をサブスクライブしてがいません。Data-bound controls in the view will not have subscribed to receive change notifications at this point.
  • 1 つ以上の発生しないPropertyChangedイベント クラスのパブリック メソッドの 1 つの同期呼び出し内で同じプロパティ名の引数とします。Never raising more than one PropertyChanged event with the same property name argument within a single synchronous invocation of a public method of a class. たとえば、NumberOfItemsプロパティのバッキング ストアとして、_numberOfItemsフィールド、メソッドの増分の場合_numberOfItems50 回ループの実行中にのみ発生させるプロパティの変更通知で、NumberOfItemsプロパティを 1 回、すべての作業が完了します。For example, given a NumberOfItems property whose backing store is the _numberOfItems field, if a method increments _numberOfItems fifty times during the execution of a loop, it should only raise property change notification on the NumberOfItems property once, after all the work is complete. 非同期メソッドは、生成、PropertyChangedイベントを非同期継続チェーンの各同期セグメントに指定されたプロパティ名。For asynchronous methods, raise the PropertyChanged event for a given property name in each synchronous segment of an asynchronous continuation chain.

EShopOnContainers のモバイル アプリでは、ExtendedBindableObjectクラスを提供する変更通知、次のコード例に表示されます。The eShopOnContainers mobile app uses the ExtendedBindableObject class to provide change notifications, which is shown in the following code example:

public abstract class ExtendedBindableObject : BindableObject  
{  
    public void RaisePropertyChanged<T>(Expression<Func<T>> property)  
    {  
        var name = GetMemberInfo(property).Name;  
        OnPropertyChanged(name);  
    }  

    private MemberInfo GetMemberInfo(Expression expression)  
    {  
        ...  
    }  
}

Xamarin.Form の BindableObject クラスが実装する、INotifyPropertyChangedインターフェイスを備え、 OnPropertyChanged メソッド。Xamarin.Form's BindableObject class implements the INotifyPropertyChanged interface, and provides an OnPropertyChanged method. ExtendedBindableObjectクラスには、RaisePropertyChangedプロパティを呼び出すメソッドを選択し、変更の通知をによって提供される機能を使用、BindableObjectクラス。The ExtendedBindableObject class provides the RaisePropertyChanged method to invoke property change notification, and in doing so uses the functionality provided by the BindableObject class.

EShopOnContainers のモバイル アプリでの各ビュー モデル クラスから派生、ViewModelBaseから派生するクラスをExtendedBindableObjectクラス。Each view model class in the eShopOnContainers mobile app derives from the ViewModelBase class, which in turn derives from the ExtendedBindableObject class. そのため、各ビュー モデル クラスを使用して、RaisePropertyChangedメソッドで、ExtendedBindableObjectプロパティの変更通知を提供するクラス。Therefore, each view model class uses the RaisePropertyChanged method in the ExtendedBindableObject class to provide property change notification. 次のコード例では、eShopOnContainers のモバイル アプリがラムダ式を使用してプロパティの変更通知を起動する方法を示します。The following code example shows how the eShopOnContainers mobile app invokes property change notification by using a lambda expression:

public bool IsLogin  
{  
    get  
    {  
        return _isLogin;  
    }  
    set  
    {  
        _isLogin = value;  
        RaisePropertyChanged(() => IsLogin);  
    }  
}

ラムダ式を使用して、この方法でわずかなパフォーマンスの各呼び出しに対して評価されるラムダ式があるため、コストの含まれることに注意してください。Note that using a lambda expression in this way involves a small performance cost because the lambda expression has to be evaluated for each call. パフォーマンス コストが小さいと、アプリが通常影響しない、ですが、多くの変更通知がある場合に、コストが発生することができます。Although the performance cost is small and would not normally impact an app, the costs can accrue when there are many change notifications. ただし、このアプローチの利点は、コンパイル時の型の安全性とプロパティを変更するとリファクタリングのサポートが提供されることです。However, the benefit of this approach is that it provides compile-time type safety and refactoring support when renaming properties.

コマンドとビヘイビアーを使用する UI 操作UI Interaction using Commands and Behaviors

モバイル アプリでのアクションは通常、分離コード ファイルで、イベント ハンドラーを作成して実装できるボタンのクリックなどのユーザー アクションへの応答で呼び出されます。In mobile apps, actions are typically invoked in response to a user action, such as a button click, that can be implemented by creating an event handler in the code-behind file. ただし、MVVM パターンでビュー モデルでは、アクションを実装する責任があるし、分離コードで配置するコードを避ける必要があります。However, in the MVVM pattern, the responsibility for implementing the action lies with the view model, and placing code in the code-behind should be avoided.

コマンドは、UI コントロールにバインドできるアクションを表す便利な方法を提供します。Commands provide a convenient way to represent actions that can be bound to controls in the UI. アクションを実装するコードをカプセル化され、ビューでのビジュアル表現から切り離すことに保つために役立ちます。They encapsulate the code that implements the action, and help to keep it decoupled from its visual representation in the view. Xamarin.Forms には、コマンドを宣言によって接続されているコントロールが含まれていて、ユーザーがコントロールと対話するときにこれらのコントロールがコマンドを呼び出します。Xamarin.Forms includes controls that can be declaratively connected to a command, and these controls will invoke the command when the user interacts with the control.

動作では、コントロール宣言によって、コマンドに接続することもできます。Behaviors also allow controls to be declaratively connected to a command. ただし、動作は、さまざまなコントロールによって発生したイベントに関連付けられているアクションを呼び出すために使用できます。However, behaviors can be used to invoke an action that's associated with a range of events raised by a control. そのため、動作の多くに対処コマンドが有効なコントロールと同じシナリオよりはるかに柔軟性と制御を提供しながらします。Therefore, behaviors address many of the same scenarios as command-enabled controls, while providing a greater degree of flexibility and control. さらに、動作は、コマンドとの対話を具体的にはデザインされていないコントロールにコマンド オブジェクトやメソッドを関連付けることも使用できます。In addition, behaviors can also be used to associate command objects or methods with controls that were not specifically designed to interact with commands.

コマンドの実装Implementing Commands

ビュー モデルは通常を実装するオブジェクトのインスタンスであるビューで、バインドのコマンド プロパティを公開、ICommandインターフェイス。View models typically expose command properties, for binding from the view, that are object instances that implement the ICommand interface. Xamarin.Forms コントロールの多くを提供して、Commandプロパティは、データにバインドされて、ICommandビュー モデルが提供するオブジェクト。A number of Xamarin.Forms controls provide a Command property, which can be data bound to an ICommand object provided by the view model. ICommandインターフェイスを定義、Execute操作自体をカプセル化するメソッド、CanExecuteメソッドをおよびコマンドを呼び出すことができるかどうかを示すCanExecuteChangedかどうかの変更に影響するが発生したときに発生するイベントコマンドを実行する必要があります。The ICommand interface defines an Execute method, which encapsulates the operation itself, a CanExecute method, which indicates whether the command can be invoked, and a CanExecuteChanged event that occurs when changes occur that affect whether the command should execute. Command Command<T> 、Xamarin.Forms が提供するクラスの実装、ICommandインターフェイス、場所Tへの引数の型は、ExecuteCanExecuteします。The Command and Command<T> classes, provided by Xamarin.Forms, implement the ICommand interface, where T is the type of the arguments to Execute and CanExecute.

ビュー モデル内で必要があります型のオブジェクト Command または Command<T> 型のビュー モデル内の各パブリック プロパティのICommandします。Within a view model, there should be an object of type Command or Command<T> for each public property in the view model of type ICommand. CommandまたはCommand<T>コンス トラクターが必要です、Actionコールバック オブジェクトを呼び出したときに、ICommand.Executeメソッドが呼び出されます。The Command or Command<T> constructor requires an Action callback object that's called when the ICommand.Execute method is invoked. CanExecuteは省略可能なコンス トラクターのパラメーターをFuncを返す、boolします。The CanExecute method is an optional constructor parameter, and is a Func that returns a bool.

次のコードは、 Command へのデリゲートを指定することで、登録コマンドを表すインスタンスが構築された、Registerモデルのメソッドを表示します。The following code shows how a Command instance, which represents a register command, is constructed by specifying a delegate to the Register view model method:

public ICommand RegisterCommand => new Command(Register);

参照を返すプロパティを通じてビューに、コマンドが公開されている、ICommandします。The command is exposed to the view through a property that returns a reference to an ICommand. ときに、Executeでメソッドが呼び出される、 Command オブジェクトで指定されたデリゲートを使用して、ビュー モデル内のメソッドの呼び出しを転送するだけ、Commandコンス トラクター。When the Execute method is called on the Command object, it simply forwards the call to the method in the view model via the delegate that was specified in the Command constructor.

使用して、コマンドによって、非同期メソッドを呼び出すことができます、asyncawaitキーワードのコマンドを指定するときにExecuteを委任します。An asynchronous method can be invoked by a command by using the async and await keywords when specifying the command's Execute delegate. これは、コールバックがあることを示します、Task待機する必要があります。This indicates that the callback is a Task and should be awaited. たとえば、次のコードはどのように、 Command へのデリゲートを指定することによって、サインイン コマンドを表すインスタンスが構築された、SignInAsyncモデルのメソッドを表示。For example, the following code shows how a Command instance, which represents a sign-in command, is constructed by specifying a delegate to the SignInAsync view model method:

public ICommand SignInCommand => new Command(async () => await SignInAsync());

パラメーターに渡すことができます、ExecuteCanExecuteアクションを使用して、 Command<T> コマンドをインスタンス化するクラス。Parameters can be passed to the Execute and CanExecute actions by using the Command<T> class to instantiate the command. たとえば、次のコードはどのように、Command<T>インスタンスがあることを示す使用、NavigateAsyncメソッドに必要な型の引数string:For example, the following code shows how a Command<T> instance is used to indicate that the NavigateAsync method will require an argument of type string:

public ICommand NavigateCommand => new Command<string>(NavigateAsync);

両方で、 Command Command<T> クラス、デリゲート、CanExecute各コンス トラクターのメソッドは省略可能です。In both the Command and Command<T> classes, the delegate to the CanExecute method in each constructor is optional. デリゲートが指定されていない場合、Command戻りますtrueCanExecuteします。If a delegate isn't specified, the Command will return true for CanExecute. ただし、ビュー モデルは、コマンドの変化を示すことができますCanExecuteステータスを呼び出すことによって、ChangeCanExecuteメソッドをCommandオブジェクト。However, the view model can indicate a change in the command's CanExecute status by calling the ChangeCanExecute method on the Command object. これにより、CanExecuteChangedイベントが発生します。This causes the CanExecuteChanged event to be raised. コマンドにバインドされているコントロールが UI には、データ バインドされたコマンドの可用性を反映するように、有効な状態を更新し、されます。Any controls in the UI that are bound to the command will then update their enabled status to reflect the availability of the data-bound command.

ビューからのコマンドを呼び出すInvoking Commands from a View

次のコード例に示す方法、 Grid で、LoginViewにバインドする、RegisterCommandで、LoginViewModelクラスを使用して、 TapGestureRecognizer インスタンス。The following code example shows how a Grid in the LoginView binds to the RegisterCommand in the LoginViewModel class by using a TapGestureRecognizer instance:

<Grid Grid.Column="1" HorizontalOptions="Center">  
    <Label Text="REGISTER" TextColor="Gray"/>  
    <Grid.GestureRecognizers>  
        <TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />  
    </Grid.GestureRecognizers>  
</Grid>

コマンドのパラメーター定義することも必要に応じてを使用して、 CommandParameter プロパティ。A command parameter can also be optionally defined using the CommandParameter property. 必要な引数の型がで指定された、ExecuteCanExecuteメソッドを対象します。The type of the expected argument is specified in the Execute and CanExecute target methods. TapGestureRecognizer ユーザーが接続されているコントロール ターゲット コマンドを自動的に呼び出します。The TapGestureRecognizer will automatically invoke the target command when the user interacts with the attached control. コマンド パラメーターを指定されている場合は、コマンドの引数として渡されますExecuteを委任します。The command parameter, if provided, will be passed as the argument to the command's Execute delegate.

動作を実装します。Implementing Behaviors

動作は、それらをサブクラス化しなくても、UI コントロールに追加する機能を使用できます。Behaviors allow functionality to be added to UI controls without having to subclass them. 代わりに、その機能はビヘイビアー クラスで実装され、それがコントロール自体の一部であるかのようにコントロールにアタッチされます。Instead, the functionality is implemented in a behavior class and attached to the control as if it was part of the control itself. 動作を簡潔に、コントロールにアタッチできパッケージには、複数のビューまたはアプリの間で再利用方法で、コントロールの API を直接操作するため、分離コードとして記述する必要が通常のコードを実装できます。Behaviors enable you to implement code that you would normally have to write as code-behind, because it directly interacts with the API of the control, in such a way that it can be concisely attached to the control, and packaged for reuse across more than one view or app. MVVM のコンテキストでは、動作は、コマンドにコントロールを接続するために役立つアプローチです。In the context of MVVM, behaviors are a useful approach for connecting controls to commands.

添付プロパティをコントロールに関連付けられている動作と呼ばれる、動作をアタッチします。A behavior that's attached to a control through attached properties is known as an attached behavior. 動作は、それがアタッチされている機能を制御する、またはビューのビジュアル ツリー内の他のコントロールに追加する要素の公開されている API を使用できます。The behavior can then use the exposed API of the element to which it is attached to add functionality to that control, or other controls, in the visual tree of the view. EShopOnContainers のモバイル アプリが含まれる、LineColorBehaviorクラスは、これは、接続されている動作です。The eShopOnContainers mobile app contains the LineColorBehavior class, which is an attached behavior. この動作の詳細については、次を参照してください。検証エラーを表示するします。For more information about this behavior, see Displaying Validation Errors.

Xamarin.Forms の動作はから派生したクラス、 Behavior または Behavior<T> クラス、T動作を適用するコントロールの種類です。A Xamarin.Forms behavior is a class that derives from the Behavior or Behavior<T> class, where Tis the type of the control to which the behavior should apply. これらのクラスを提供OnAttachedToOnDetachingFromメソッドで、動作にアタッチされているし、コントロールからデタッチされたときに実行するロジックを提供するオーバーライドする必要があります。These classes provide OnAttachedTo and OnDetachingFrom methods, which should be overridden to provide logic that will be executed when the behavior is attached to and detached from controls.

EShopOnContainers のモバイル アプリで、BindableBehavior<T>クラスから派生、 Behavior<T> クラス。In the eShopOnContainers mobile app, the BindableBehavior<T> class derives from the Behavior<T> class. 目的、BindableBehavior<T>を必要とする Xamarin.Forms の動作の基本クラスを提供するクラスは、 BindingContext のアタッチされたコントロールに設定する動作。The purpose of the BindableBehavior<T> class is to provide a base class for Xamarin.Forms behaviors that require the BindingContext of the behavior to be set to the attached control.

BindableBehavior<T>クラスには、オーバーライド可能なOnAttachedToを設定するメソッド、 BindingContext の動作と、オーバーライド可能なOnDetachingFromをクリーンアップする方法、BindingContextします。The BindableBehavior<T> class provides an overridable OnAttachedTo method that sets the BindingContext of the behavior, and an overridable OnDetachingFrom method that cleans up the BindingContext. さらに、このクラスでは、アタッチされたコントロールへの参照が AssociatedObject プロパティに格納されます。In addition, the class stores a reference to the attached control in the AssociatedObject property.

EShopOnContainers のモバイル アプリでは、EventToCommandBehaviorクラスは、発生中のイベントに応答するコマンドを実行します。The eShopOnContainers mobile app includes an EventToCommandBehavior class, which executes a command in response to an event occurring. このクラスから派生、BindableBehavior<T>クラスの動作にバインドして実行できるように、ICommandで指定された、Commandプロパティを使用すると、動作します。This class derives from the BindableBehavior<T> class so that the behavior can bind to and execute an ICommand specified by a Command property when the behavior is consumed. 次に示すのは、EventToCommandBehavior クラスのコード例です。The following code example shows the EventToCommandBehavior class:

public class EventToCommandBehavior : BindableBehavior<View>  
{  
    ...  
    protected override void OnAttachedTo(View visualElement)  
    {  
        base.OnAttachedTo(visualElement);  

        var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray();  
        if (events.Any())  
        {  
            _eventInfo = events.FirstOrDefault(e => e.Name == EventName);  
            if (_eventInfo == null)  
                throw new ArgumentException(string.Format(  
                        "EventToCommand: Can't find any event named '{0}' on attached type",   
                        EventName));  

            AddEventHandler(_eventInfo, AssociatedObject, OnFired);  
        }  
    }  

    protected override void OnDetachingFrom(View view)  
    {  
        if (_handler != null)  
            _eventInfo.RemoveEventHandler(AssociatedObject, _handler);  

        base.OnDetachingFrom(view);  
    }  

    private void AddEventHandler(  
            EventInfo eventInfo, object item, Action<object, EventArgs> action)  
    {  
        ...  
    }  

    private void OnFired(object sender, EventArgs eventArgs)  
    {  
        ...  
    }  
}

OnAttachedToOnDetachingFromメソッドを使用して、登録し、で定義されているイベントのイベント ハンドラーを登録解除、EventNameプロパティ。The OnAttachedTo and OnDetachingFrom methods are used to register and deregister an event handler for the event defined in the EventName property. その後、イベントが発生したとき、OnFiredメソッドが呼び出されるコマンドを実行します。Then, when the event fires, the OnFired method is invoked, which executes the command.

使用する利点、EventToCommandBehaviorイベントが発生したときにコマンドを実行するにはコマンドがコマンドと対話するように設計でした。 コントロールに関連付けできます。The advantage of using the EventToCommandBehavior to execute a command when an event fires, is that commands can be associated with controls that weren't designed to interact with commands. さらに、このイベント処理コードに移動ビュー モデルでは、単体テストを行うことできます。In addition, this moves event-handling code to view models, where it can be unit tested.

ビューからの起動の動作Invoking Behaviors from a View

EventToCommandBehaviorはコマンドをコマンドでサポートされていないコントロールにアタッチするために特に便利です。The EventToCommandBehavior is particularly useful for attaching a command to a control that doesn't support commands. たとえば、ProfileViewを使用して、EventToCommandBehaviorを実行する、OrderDetailCommandときに、 ItemTapped でイベントが発生した、 ListView に示すように、ユーザーの注文が一覧表示次のコード。For example, the ProfileView uses the EventToCommandBehavior to execute the OrderDetailCommand when the ItemTapped event fires on the ListView that lists the user's orders, as shown in the following code:

<ListView>  
    <ListView.Behaviors>  
        <behaviors:EventToCommandBehavior             
            EventName="ItemTapped"  
            Command="{Binding OrderDetailCommand}"  
            EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />  
    </ListView.Behaviors>  
    ...  
</ListView>

実行時に、EventToCommandBehaviorとの対話に応答が、 ListViewします。At runtime, the EventToCommandBehavior will respond to interaction with the ListView. 項目を選択すると、 ListView ItemTapped が実行されるイベントは起動、OrderDetailCommandで、ProfileViewModelします。When an item is selected in the ListView, the ItemTapped event will fire, which will execute the OrderDetailCommand in the ProfileViewModel. 既定では、イベントのイベント引数は、コマンドに渡されます。By default, the event arguments for the event are passed to the command. このデータは、ソースとターゲット間で指定されたコンバーターによって渡される、変換、EventArgsConverter返すプロパティを Item ListViewから、 ItemTappedEventArgs.This data is converted as it's passed between source and target by the converter specified in the EventArgsConverter property, which returns the Item of the ListView from the ItemTappedEventArgs. そのため、OrderDetailCommandが実行される、選択したOrderは登録されているアクションをパラメーターとして渡されます。Therefore, when the OrderDetailCommand is executed, the selected Order is passed as a parameter to the registered Action.

動作の詳細については、次を参照してください。動作します。For more information about behaviors, see Behaviors.

まとめSummary

モデル-ビュー-ビューモデル (MVVM) パターンは、そのユーザー インターフェイス (UI) からアプリケーションのビジネスとプレゼンテーション ロジックを明確に分離するのに役立ちます。The Model-View-ViewModel (MVVM) pattern helps to cleanly separate the business and presentation logic of an application from its user interface (UI). アプリケーション ロジックと UI の明確な分離を維持し、開発のさまざまな問題に対処するのに役立つ、し、アプリケーションのテスト、保守、および進化を簡単にします。Maintaining a clean separation between application logic and the UI helps to address numerous development issues and can make an application easier to test, maintain, and evolve. コードの再利用機会も大幅に向上し、により、開発者とより UI デザイナーは、それぞれの部品のアプリを開発するときに簡単に共同作業します。It can also greatly improve code re-use opportunities and allows developers and UI designers to more easily collaborate when developing their respective parts of an app.

パターン、MVVM を使用して、アプリの UI と基になるプレゼンテーションとビジネス ロジックは 3 つの独立したクラスに分割されます。 UI と UI をカプセル化すると、ビュー ロジック。プレゼンテーション ロジックと状態をカプセル化するビュー モデルモデルでは、アプリのビジネス ロジックとデータをカプセル化します。Using the MVVM pattern, the UI of the app and the underlying presentation and business logic is separated into three separate classes: the view, which encapsulates the UI and UI logic; the view model, which encapsulates presentation logic and state; and the model, which encapsulates the app's business logic and data.