C# 通过MVVM模式缩放绘图?
我有一个复杂的Plot Rendering控件,我已将其放置到视图中。对于MVVM模式,处理缩放的理想方式是什么?我希望用户能够通过单击并拖动绘图来缩放 我看到的一种方法是使用Plot控件的MouseMove、MouseUp、MouseDown事件,并将它们连接到PlotViewModel中的命令。现在,作为对命令的响应,ViewModel可以更新其ZoomLevel属性,该属性可以绑定到视图,并使视图放大。当用户单击并拖动时,我还想显示一个矩形,指示要缩放的区域。在PlotViewModel中保留注释ViewModel用于缩放预览有意义吗 另一种方法是在视图中处理这一切,而根本不涉及ViewModelC# 通过MVVM模式缩放绘图?,c#,wpf,mvvm,C#,Wpf,Mvvm,我有一个复杂的Plot Rendering控件,我已将其放置到视图中。对于MVVM模式,处理缩放的理想方式是什么?我希望用户能够通过单击并拖动绘图来缩放 我看到的一种方法是使用Plot控件的MouseMove、MouseUp、MouseDown事件,并将它们连接到PlotViewModel中的命令。现在,作为对命令的响应,ViewModel可以更新其ZoomLevel属性,该属性可以绑定到视图,并使视图放大。当用户单击并拖动时,我还想显示一个矩形,指示要缩放的区域。在PlotViewModel中
我看到的主要区别是,在ViewModel中捕获行为将使该行为比在视图中更可重用。虽然我觉得底层的绘图控件和生成的视图足够复杂,因此无论如何都不会有太多的重复使用的机会。你觉得怎么样?我认为有几种方法可以解决你的问题。右上角,当他说缩放适用于
视图时,建议将其保留在视图
侧边。但是还有其他选择,我们考虑如下。不幸的是,我没有处理绘图渲染控件
,因此我将描述一个基于抽象的、独立于控件的解决方案
附加行为
在这种情况下,我会尝试通过附加的行为来识别所有可能的控件工作,它非常适合MVVM模式,并且可以在混合中使用
工作实例
在视图中
定义了控件并附加了一个行为,如下所示:
<RenderingControl Name="MyPlotControl"
AttachedBehaviors:ZoomBehavior.IsStart="True" ... />
一旦将propertyIsStart
设置为true,就会触发PropertyChanged处理程序,并为包含基本逻辑的事件设置处理程序
要在行为中传输其他数据,请注册其他依赖项属性,例如:
<RenderingControl Name="MyPlotControl"
AttachedBehaviors:ZoomBehavior.IsStart="True"
AttachedBehaviors:ZoomBehavior.ZoomValue="50" />
为了检索有关行为的数据,我使用了单例模式。此模式表示对象的全局静态访问点,并且必须保证类的单个实例的存在
使用此模式的示例(摘自在视图中处理时间显示的行为):
访问视图中的数据
:
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();
}
}
<TextBlock Name="WidgetTimeTextBlock"
Text="{Binding Path=CurrentTime,
Source={x:Static Member=AttachedBehaviors:TimeBehavior.Instance}}" />
选择
通过接口在视图中工作
这种方法的要点是,我们通过ViewModel
调用View
中的一个方法,它完成了所有的工作,而他不知道视图
。这是通过操作接口和此处详细描述的:
使用ServiceLocator
ServiceLocator允许您在视图模型中工作,而不会违反MVVM的原则。您有一个RegisterService方法,在其中注册您想要提供的服务的实例,还有一个GetService方法,您可以使用它来获得您想要的服务
更多信息可在此处找到:
Zoom
似乎是视图特有的问题。在视图级别处理该问题。ViewModel与此无关(除非您希望在数据库中持久化缩放级别值或对其执行一些应用程序逻辑)
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();
}
}
<TextBlock Name="WidgetTimeTextBlock"
Text="{Binding Path=CurrentTime,
Source={x:Static Member=AttachedBehaviors:TimeBehavior.Instance}}" />