2016-05-19 2 views
0

Я думал создать пользовательский элемент управления, который будет использоваться во многих приложениях, и я хотел бы использовать шаблон MVVM.Возможно ли иметь пользовательский контроль в MVVM?

Например, у меня есть пользовательский элемент управления с календарем, который, когда я нажимаю в течение дня, выполняет поиск пользователем задач, которые я должен выполнить в этот день.

Так что я думал, что пользовательский элемент управления имеет модель представления для логики внутри пользовательского элемента управления, то есть для поиска задач дня. Поэтому я привязываю свойство selectedDate к календарю в пользовательском элементе управления к образцу модели представления пользовательского элемента управления, поэтому, когда значение изменяется, модель представления может искать задачи дня.

Кроме того, я хочу, чтобы этот пользовательский элемент управления уведомлял основное приложение, выбранное в календаре, потому что основное приложение должно выполнять другие действия при изменении выбранной даты. Поэтому я попытался привязать свойство в моей основной модели представления к свойству зависимости, которое я создал в своем пользовательском элементе управления, но как в свойстве в пользовательском элементе управления связывается с свойством модели представления пользовательского элемента управления, модель основного вида не уведомляет о том, когда день изменился.

Я знаю, как это сделать в коде, но я хотел бы знать, можно ли это сделать в MVVM, потому что пользовательский элемент управления имеет свою собственную логику, и я хотел бы следовать шаблону MVVM. Если нет, когда у меня есть много пользовательских элементов управления в моем приложении, только основное приложение использует шаблон MVVM и остальную часть кода, поэтому я мог бы иметь hugh процентов моего приложения в коде позади, и я хотел бы избежать этого.

Таким образом, я хотел бы знать, как при изменении даты в календаре пользовательский элемент уведомляет о своей модели представления, а также уведомляет об этом свойство привязки в главном представлении моего приложения.

Спасибо.

EDIT

Наконец у меня есть получить то, что я хотел сделать с событиями, чтобы сообщить изменения в модели представления пользовательского элемента управления в коде позади контроля пользователя, который обновляет свойства зависимые пакеты и зависимость свойства сообщают об изменениях в главном представлении. Код следующим образом:

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 { }; 
    } 
} 

С помощью этого решения мы можем видеть, что если я изменяю дату в календаре, первое текстовое поле в главном представлении обновляется, но не второе текстовое поле, потому что это не привязан к пользовательскому элементу управления.

Если я изменил дату в первом текстовом поле на главном экране, также будет обновлен выбранный день в календаре пользовательского элемента управления, но не во втором текстовом поле.

Если я изменяю дату во втором текстовом поле, это изменяется в календаре beccause, я обновляю свойство selectedItemInUserControl модели представления, и это свойство уведомляет пользовательский элемент управления, который изменяется в календаре.

Таким образом, с этим решением у меня может быть MVVM-patter внутри пользовательского элемента управления, который просто выставляет свойства зависимостей для связи с основным видом.

+0

Из того, что я могу понять, это потому, что английский не является моим первым языком, вы можете просто привязать календарь «SelectedDate» к модели основного вида и вашему приложению, чтобы найти изменение в модели просмотра. Поэтому вместо подключения App-> UserControl-> ViewModel вы должны подключить приложение и UserControl к ViewModel. – XAMlMAX

+0

** Отличный вопрос. + 1 ** Я всегда задавался вопросом, возможно ли такое. т.е. средство многократного использования, имеющее модель обзора. Но я не думаю, что это возможно. Возможно, у вас могут быть отдельные модели просмотра для многоразового управления в разных местах, кроме как для конкретного элемента управления. В обычном сценарии мы записываем код в код позади и получаем возможность повторного использования с использованием ** Свойства зависимостей ** и действий при изменении этих ** свойств **. – ViVi

+0

@ Vishakh369: право, в коде позади и свойства зависимостей это легко сделать, у меня есть несколько простых пользовательских элементов управления, которые могут уведомлять изменения в обоих направлениях: от основного приложения до пользовательского элемента управления и от пользовательского управления до основного приложения. Но этот пользовательский элемент управления не имеет внутренней логики. Но если вам нужна внутренняя логика, если основное приложение использует многие из этих элементов управления, в конце у вас есть основное приложение, использующее MVVM, но что произойдет, если пользовательские элементы управления составляют 50% приложения? Тогда у меня будет только 50% моего приложения с MVVM. И как сделать модульные тесты для моих пользователей? –

ответ

1

Да. Если вы используете фреймворк, который использует навигационную систему для перемещения между View/ViewModels, вы можете приспособить это, чтобы запустить свой View/ViewModel UserControl. Не имеет значения, является ли вид Window или UserControl.

EDIT

Также можно использовать Messenger системы (снова доступна в большинстве структур MVVM) для передачи информации между видом моделями, поэтому, когда свойство в ViewModel изменении элемента управления, он может отправить сообщение к главному ViewModel, чтобы изменить его свойства

+0

Я думаю, вы неправильно поняли, что ** Álvaro García ** спросил. – ViVi

 Смежные вопросы

  • Нет связанных вопросов^_^