2016-05-19 12 views
0

私は、多くのアプリケーションで使用されるユーザーコントロールを作成することを考えていました。MVVMパターンを使用したいと思います。MVVMでユーザーコントロールを使用することはできますか?

例えば、私は1日でクリックすると、この日に行う必要があるタスクをユーザーコントロールが検索するカレンダー付きのユーザーコントロールを持っています。

だから私は、ユーザーコントロールには、その日のタスクを検索するためのユーザーコントロール内のロジックのビューモデルがあると考えていました。したがって、ユーザーコントロールのカレンダーのselectedDateプロパティをユーザーコントロールのビューモデルのプロパティにバインドします。値が変更されると、ビューモデルはその日のタスクを検索できます。

また、私は主なアプリケーションは、選択された日付が変更されたときに別のことをしなければならないので、メインアプリケーション、カレンダーのselectedDateに通知することを望みます。だから、メインビューモデルのプロパティを自分のユーザーコントロールで作成した依存プロパティにバインドしようとしましたが、ユーザーコントロールのプロパティでは、ユーザーコントロールのビューモデルのプロパティにバインドする方法日が変更されたときにメインビューモデルに通知されません。

私はコードの背後でこれを行う方法を知っていますが、ユーザーコントロールに独自のロジックがあり、MVVMパターンに従いたいと思いますので、MVVMで実行できるかどうかを知りたいと思います。そうでなければ、私のアプリケーションに多くのユーザコントロールがあると、メインアプリケーションだけがMVVMパターンと残りのコードを使用するので、私のアプリケーションのコードが後ろに残っている可能性があり、これを避けたいと思います。

要約すると、カレンダーで日付を変更すると、ユーザーコントロールがビューモデルに通知し、アプリケーションのメインビューのバインディングプロパティに通知する方法を知りたいと思います。

ありがとうございました。

EDIT最後に

I持って、私はdependecyプロパティと依存関係を更新し、ユーザーコントロールの背後にあるコードをユーザーコントロールのビューモデルの変化を伝えるためのイベントでやってみたかったものを手に入れますプロパティはメインビューの変更を通知します。メインビューの

XAML:コードは、以下である

<Window x:Class="UserControlMvvm.MainView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:UserControlMvvm" 
     xmlns:vm="clr-namespace:UserControlMvvm" 
     mc:Ignorable="d" 
     Name="_mainView" 
     Title="MainView" Height="350" Width="525"> 

    <Window.DataContext> 
     <vm:MainViewModel /> 
    </Window.DataContext> 

    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="102*"/> 
      <RowDefinition Height="217*"/> 
     </Grid.RowDefinitions> 

     <local:ucMyUserControlView HorizontalAlignment="Center" Margin="0,0,0,0" Grid.Row="1" VerticalAlignment="Center" 
            SelectedDate="{Binding ElementName=_mainView, Path=DataContext.SelectedDateInUserControl, Mode=TwoWay}"/> 

     <TextBox x:Name="txtSelectedDateInUserControl" Text="{Binding SelectedDateInUserControl}" HorizontalAlignment="Left" Height="23" Margin="10,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/> 

     <Label x:Name="lblSelectedDate" Content="SelectedDate in UserControl" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top"/> 

     <TextBox x:Name="txtSelectedDateToUserControl" HorizontalAlignment="Right" Height="23" Margin="0,35,5,0" TextWrapping="Wrap" Text="{Binding SelectedDateToUserControl, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/> 

     <Label x:Name="lblSeelectedDateToUserControl" Content="Change date on user control" HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Top"/> 

    </Grid> 
</Window> 

メインビューモデルのコード:

using System; 


namespace UserControlMvvm 
{ 
    class MainViewModel : ViewModelBase 
    { 
     #region properties 
     private DateTime? _selectedDateInUserControl; 
     public DateTime? SelectedDateInUserControl 
     { 
      get { return _selectedDateInUserControl; } 
      set 
      { 
       if(_selectedDateInUserControl != value) 
       { 
        SetProperty(ref _selectedDateInUserControl, value); 
        selectedDateInUserControlChanged(); 
       } 
      } 
     } 

     private string _selectedDateInUserControlText; 
     public string SelectedDateInUserControlText 
     { 
      get { return _selectedDateInUserControlText; } 
      set 
      { 
       if (_selectedDateInUserControlText != value) 
       { 
        SetProperty(ref _selectedDateInUserControlText, value); 
       } 
      } 
     } 

