WCF RIAサービス。 Model-View-ViewModel(MVVM)パターンの紹介。 パート4

WCF RIAサービス。 スタート。 パート1

WCF RIAサービス。 データを受信します。 パート2

WCF RIAサービス。 データ更新。 パート3

WCF RIAサービス。 Model-View-ViewModel(MVVM)パターンの紹介。 パート4



エントリー



Model-View-ViewModel(MVVM)パターンは、SilverlightおよびWPFで疎結合アプリケーションを作成するために使用されます。 このコースでは、このアプローチの基本を検討するのではなく、3つのレッスンで作成したプロジェクトにそれを実装する方法を学習します。 ワイルドに入らない場合、MVVMはMVCやMVPなどのプログラミングパターンの論理的な開発であり、データバインディング、コマンド、およびSilverlightとWPFが提供するすべての機能を完全にサポートする代替手段の1つです。 ビューモデル(ViewModel)は、ビュー(View)に必要なすべてのリソースを提供する役割を果たします。 つまり、ビューがデータを簡単にバインドし、コマンドを作成できるように、必要なすべてのプロパティが提供されます。一方、ビューモデルには、アプリケーションに必要なすべてのロジックが存在し、機能します。 構造的には、「DataContext」セットは、データがバインドされているビューモデルのインスタンスと同じです。



このアプローチの主な利点は、ビューモデルからビューがほぼ完全に独立していることです。これは、プログラマとデザイナーが各パーツを独立して開発することで大まかに表現できます。 また、この分離の快適な結果は、ロジックがUI(ユーザーインターフェイス)と完全に無関係であるため、モジュールテキストの作成が容易であることです(ユニットテスト)。



そして、伝統によると、私たちのレッスンへの入り口は、トレーニングの前の段階で作成されたプロジェクトです。



ステップ1:ビューモデルの作成





最初から、ページのコンパニオンファイル(page_name.xaml.cs)のすべてのロジックを整理しました。 したがって、論理と表現の強力な一貫性を生成しました。 そして、これはほとんどの場合、非常に悪いトーンです。 これらの概念を区別する時が来ました。 ビューモデルのすべてのコードを取り出します。 「TasksViewModel」という名前のクライアントプロジェクトに新しいクラスを作成します。 さらに、ビューを分析し、ビューモデルで作成する必要があるプロパティを決定するとよいでしょう。 次の図に、作成する必要のあるフォームを示します。 最初に、DatePickersを使用して日付選択ボタンを上の2つのTextBoxに追加し、DataGridから未使用の列を削除し、残りの列をわずかに調整して、より魅力的で読みやすい外観にします。



上記のUIを簡単に調べた後、ビューモデルに追加する必要がある6つのプロパティを簡単に特定できます。それぞれ、開始日と終了日の2つのDateTimeプロパティ、3つのボタンコマンド、およびタスクコレクションです。 次のようになります。



public class TasksViewModel { public DateTime LowerSearchDate { get; set; } public DateTime UpperSearchDate { get; set; } public ICommand SearchByDateCommand { get; set; } public ICommand AddTaskCommand { get; set; } public ICommand SaveChangesCommand { get; set; } public IEnumerable<Task> Tasks { get; set; } }
      
      





また、各プロパティに対してINotifyPropertyChangedインターフェイスを実装する必要があります。これは、変更に伴いUIを追跡および変更する必要があります。 コードでは、次のようになります。



 public class TasksViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; DateTime _LowerSearchDate; public DateTime LowerSearchDate { get { return _LowerSearchDate; } set { if (value != _LowerSearchDate) { _LowerSearchDate = value; PropertyChanged(this, new PropertyChangedEventArgs("LowerSearchDate")); } } } DateTime _UpperSearchDate; public DateTime UpperSearchDate { get { return _UpperSearchDate; } set { if (value != _UpperSearchDate) { _UpperSearchDate = value; PropertyChanged(this, new PropertyChangedEventArgs("UpperSearchDate ")); } } }
      
      







ICommandプロパティを使用するには、適切な実装を追加する必要があります。 RelayCommandとも呼ばれる最も単純な実装を使用します。 しかし、実際のプロジェクトでは、Prismを提供するDelegateCommandを使用することをお勧めします。



