C# 将视图操作与输入源解耦
在图形密集型工程软件(CAD、FEM前/后处理)以及游戏中,通常会将视图的连续操纵动作与导致这些操纵的输入源分离。例如,二维或三维视图的旋转可能由以下任意一种方式完成:C# 将视图操作与输入源解耦,c#,wpf,C#,Wpf,在图形密集型工程软件(CAD、FEM前/后处理)以及游戏中,通常会将视图的连续操纵动作与导致这些操纵的输入源分离。例如,二维或三维视图的旋转可能由以下任意一种方式完成: Ctrl键+鼠标左键拖动 鼠标滚轮旋转 在UI中拖动滑块控件 像3DConnexion一样在3D鼠标上推动轴 在应用程序的设置中,用户可以自由地将操纵“旋转视图”重新指定给这些鼠标输入动作之一,以便根据自己的喜好(或者可能是其他应用程序中的习惯)自定义应用程序的行为 在WPF中有没有一种公认的去耦方法,或者我必须为它想出自己
- Ctrl键+鼠标左键拖动
- 鼠标滚轮旋转
- 在UI中拖动滑块控件
- 像3DConnexion一样在3D鼠标上推动轴
我认为问题还可能很复杂:许多不同的键/鼠标按钮/鼠标轴组合。甚至在某些情况下,按下按钮的顺序决定了视图操作(即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应用于该特定问题。通过鼠标移动进行旋转(谈论“拖动”可能是错误的)如何适应此图片?它在一个解耦的命令中处理鼠标滚轮的旋转。问一个如何处理鼠标滚轮旋转的问题怎么不合适?没问题,安迪,谢谢。但正如你所看到的,鼠标滚轮只是我问题的一部分。我列表中的第一项是关于用于旋转的鼠标移动,以及为什么我看不到现成的解决方案。如果只能通过大量编码实现,那么这当然是一个有效的结果。使用鼠标移动进行旋转的一种方法是在装饰器中插入拇指。就像控制调整大小和移动在设计器中工作一样。我终于找到时间来阅读拇指控制。关于我的需求,它有点有限(只有鼠标左键会产生阻力;没有修改状态序列,请看我的问题),但我认为修改它以满足我的需求并不是什么大问题。