     private string _selectedDateToUserControl; 
     public string SelectedDateToUserControl 
     { 
      get { return _selectedDateToUserControl; } 
      set 
      { 
       if (_selectedDateToUserControl != value) 
       { 
        SetProperty(ref _selectedDateToUserControl, value); 
        DateTime miDateParsed; 
        DateTime.TryParse(value, out miDateParsed); 
        SelectedDateInUserControl = miDateParsed; 
       } 
      } 
     } 
     #endregion properties 



     #region methods 
     /// <summary> 
     /// This method is used to do all the tasks needed when the selectedDate in the user control is changed. 
     /// </summary> 
     private void selectedDateInUserControlChanged() 
     { 
      try 
      { 
       //here the code that the main view model has to do when the selected date is changed in the user control. 
      } 
      catch 
      { 
       throw; 
      } 
     }//selectedDateInUserControlChanged 
     #endregion methods 
    } 
} 

ユーザーコントロールのXAML:

<UserControl x:Class="UserControlMvvm.ucMyUserControlView" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:UserControlMvvm" 
      mc:Ignorable="d" 
      Name="_ucMyUserControl" 
      Width="Auto" Height="Auto"> 
    <Grid> 
     <Calendar HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" 
        SelectedDate="{Binding ElementName=_ucMyUserControl,Path=DataContext.SelectedDate, Mode=TwoWay}"/> 

    </Grid> 
</UserControl> 

依存関係のプロパティを宣言し、変更をビューmoに通知するために、ユーザーコントロールのコードビーンデル。ロジックはビューモデル内にあります。

using System.Windows.Controls; 
using System; 
using System.Windows; 


namespace UserControlMvvm 
{ 
    /// <summary> 
    /// Interaction logic for ucMyUserControl.xaml 
    /// </summary> 
    public partial class ucMyUserControlView : UserControl 
    { 
     ucMyUserControlViewModel _viewModel; 

     public ucMyUserControlView() 
     { 
      InitializeComponent(); 

      //The view model is needed to be instantiate here, not in the XAML, because if not, you will get a null reference exception 
      //because you try to access to a property when the view model is not still instantiate. 
      _viewModel = new ucMyUserControlViewModel(); 
      DataContext = _viewModel; 

      //Events 
      _viewModel.SelectedDateChangedEvent += selectedDateChanged; 
     } 




     #region dependency properties 
     //This are the properties that the main view will have available when will use the user control, so dependency properties are the 
     //communication way between the main view and the user control. 
     //So here you have to declare all the properties that you want to expose to outside, to the main view. 

     public static readonly DependencyProperty SelectedDateProperty = 
      DependencyProperty.Register("SelectedDate", typeof(DateTime?), 
       typeof(ucMyUserControlView), new PropertyMetadata(null, selectedDateChanged)); 
     public DateTime? SelectedDate 
     { 
      get 
      { 
       return (DateTime?)GetValue(SelectedDateProperty); 
      } 
      set 
      { 
       //This is the way in which the user control notify to the main view that the selected date is changed. 
       SetValue(SelectedDateProperty, value); 
      } 
     } 

     private static void selectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      //This is the way in which the code behind notify to the view model that the main view has changed by the main view. 
      ((ucMyUserControlView)d)._viewModel.SelectedDate = e.NewValue as DateTime?; 
     } 
     #endregion dependency properties 



     #region methods to receive notifications from the view model 
     //These are the methods that are subcribed to the events of the view model, to know when a property has changed in the view 
     //model and be able to notify to the main view. 
     private void selectedDateChanged(DateTime? paramSelectedDate) 
     { 
      try 
      { 
       //This update the dependency property, so this notify to the main main that the selected date has been changed in the 
       //user control. 
       SetValue(SelectedDateProperty, paramSelectedDate); 
      } 
      catch 
      { 
       throw; 
      } 
     }//selectedChanged 
     #endregion methods to receive notificactions from the view model 
    } 
} 