RelayCommandを作成するには、「RelayCommand」という名前の新しいクラスをクライアントプロジェクトに追加する必要があります。



 namespace TaskManager { public class RelayCommand<T> : ICommand { Action<T> _TargetExecuteMethod; Func<T, bool> _TargetCanExecuteMethod; public RelayCommand(Action<T> executeMethod) { _TargetExecuteMethod = executeMethod; } public RelayCommand(Action<T> executeMethod, Func<T,bool> canExecuteMethod) { _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } #region ICommand Members bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { T tparm = (T)parameter; return _TargetCanExecuteMethod(tparm); } if (_TargetExecuteMethod != null) { return true; } return false; } public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod((T)parameter); } } #endregion } }
      
      





ビューモデルはビューに必要なすべてのデータを提供し、DomainContextはデータが変更されたときにタイムリーに更新する責任があるため、明らかな解決策は、RIAサービスでMVVMパターンを使用する場合、ビューモデル内でDomainContextを使用するだけです



 TasksDomainContext _Context = new TasksDomainContext(); public TasksViewModel() { SearchByDateCommand = new RelayCommand<object>(OnSearchByDate); AddTaskCommand = new RelayCommand<object>(OnAddTask); SaveChangesCommand = new RelayCommand<object>(OnSaveChanges); Tasks = _Context.Tasks; if (!DesignerProperties.IsInDesignTool) { _Context.Load(_Context.GetTasksQuery()); } }
      
      





上記のコードでは、ビューモデル内にTasksDomainContextを作成し、コンストラクターでコマンドを初期化して必要なメソッドにバインドします。 ビューモデルのTasksプロパティには、Tasksエンティティのコレクションへの参照が含まれています。これは、ドメインコンテキストによって提供され、「load」または「SubmitChanges」メソッドが呼び出されバックグラウンドでエンティティを更新します。 「If」ブロックでチェックされるコンストラクターの「DesignerProperties.IsInDesignTool」プロパティに注意してください。これにより、デザイナーから「Load」メソッドを呼び出すことができなくなります。エラーが発生します。



次のステップは、メソッドをMainPage.xaml.csからビューモデルに転送することです。 それがロジックの転送です。 MainPage.xaml.csを完全にクリアします。これは内部にのみ残ります。



 namespace TasksManager { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); }
      
      







検索メソッドは、3回目のレッスンで説明した遅延実行を使用するようになりました。 クライアントはドメインサービスに何を検索し、どのようにリクエストを形成するかを伝えることができるため、GetTasksByStartDateメソッドは不要になりました。 また、新しいタスクの作成は、追加されたデータを編集できる別のポップアップウィンドウで行われます。 ビューモデルから直接ポップアップウィンドウを呼び出す例は、あまり良い例ではないことに注意することが重要です。 ただし、本格的なMVVMとその利点をすべて考慮することはこれらのレッスンの主な目標ではないため、これはコードを簡素化するために行われました。 MVVMのより正確な定義と使用は、Prismによって提供されます。



 private void OnSearchByDate(object param) { _Context.Tasks.Clear(); EntityQuery<Task> query = _Context.GetTasksQuery(); LoadOperation<Task> loadOp = _Context.Load(query.Where(t => t.StartDate >= LowerSearchDate && t.StartDate <= UpperSearchDate)); } private void OnAddTask(object param) { //      //     MVVM  ,   Prism 4 AddTaskView popup = new AddTaskView(); popup.DataContext = new Task(); popup.Closed += delegate { if (popup.DialogResult == true) { Task newTask = popup.DataContext as Task; if (newTask != null) _Context.Tasks.Add(newTask); } }; popup.Show(); } private void OnSaveChanges(object param) { _Context.SubmitChanges(); }
      
      







次に、ポップアップを追加します。 クライアントプロジェクトで、「Add」-「Create Element」-「Silverlight Page」という名前で、「AddTaskView」という名前と次のコンテンツがあります。



 <controls:ChildWindow x:Class="TaskManager.AddTaskView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" Width="361" Height="287" Title="Add Task" mc:Ignorable="d" xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:TaskManager.Web" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <Grid x:Name="LayoutRoot" Margin="2"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" /> <Button x:Name="SaveButton" Content="Save" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" Click="OKButton_Click" /> <Grid DataContext="{Binding}" HorizontalAlignment="Left" Margin="12,12,0,0" Name="grid1" VerticalAlignment="Top" Width="315"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="237" /> <ColumnDefinition Width="4*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="100" /> </Grid.RowDefinitions> <sdk:Label Content="Description:" Grid.Column="0" Grid.Row="3" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="3" Height="91" HorizontalAlignment="Left" Margin="3,3,0,6" Name="descriptionTextBox" Text="{Binding Path=Description, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="221" /> <sdk:Label Content="End Date:" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <controls:DatePicker Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="3,3,0,3" Name="endDateDatePicker" SelectedDate="{Binding Path=EndDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Start Date:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <controls:DatePicker Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3,3,0,3" Name="startDateDatePicker" SelectedDate="{Binding Path=StartDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" /> <sdk:Label Content="Task Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" /> <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3,3,0,3" Name="taskNameTextBox" Text="{Binding Path=TaskName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" VerticalAlignment="Center" Width="221" /> </Grid> </Grid> </controls:ChildWindow>
      
      





それだけです この段階のビューモデルは完全に準備され、機能しています。 UIの最終処理に進みましょう。



ステップ2:ビューとビューモデルをリンクする



最初の部分から始めて、ドラッグアンドドロップ中に自動的に生成されたDomainDataSourceを使用します。 MVVMを使用する場合、XAMLでの使用はMVVMパーティショニングの概念に違反するため、DomainDataSourceを取り除く必要があります。



DataContextを追加します。 ビューモデルの名前を指定します。 そして、作成されたプロパティに基づいてバインディングを追加します。



 <UserControl x:Class="TaskManager.MainPage" ...> <UserControl.DataContext> <local:TasksViewModel/> </UserControl.DataContext> <Grid x:Name="LayoutRoot" Background="White"> <sdk:DataGrid ItemsSource="{Binding Tasks}" .../> <Button Command="{Binding SearchByDateCommand}" .../> <Button Command="{Binding AddTaskCommand}" ... /> <Button Command="{Binding SaveChangesCommand}" ... /> <sdk:DatePicker SelectedDate="{Binding LowerSearchDate}" ... /> <sdk:DatePicker SelectedDate="{Binding UpperSearchDate}" ... /> </Grid> </UserControl>
      
      



このチュートリアルのビデオ







ソースコード



githubで



All Articles