从输入源解耦视图操作

时间:2018-03-31 22:34:43

标签: c# wpf

在图形密集型工程软件(CAD,FEM预处理/后处理)以及游戏中,将视图的连续操作动作与导致这些操作的输入源分离是很常见的。例如,2D或3D视图的旋转可以通过

中的任何一个来完成
  • Ctrl-Key + LeftMouse-Drag
  • MouseWheel Rotation
  • 在UI中拖动滑块控件
  • 在3D鼠标上推动轴,如3DConnexion

在应用程序的设置中,用户可以自由地重新分配操作"旋转视图"对其中一个鼠标输入操作,以便根据自己的喜好自定义应用程序的行为(或者可能是他习惯使用的其他应用程序)。

是否有可接受的方式在WPF中进行此类解耦,或者我是否必须为此提出自己的解决方案?

我考虑过命令,但我认为这并不合适,因为移动鼠标不是命令的触发器。也许手势最接近,但它们似乎或多或少都是为触摸输入设计的。

我认为问题还在于可能的复杂性:许多不同的键/鼠标 - 按钮/鼠标 - 轴组合。甚至在某些情况下,按钮序列决定了视图操作(即CATIA V5)。

1 个答案:

答案 0 :(得分:1)

我不知道在3D鼠标上推动轴的意思是什么。 我现在正在编写一个wpf游戏套件,我使用MVVM结合鼠标绑定机制。 滑块控件只是拖动,你拖动的东西叫做拇指。 你可以把任何东西放在wpf ui中的任何东西中,并且制作可拖动的东西的一种方法是把它放在拇指里面。 这是在设置场景时拖动单位以定位它们的方法。

以下是场景编辑器的一些标记。您可以拖动它们并通过将鼠标悬停在一块上并旋转鼠标滚轮来改变面部。您也可以使用向左或向右按​​钮进行控制+单击以更改块中的旋转角度。视图模型更改了一个双Facing属性,该属性绑定到rotatetransform上的角度。

import re

scrubbedXML = re.sub('&.+[0-9]+;', '', xmlstr)
scrubbedXML = re.sub('é', '', scrubbedXML)

root = ET.fromstring(scrubbedXML)
levels = root.findall('.//text')
for level in levels:
    print (level.text)

我混合实现命令的方式的原因是因为我使用点击用于多种目的而且直接鼠标绑定不那么喜欢。否则我很少使用routeduicommands。

鼠标滚轮手势

    <UserControl.CommandBindings>
        <CommandBinding Command="local:CommandLibrary.LeftClick"   Executed="LeftClick_Executed" />
        <CommandBinding Command="local:CommandLibrary.RightClick"  Executed="RightClick_Executed" />
    </UserControl.CommandBindings>
    <UserControl.InputBindings>
        <MouseBinding Gesture="Shift+LeftClick" 
                      Command="{Binding RotateCounterClockWiseCommand}"
                      CommandParameter="{StaticResource FortyFive}"/>
        <MouseBinding Gesture="Shift+RightClick" 
                      Command="{Binding RotateClockWiseCommand}"
                      CommandParameter="{StaticResource FortyFive}"/>
<!--  Up is away from user and Down is towards user   -->
        <MouseBinding Gesture="{local:MouseWheel Direction=Up}" 
                      Command="{Binding RotateCounterClockWiseCommand}"
                      CommandParameter="{StaticResource Five}"/>
        <MouseBinding Gesture="{local:MouseWheel Direction=Down}" 
                      Command="{Binding RotateClockWiseCommand}"
                      CommandParameter="{StaticResource Five}"/>
        <MouseBinding Gesture="LeftClick" 
                      Command="local:CommandLibrary.LeftClick" 
                      CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:PieceView}}}"
                      />
        <MouseBinding Gesture="RightClick" 
                      Command="local:CommandLibrary.RightClick" 
                      CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:PieceView}}}"
                      />
    </UserControl.InputBindings>

这是PieceVM的一个命令:

public enum MouseWheelDirection { Up, Down }

public class MouseWheelGesture : MouseGesture
{
    public MouseWheelDirection Direction { get; set; }

    public MouseWheelGesture(ModifierKeys keys, MouseWheelDirection direction)
        : base(MouseAction.WheelClick, keys)
    {
        Direction = direction;
    }

    public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
    {
        var args = inputEventArgs as MouseWheelEventArgs;
        if (args == null)
            return false;
        if (!base.Matches(targetElement, inputEventArgs))
            return false;
        if (   Direction == MouseWheelDirection.Up && args.Delta > 0
            || Direction == MouseWheelDirection.Down && args.Delta < 0)
        {
            inputEventArgs.Handled = true;
            return true;
        }

        return false;
    }

}

public class MouseWheel : MarkupExtension
{
    public MouseWheelDirection Direction { get; set; }
    public ModifierKeys Keys { get; set; }

    public MouseWheel()
    {
        Keys = ModifierKeys.None;
        Direction = MouseWheelDirection.Down;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new MouseWheelGesture(Keys, Direction);
    }
}

RelayCommand来自MVVMLight。您可以使用nuget MvvmLightLibs添加它。

每个部分(在板上)是一个用户控件,其中有一个PieceVM作为datacontext。实际的部件是通过模板生产的,模板将一个部件与一个部件相关联。这是一个相当标准的viewmodel第一种方法。

这是旋转变换。

private RelayCommand<Double> rotateClockWiseCommand;

public RelayCommand<Double> RotateClockWiseCommand
{
    get
    {
        return rotateClockWiseCommand
        ?? (rotateClockWiseCommand = new RelayCommand<Double>(
        (increase) =>
        {
            if(Facing + increase > 359)
            {
                Facing = 0;
            }
            else
            {
                Facing += increase;
            }
        }));
    }
}

一件实际上是圆形的,因为它的根是边框:

<UserControl.RenderTransform>
    <RotateTransform Angle="{Binding Facing, FallbackValue=0, TargetNullValue=0}" />
</UserControl.RenderTransform>