ユーザーコントロールのビューモデル:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace UserControlMvvm 
{ 
    class ucMyUserControlViewModel : ViewModelBase 
    { 
     #region events 
     //The events are user to notify changes from the properties in this view model to the code behind of the user control, so 
     //later the user control can notify the changes from the code behind to the main view. 
     //This is because the user control only can notify changes to the main view from the dependency properties and the dependency properties 
     //are declared in the code behind. So to communicate changes from the view model to the code behind it is needed the use of an event. 

     //So the changes are notify in this way: 
     //view model --> code behind --> main view 

     public delegate void SelectedDateChangedEventHandler(DateTime? paramSelectedDate); 
     public event SelectedDateChangedEventHandler SelectedDateChangedEvent; 


     private void OnSelectedDateChanged(DateTime? paramTipoSeleccionado) 
     { 
      try 
      { 
       //Here notify to the code behind of the user control that the selectedDate is changed. 
       SelectedDateChangedEvent?.Invoke(paramTipoSeleccionado); 
      } 
      catch 
      { 
       throw; 
      } 
     }//OnSelectedDateChanged 
     #endregion events 



     #region properties 
     private DateTime? _selectedDate; 
     public DateTime? SelectedDate 
     { 
      get { return _selectedDate; } 
      set 
      { 
       if(_selectedDate != value) 
       { 
        SetProperty(ref _selectedDate, value); 
        selectedDateChanged(); 
        OnSelectedDateChanged(SelectedDate); 
       } 
      } 
     } 
     #endregion properties 



     #region methods 
     private void selectedDateChanged() 
     { 
      try 
      { 
       //Here the code that the user control has to execute when the selectedDate is changed. 
      }//try 
      catch 
      { 
       throw; 
      } 
     } 
     #endregion methods 
    } 
} 

最後に、INotifyPropertyChangedのを実装するクラスは、それはあなたが好む任意の実装することができますが、おそらく誰かのために興味深いものになることができます。

/* 
* Class that implements the INotifyPropertyChanged that it is used by all view models to 
* notifiy changes in their properties to the view. 
*/ 

using System.Runtime.CompilerServices; 
using System.ComponentModel; 

namespace UserControlMvvm 
{ 
    public class ViewModelBase : INotifyPropertyChanged 
    { 
     protected virtual void SetProperty<T>(ref T member, T val, 
      [CallerMemberName] string propertyName = null) 
     { 
      if (object.Equals(member, val)) return; 

      member = val; 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
    } 
} 

このソリューションでは、カレンダーの日付を変更すると、メインビューの最初のテキストボックスは更新されますが、2番目のテキストボックスは更新されませんユーザーコントロールにバインドされます。

メインビューの最初のテキストボックスで日付を変更すると、ユーザーコントロールのカレンダーで選択した日付も更新されますが、2番目のテキストボックスは更新されません。

2番目のテキストボックスの日付を変更すると、ビューモデルのselectedItemInUserControlプロパティを更新するため、カレンダーに変更され、このプロパティはカレンダーの変更をユーザーコントロールに通知します。

このソリューションでは、メインビューと通信するための依存関係プロパティを公開するだけで、ユーザーコントロールの内部にMVVMパターンを作成できます。

+0

英語は私の母国語ではないので、あなたはカレンダーの「SelectedDate」をメインビューモデルにバインドし、アプリケーションでビューモデルの変更。したがって、App-> UserControl-> ViewModelを接続するのではなく、AppとUserControlの両方をViewModelに接続する必要があります。 – XAMlMAX

+0

**大きな質問です。 + 1 ** 私はいつもそういうことが可能かどうか疑問に思っています。ビューモデルを有する再利用可能なコントロールである。しかし、私はそれを達成することは可能ではないと思う。 再利用可能なコントロールの個別のビューモデルを、特定のコントロールのみの場合とは別の場所で別々の場所に配置することもできます。 通常のシナリオでは、コードをコード内に記述し、** Dependency Properties **と**これらのプロパティが変更されたときのアクションを使用して再利用性を達成します。 – ViVi

+0

@ Vishakh369:コードビハインドと依存関係プロパティで簡単に行うことができますが、主なアプリケーションからユーザーコントロール、ユーザーコントロールからメインアプリケーションまで、両方の方法で変更を通知できるシンプルなユーザーコントロールはほとんどありません。しかし、このユーザーコントロールには内部ロジックがありません。しかし、内部ロジックが必要な場合、メインアプリケーションでこのコントロールの多くを使用すると、MVVMをメインアプリケーションに使用することになりますが、ユーザーコントロールがアプリケーションの50%の場合はどうなりますか?それから私はMVVMを使って自分のアプリケーションの50%しか持っていないでしょう。そして、ユーザーコントロールに単体テストを行う方法は? –

答えて

1

はい。ナビゲーションシステムを使用するフレームワークを使用してView/ViewModels間を移動する場合は、これを適用してUserControl View/ViewModelを起動します。表示がWindowまたはUserControlの場合は問題ありません。

EDIT

コントロールのビューモデルの変更にプロパティは、それがメッセージを送信することができるときに、ビューモデルの間で情報を渡すために(最もMVVMフレームワークで再び利用可能)Messengerシステムを使用することも可能ですメインのViewModelにプロパティを変更するには

+0

私はあなたが**ÁlvaroGarcía**が尋ねたことを誤解したと思います。 – ViVi

関連する問題