我有一个复杂的Plot RenderingControl,我已将其放入View中。处理MVVM模式的缩放的理想方法是什么?我希望用户能够通过在绘图上单击并拖动来进行缩放。
我看到的一种方法是获取Plot控件的MouseMove,MouseUp,MouseDown事件,并将它们连接到PlotViewModel中的命令。现在,为了响应命令,ViewModel可以更新它的ZoomLevel属性,该属性可以绑定到视图,并使视图放大。当用户点击并拖动时,我还想显示一个矩形,指示将要放置的区域被放大。在PlotViewModel中保留AnnotationViewModel以进行缩放预览是否有意义?
另一种方法是在视图中处理所有内容,而根本不涉及ViewModel。
我看到的主要区别是捕获ViewModel中的行为将使该行为比在View中更可重用。虽然我感觉底层的Plot控件和生成的View足够复杂,但无论如何都不会有太多重复使用的机会。你觉得怎么样?
答案 0 :(得分:0)
我认为有几种方法可以解决您的问题。 HighCore对,当他说Zoom适用于View
时,所以建议将它放在View
边。但是有其他选择,我们在下面考虑它们。不幸的是,我没有处理Plot RenderingControl
,所以我将描述一个基于抽象的解决方案,独立于控件。
在这种情况下,我会尝试通过附加行为识别控件的所有可能工作,它非常适合MVVM模式,并且可以在Blend中使用。
工作示例
在View
中,定义了控件并附加了行为,如下所示:
<RenderingControl Name="MyPlotControl"
AttachedBehaviors:ZoomBehavior.IsStart="True" ... />
在代码隐藏中:
public static class ZoomBehavior
{
public static readonly DependencyProperty IsStartProperty;
public static void SetIsStart(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsStartProperty, value);
}
public static bool GetIsStart(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsStartProperty);
}
static ZoomBehavior()
{
IsStartMoveProperty = DependencyProperty.RegisterAttached("IsStart",
typeof(bool),
typeof(ZoomBehavior),
new UIPropertyMetadata(false, IsStart));
}
private static void IsStart(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
UIElement uiElement = sender as UIElement;
if (uiElement != null)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
uiElement.MouseDown += new MouseButtonEventHandler(ObjectMouseDown);
uiElement.MouseMove += new MouseEventHandler(ObjectMouseMove);
uiElement.MouseUp += new MouseButtonEventHandler(ObjectMouseUp);
}
}
}
// Below is event handlers
}
一旦为属性IsStart
设置为 true ,就会触发PropertyChanged处理程序,并为包含基本逻辑的事件设置处理程序。
为了在您的行为中传输其他数据,请注册其他依赖项属性,例如:
<RenderingControl Name="MyPlotControl"
AttachedBehaviors:ZoomBehavior.IsStart="True"
AttachedBehaviors:ZoomBehavior.ZoomValue="50" />
在代码隐藏中:
// ... Here registered property
public static void SetZoomValue(DependencyObject DepObject, int value)
{
DepObject.SetValue(ZoomValueProperty, value);
}
public static int GetZoomValue(DependencyObject DepObject)
{
return (int)DepObject.GetValue(ZoomValueProperty);
}
// ... Somewhere in handler
int value = GetZoomValue(plotControl);
要检索有关行为的数据,我使用单例模式。此模式表示对象的全局静态访问点,并且必须保证该类的单个实例的存在。
使用此模式的示例(取自行为,使用View
中的时间显示):
public class TimeBehavior : INotifyPropertyChanged
{
// Global instance
private static TimeBehavior _instance = new TimeBehavior();
public static TimeBehavior Instance
{
get
{
return _instance;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private string _currentTime = DateTime.Now.ToString("HH:mm");
public string CurrentTime
{
get
{
return _currentTime;
}
set
{
if (_currentTime != value)
{
_currentTime = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CurrentTime"));
}
}
}
}
private string _currentDayString = ReturnDayString();
public string CurrentDayString
{
get
{
return _currentDayString;
}
set
{
if (_currentDayString != value)
{
_currentDayString = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CurrentDayString"));
}
}
}
}
private string _currentMonthAndDayNumber = ReturnMonthAndDayNumber();
public string CurrentMonthAndDayNumber
{
get
{
return _currentMonthAndDayNumber;
}
set
{
if (_currentMonthAndDayNumber != value)
{
_currentMonthAndDayNumber = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CurrentMonthAndDayNumber"));
}
}
}
}
public static readonly DependencyProperty IsTimerStartProperty;
public static void SetIsTimerStart(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsTimerStartProperty, value);
}
public static bool GetIsTimerStart(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsTimerStartProperty);
}
static void OnIsTimerStartPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
static TimeBehavior()
{
IsTimerStartProperty = DependencyProperty.RegisterAttached("IsTimerStart",
typeof(bool),
typeof(TimeBehavior),
new PropertyMetadata(new PropertyChangedCallback(OnIsTimerStartPropertyChanged)));
}
private static void timer_Tick(object sender, EventArgs e)
{
_instance.CurrentTime = DateTime.Now.ToString("HH:mm");
_instance.CurrentDayString = ReturnDayString();
_instance.CurrentMonthAndDayNumber = ReturnMonthAndDayNumber();
}
}
访问View
中的数据:
<TextBlock Name="WidgetTimeTextBlock"
Text="{Binding Path=CurrentTime,
Source={x:Static Member=AttachedBehaviors:TimeBehavior.Instance}}" />
通过界面在视图中工作
这种方式的一点是,我们通过View
通过ViewModel
调用方法,该方法执行所有工作,而他不了解View
。这是通过界面的操作和这里描述的井完成的:
使用ServiceLocator
ServiceLocator允许您在ViewModel
中工作,而不违反MVVM的原则。您有一个RegisterService方法,您可以在其中注册要提供的服务实例,以及用于获取所需服务的GetService方法。
可在此处找到更多信息: