C# 将视图操作与输入源解耦

C# 将视图操作与输入源解耦,c#,wpf,C#,Wpf,在图形密集型工程软件(CAD、FEM前/后处理)以及游戏中,通常会将视图的连续操纵动作与导致这些操纵的输入源分离。例如,二维或三维视图的旋转可能由以下任意一种方式完成: Ctrl键+鼠标左键拖动 鼠标滚轮旋转 在UI中拖动滑块控件 像3DConnexion一样在3D鼠标上推动轴 在应用程序的设置中,用户可以自由地将操纵“旋转视图”重新指定给这些鼠标输入动作之一,以便根据自己的喜好(或者可能是其他应用程序中的习惯)自定义应用程序的行为 在WPF中有没有一种公认的去耦方法,或者我必须为它想出自己

在图形密集型工程软件(CAD、FEM前/后处理)以及游戏中,通常会将视图的连续操纵动作与导致这些操纵的输入源分离。例如,二维或三维视图的旋转可能由以下任意一种方式完成:

  • Ctrl键+鼠标左键拖动
  • 鼠标滚轮旋转
  • 在UI中拖动滑块控件
  • 像3DConnexion一样在3D鼠标上推动轴
在应用程序的设置中,用户可以自由地将操纵“旋转视图”重新指定给这些鼠标输入动作之一,以便根据自己的喜好(或者可能是其他应用程序中的习惯)自定义应用程序的行为

在WPF中有没有一种公认的去耦方法,或者我必须为它想出自己的解决方案

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


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

我不知道在3d鼠标上按下轴意味着什么。 我现在正在编写一个wpf游戏套件,我使用MVVM结合多种机制进行鼠标绑定。 滑块控件只是拖动,拖动的对象称为拇指。 您可以在wpf ui中的任何内容中放入几乎任何内容,而使某些内容可拖动的一种方法是将其放入拇指中。 这就是在设置场景时拖动单元以定位它们的方式

下面是场景编辑器pieceview中的一些标记。通过将鼠标悬停在工件上并旋转鼠标滚轮,可以拖动这些面并更改面。您还可以按住ctrl键并单击左键或右键来更改块中的旋转角度。viewmodel会更改双面特性,该特性绑定到旋转变换器上的角度

    <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>

我之所以混合使用各种方法来实现命令,是因为我将单击用于多种目的,而直接鼠标绑定不太喜欢这种方式。否则我很少使用常规命令

鼠标滚轮手势

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);
    }
}
public enum mouseweeldirection{Up,Down}
公共类鼠标滚轮手势:MouseGesture
{
公共鼠标滚轮方向{get;set;}
公共鼠标滚轮手势(修改键、鼠标滚轮方向)
:base(鼠标移动。车轮单击,按键)
{
方向=方向;
}
公共覆盖布尔匹配(对象targetElement、InputEventArgs和InputEventArgs)
{
var args=作为MouseWheelEventArgs的inputEventArgs;
如果(args==null)
返回false;
如果(!base.Matches(targetElement,inputEventArgs))
返回false;
如果(方向==MouseWheelDirection.Up&&args.Delta>0
||方向==鼠标滚轮方向。向下(&A)参数。增量<0)
{
inputEventArgs.Handled=true;
返回true;
}
返回false;
}
}
公共类鼠标滚轮:MarkupExtension
{
公共鼠标滚轮方向{get;set;}
公共ModifierKeys{get;set;}
公共鼠标轮()
{
Keys=ModifierKeys.None;
方向=鼠标滚轮方向。向下;
}
公共覆盖对象ProviderValue(IServiceProvider服务提供程序)
{
返回新的鼠标滚轮手势(键、方向);
}
}
下面是命令之一:

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;
            }
        }));
    }
}
private RelayCommand rotateclockwise命令;
公共中继命令RotateClockWiseCommand
{
得到
{
返回rotateClockWiseCommand
??(rotateClockWiseCommand=新继电器命令(
(增加)=>
{
如果(朝向+增加>359)
{
正面=0;
}
其他的
{
正面+=增加;
}
}));
}
}
RelayCommand来自MVVMLight。您可以使用nuget MvvmLightLibs添加它

每一块(板上)都是一个用户控件,它的datacontext是一个PieceVM。实际的片段是通过模板生成的,模板将一个片段VM与一个片段相关联。这是一种相当标准的viewmodel-first方法

这是旋转变换

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

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

<Border  CornerRadius="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" 
         Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" 
         Name="border"
         BorderThickness="1.5"
         >


Oliver,使用MVVM模式怎么样?您可以使用命令和绑定来完成此操作。@MichelBorges:是的,这就是问题所在:如何将MVVM应用于该特定问题。通过鼠标移动进行旋转(谈论“拖动”可能是错误的)如何适应此图片?它在一个解耦的命令中处理鼠标滚轮的旋转。问一个如何处理鼠标滚轮旋转的问题怎么不合适?没问题,安迪,谢谢。但正如你所看到的,鼠标滚轮只是我问题的一部分。我列表中的第一项是关于用于旋转的鼠标移动,以及为什么我看不到现成的解决方案。如果只能通过大量编码实现,那么这当然是一个有效的结果。使用鼠标移动进行旋转的一种方法是在装饰器中插入拇指。就像控制调整大小和移动在设计器中工作一样。我终于找到时间来阅读拇指控制。关于我的需求,它有点有限(只有鼠标左键会产生阻力;没有修改状态序列,请看我的问题),但我认为修改它以满足我的需求并不是什么大问题。