10

好的,我已经知道如何使用 LayoutTransform 和 ScaleTransform 来缩放 UI 元素网格。我不明白的是如何让我的视图响应 CTRL+MouseWheelUp\Down 来做到这一点,以及如何使代码适合 MVVM 模式。

我的第一个想法是将 ZoomFactor 存储为一个属性,并绑定到一个命令来调整它。

我在看类似的东西:

<UserControl.InputBindings>
 <MouseBinding Command="{Binding ZoomGrid}" Gesture="Control+WheelClick"/>
</UserControl.InputBindings>

但我看到两个问题:

1)我认为没有办法判断轮子是向上还是向下移动,我也看不出如何确定多少。我见过 MouseWheelEventArgs.Delta,但不知道如何获得它。

2)绑定到视图模型上的命令似乎不正确,因为它严格来说是一个视图。

由于缩放严格来说只是 UI 视图,我认为实际代码应该放在代码隐藏中。

你们将如何实现这一点?

ps,我正在使用 .net\wpf 4.0,将 Cinch 用于 MVVM。

4

6 回答 6

13

真正的答案是编写自己的 MouseGesture,这很容易。

<MouseBinding Gesture="{x:Static me:MouseWheelGesture.CtrlDown}"  
              Command="me:MainVM.SendBackwardCommand" />

public class MouseWheelGesture : MouseGesture
{
    public static MouseWheelGesture CtrlDown
        => new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Down};

    public MouseWheelGesture(): base(MouseAction.WheelClick)
    {
    }

    public MouseWheelGesture(ModifierKeys modifiers) : base(MouseAction.WheelClick, modifiers)
    {
    }

    public WheelDirection Direction { get; set; }

    public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
    {
        if (!base.Matches(targetElement, inputEventArgs)) return false;
        if (!(inputEventArgs is MouseWheelEventArgs args)) return false;
        switch (Direction)
        {
            case WheelDirection.None:
                return args.Delta == 0;
            case WheelDirection.Up:
               return args.Delta > 0;
            case WheelDirection.Down:
                return args.Delta < 0;
            default:
                return false;
        }
    }

    public enum WheelDirection
    {
      None,
      Up,
      Down,
    }
}
于 2011-09-23T10:16:58.053 回答
4

我建议你在你的虚拟机中实现一个通用的缩放命令。该命令可以使用新的缩放级别进行参数化,或者(可能更简单)您可以实现increaseZoomCommandDecreaseZoomCommand。然后在处理完鼠标滚轮事件的事件参数后,使用视图的代码调用这些命令。如果增量为正,则放大,如果为负,则缩小。

通过使用后面的几行代码来解决这个问题并没有什么坏处。MVVM 的主要思想是,您可以在不依赖于 UI 的对象中跟踪和修改视图的几乎完整状态(增强可测试性)。因此,作为缩放结果的新视口的计算应该在 VM 中完成,而不是在后面的代码中。

代码中存在的可测试性的小差距可以被自动 UI 测试忽略或覆盖。然而,自动 UI 测试可能非常昂贵。

于 2010-02-16T12:26:32.930 回答
2

如果您不想使用后面的代码,可以使用 mvvm light 的 EventToCommand 功能:

看法:

 <...
     xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
     ...> 
<i:Interaction.Triggers>
         <i:EventTrigger EventName="PreviewMouseWheel">
             <cmd:EventToCommand Command="{Binding
     Path=DataContext.ZoomCommand,
     ElementName=Root, Mode=OneWay}"
         PassEventArgsToCommand="True"   />
         </i:EventTrigger> </i:Interaction.Triggers>

视图模型:

ZoomCommand = new RelayCommand<RoutedEventArgs>(Zoom);
...
public void Zoom(RoutedEventArgs e)
{
    var originalEventArgs = e as MouseWheelEventArgs;
    // originalEventArgs.Delta contains the relevant value
}

我希望这可以帮助别人。我知道这个问题有点老...

于 2011-06-25T20:38:58.323 回答
0

我认为您尝试做的事情与视图非常相关,因此将代码放在您的代码后面没有害处(至少在我看来),尽管我确信有一些优雅的方式来处理这个问题更多基于视图模型。

您应该能够注册到 OnPrevewMouseWheel 事件,检查用户是否按下了控制键并相应地更改缩放因子以获得您正在寻找的缩放效果。

于 2010-02-16T08:40:37.323 回答
0

我同意这两个答案,并且只会补充说,在这种情况下,使用后面的代码是唯一的方法,所以你甚至不必考虑它是否违反了任何好的做法。

事实是,获取 MouseEventArgs(以及 Delta)的唯一方法是在后面的代码中,所以抓住你需要的东西(不需要逻辑)并将它传递给你的视图模型,正如 olli 建议的那样。

另一方面,您可能希望使用更通用的增量(例如,在将其作为步骤传递给视图模型之前将其除以 120),以使其不了解与视图或操作系统相关的任何约定。这将允许在视图模型中最大限度地重用您的代码。

于 2010-03-19T13:43:06.853 回答
0

为了避免整个问题,还有一个选项: - 在 xaml 中使用 ContentPresenter 并将其内容绑定到 viewmodel 对象。- 处理视图模型中的鼠标滚轮事件。

于 2013-12-19T13:35:15.043 回答