Я думал создать пользовательский элемент управления, который будет использоваться во многих приложениях, и я хотел бы использовать шаблон 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 внутри пользовательского элемента управления, который просто выставляет свойства зависимостей для связи с основным видом.
Из того, что я могу понять, это потому, что английский не является моим первым языком, вы можете просто привязать календарь «SelectedDate» к модели основного вида и вашему приложению, чтобы найти изменение в модели просмотра. Поэтому вместо подключения App-> UserControl-> ViewModel вы должны подключить приложение и UserControl к ViewModel. – XAMlMAX
** Отличный вопрос. + 1 ** Я всегда задавался вопросом, возможно ли такое. т.е. средство многократного использования, имеющее модель обзора. Но я не думаю, что это возможно. Возможно, у вас могут быть отдельные модели просмотра для многоразового управления в разных местах, кроме как для конкретного элемента управления. В обычном сценарии мы записываем код в код позади и получаем возможность повторного использования с использованием ** Свойства зависимостей ** и действий при изменении этих ** свойств **. – ViVi
@ Vishakh369: право, в коде позади и свойства зависимостей это легко сделать, у меня есть несколько простых пользовательских элементов управления, которые могут уведомлять изменения в обоих направлениях: от основного приложения до пользовательского элемента управления и от пользовательского управления до основного приложения. Но этот пользовательский элемент управления не имеет внутренней логики. Но если вам нужна внутренняя логика, если основное приложение использует многие из этих элементов управления, в конце у вас есть основное приложение, использующее MVVM, но что произойдет, если пользовательские элементы управления составляют 50% приложения? Тогда у меня будет только 50% моего приложения с MVVM. И как сделать модульные тесты для моих пользователей? –