在装饰器中将来自 CustomControls 的事件与 Viewmodel 混合
Mixing events from CustomControls, in an adorner, with a Viewmodel
我一直在反复讨论以下两种从自定义控件处理事件的方法的优缺点。我的争论基本上围绕着在自定义(而非用户)控件中应该放置多少 "logic" 以及如何最好地将事件放入视图模型。
DataGridAnnotationControl "control" 驻留在我的数据网格的装饰器中。此处的目标是响应用户从自定义控件中显示的组合框中选择的项目。
第一个示例,示例 #1,在 DataGridAnnotationControl 中使用了一个非常标准的自定义事件
然后通过装饰器将其映射到目标 AppointmentEditor(视图模型)。我对此最大的抱怨是装饰者明显依赖于 (AppointmentEditor) 来实现正确的事件路由。
♦ Example #1:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
......
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseSelectionChanged();
}
public event Action SelectionChanged;
public void RaiseSelectionChanged()
{
SelectionChanged?.Invoke();
}
♦ Adorner DataGridAnnotationAdorner
public DataGridAnnotationAdorner(DataGrid adornedDataGrid)
: base(adornedDataGrid)
{
......
Control = new DataGridAnnotationControl();
this.SelectionChanged += ((AppointmentEditor)adornedDataGrid.DataContext).SelectionChanged; <--This requires a reference to Patient_Registration.Editors. THIS IS FORCING
A DEPENDENCY ON THE PATIENT_REGISTRATION PROJECT.
}
public event Action SelectionChanged
{
add { Control.SelectionChanged += value; }
remove { Control.SelectionChanged -= value; }
}
♦ AppointmentEditor
public void SelectionChanged()
{
throw new NotImplementedException();
}
示例 #2 此示例使用非常标准的事件路由到主窗口,从该主窗口使用事件聚合器将 AppointmentEditor 作为事件的订阅者。我在这里最大的抱怨是需要所有额外的代码(超过示例 #1)。此外,爬上可视化树只是为了跳入旨在支持此自定义控件的一个视图模型似乎是一个复杂的因素。
Example #2:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
.....
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaisePatientNameSelectionChangedEvent();
}
public static readonly RoutedEvent PatientNameSelectionChangedEvent = EventManager.RegisterRoutedEvent(
"PatientNameSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DataGridAnnotationControl));
// Provide CLR accessors for the event
public event RoutedEventHandler PatientNameSelectionChanged
{
add { AddHandler(PatientNameSelectionChangedEvent, value); }
remove { RemoveHandler(PatientNameSelectionChangedEvent, value); }
}
protected virtual void RaisePatientNameSelectionChangedEvent()
{
RoutedEventArgs args = new RoutedEventArgs(DataGridAnnotationControl.PatientNameSelectionChangedEvent);
RaiseEvent(args);
}
♦ public partial class MainWindow : Window
{
public MainWindow(IMainWindowViewModel mainWindowViewModel, EventAggregator eventAggregator)
{
InitializeComponent();
EventAggregator = eventAggregator;
DataContext = mainWindowViewModel;
....
AddHandler(DataGridAnnotationControl.PatientNameSelectionChangedEvent, new RoutedEventHandler(PatientNameSelectionChangedHandler));
}
private void PatientNameSelectionChangedHandler(object sender, RoutedEventArgs e)
{
EventAggregator.PublishEvent( new PatientNameSelected() );
}
}
♦ public class AppointmentEditor : INotifyPropertyChanged, ISubscriber<PatientNameSelected>
public void OnEventHandlerAsync(PatientNameSelected e)
{
throw new NotImplementedException();
}
有更好的方法吗?
TIA
理想情况下,您的自定义控件应该不知道您的视图模型。
使用 MVVM,您可以将自定义控件中的事件绑定到视图模型中的命令。
我编写并维护了大量其他团队使用的自定义控件。我总是公开一个与任何事件关联的 ICommand
,以使 MVVM 用户能够以最简单的方式轻松使用我的控件。
我一直在反复讨论以下两种从自定义控件处理事件的方法的优缺点。我的争论基本上围绕着在自定义(而非用户)控件中应该放置多少 "logic" 以及如何最好地将事件放入视图模型。
DataGridAnnotationControl "control" 驻留在我的数据网格的装饰器中。此处的目标是响应用户从自定义控件中显示的组合框中选择的项目。
第一个示例,示例 #1,在 DataGridAnnotationControl 中使用了一个非常标准的自定义事件 然后通过装饰器将其映射到目标 AppointmentEditor(视图模型)。我对此最大的抱怨是装饰者明显依赖于 (AppointmentEditor) 来实现正确的事件路由。
♦ Example #1:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
......
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseSelectionChanged();
}
public event Action SelectionChanged;
public void RaiseSelectionChanged()
{
SelectionChanged?.Invoke();
}
♦ Adorner DataGridAnnotationAdorner
public DataGridAnnotationAdorner(DataGrid adornedDataGrid)
: base(adornedDataGrid)
{
......
Control = new DataGridAnnotationControl();
this.SelectionChanged += ((AppointmentEditor)adornedDataGrid.DataContext).SelectionChanged; <--This requires a reference to Patient_Registration.Editors. THIS IS FORCING
A DEPENDENCY ON THE PATIENT_REGISTRATION PROJECT.
}
public event Action SelectionChanged
{
add { Control.SelectionChanged += value; }
remove { Control.SelectionChanged -= value; }
}
♦ AppointmentEditor
public void SelectionChanged()
{
throw new NotImplementedException();
}
示例 #2 此示例使用非常标准的事件路由到主窗口,从该主窗口使用事件聚合器将 AppointmentEditor 作为事件的订阅者。我在这里最大的抱怨是需要所有额外的代码(超过示例 #1)。此外,爬上可视化树只是为了跳入旨在支持此自定义控件的一个视图模型似乎是一个复杂的因素。
Example #2:
♦ CustomControl DataGridAnnotationControl
public override void OnApplyTemplate()
{
.....
_cboLastName.SelectionChanged += _cboLastName_SelectionChanged;
}
private void _cboLastName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaisePatientNameSelectionChangedEvent();
}
public static readonly RoutedEvent PatientNameSelectionChangedEvent = EventManager.RegisterRoutedEvent(
"PatientNameSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DataGridAnnotationControl));
// Provide CLR accessors for the event
public event RoutedEventHandler PatientNameSelectionChanged
{
add { AddHandler(PatientNameSelectionChangedEvent, value); }
remove { RemoveHandler(PatientNameSelectionChangedEvent, value); }
}
protected virtual void RaisePatientNameSelectionChangedEvent()
{
RoutedEventArgs args = new RoutedEventArgs(DataGridAnnotationControl.PatientNameSelectionChangedEvent);
RaiseEvent(args);
}
♦ public partial class MainWindow : Window
{
public MainWindow(IMainWindowViewModel mainWindowViewModel, EventAggregator eventAggregator)
{
InitializeComponent();
EventAggregator = eventAggregator;
DataContext = mainWindowViewModel;
....
AddHandler(DataGridAnnotationControl.PatientNameSelectionChangedEvent, new RoutedEventHandler(PatientNameSelectionChangedHandler));
}
private void PatientNameSelectionChangedHandler(object sender, RoutedEventArgs e)
{
EventAggregator.PublishEvent( new PatientNameSelected() );
}
}
♦ public class AppointmentEditor : INotifyPropertyChanged, ISubscriber<PatientNameSelected>
public void OnEventHandlerAsync(PatientNameSelected e)
{
throw new NotImplementedException();
}
有更好的方法吗?
TIA
理想情况下,您的自定义控件应该不知道您的视图模型。
使用 MVVM,您可以将自定义控件中的事件绑定到视图模型中的命令。
我编写并维护了大量其他团队使用的自定义控件。我总是公开一个与任何事件关联的 ICommand
,以使 MVVM 用户能够以最简单的方式轻松使用我的控件。