从ViewModel操纵视图

本文关键字:视图 操纵 ViewModel | 更新日期: 2023-09-27 18:17:20

早上好,提前感谢您的回答。

我的视图实现了一个pan &缩放库:用于缩放和平移的WPF自定义控件

在视图中,有一个控件可以根据鼠标双击缩放到一个点:

private void zoomAndPanControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
   if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
   {
      Point doubleClickPoint = e.GetPosition(content);
      zoomAndPanControl.AnimatedSnapTo(doubleClickPoint);
   }
}

我想强制"zoomAndPanControl.AnimatedSnapTo(doubleClickPoint);"到一个特定的点基于ViewModel数据的几何点,我画,当我拉它。视图会平移到新几何体的x,y坐标。几何图形/点已经绑定到视图。

作为补充说明,提取几何点的数据发生在DispatchTimer中。当读取新的几何线时,我希望视图平移。

是否有一个简单的方法来访问这个控件从ViewModel,当我得到数据?可能模拟鼠标事件与自定义点?我不知道该怎么做才好。

从ViewModel操纵视图

要通过绑定来控制View,你的控件需要绑定到一些东西。

  1. 为你的自定义控件添加一个接受'Point'类型的依赖属性
  2. 绑定INotifyPropertyChanged属性从你的视图模型实现到DP -注意,一个ValueConverter可以添加在这里,如果你想避免使用'Point'类在你的VM。
  3. 在您的DP定义中,调整setter以触发AnimatedSnapTo(…)方法

希望对你有帮助。

我通常处理这类场景的方式是在ViewModel上定义一个委托,在View中设置它,并在需要时调用它:

在ViewModel中定义一个方法,该方法接受一个Action<Point>和一个Point对象作为当前鼠标位置:

public void ExecuteAnimatedSnapTo(Action<Point> animatedSnapToAction, Point pointerPosition)
{
    if (animatedSnapToAction != null && pointerPosition != null)
    {
        // Create a new point based on the one passed in and data in ViewModel
        Point newPoint =
            new Point(pointerPosition.X + viewModelData.X, pointerPosition.Y + viewModelData.Y);
        // Invoke the delegate using the new point
        animatedSnapToAction(newPoint);
    }
}

然后在视图后面的代码中,执行这个方法:

private void zoomAndPanControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
   if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
   {
      Point doubleClickPoint = e.GetPosition(content);
      var viewModel = (MyViewModel)this.DataContext;
      viewModel.ExecuteAnimatedSnapTo(zoomAndPanControl.AnimatedSnapTo, doubleClickPoint);
   }
}   

使用这种方法,您仍然可以保持View与ViewModel的隔离。当[单元]测试VM时,当传递到方法中时,委托和点可能是空的。if块然后阻止"UI"逻辑被测试。

你需要注意的一件事是,如果ViewModel数据是在不同的线程上计算的,你必须在UI调度程序上执行委托。

编辑

我的印象是ViewModel拥有在MouseDoubleClick被触发时调用委托所需的所有数据。如果不是这样,更好的解决方案是将Action作为VM的属性公开,并在需要时调用它:

public Action<Point> AnimatedSnapToAction { get; set; }

在View上创建VM实例时,也要设置该属性:

public MyView()
{
    InitializeComponent();
    MyViewModel viewModel = new MyViewModel();
    viewModel.AnimatedSnapToAction = zoomAndPanControl.AnimatedSnapTo;
    this.DataContext = viewModel;
}

现在您可以随时在VM上执行委托。例如,如果需要在DispatcherTimer的滴答声中调用它,它看起来像这样:

private void dispatcherTimer_Tick(object sender, EventArgs e)
{
    // Calculate geometry data
    if(AnimatedToSnapAction != null)
    {
        AnimatedSnapToAction(pointCalculatedUsingGeometryData);
    }
}

视图模型应该公开视图需要显示和交互的数据和行为,它永远不应该有对视图本身的引用。

View隐式或显式地引用了ViewModel。通过这个引用,你可以调用方法,绑定数据,连接事件等…

为了在这种情况下保持简单,您可能希望在用户双击控件时在ViewModel上执行命令,然后可以在命令执行中修改ViewModel公开的任何公共属性。然后可以使用这些属性来确定要缩放到的点。