Wpf MVVM Light EventToCommand不使用CaptureMouse
我在EventToCommand中遇到了一些问题,它的行为与我对CaptureMouse的预期不符 我有一个ResizeGrip,我已经在上面定义了几个EventToCommand:Wpf MVVM Light EventToCommand不使用CaptureMouse,wpf,mvvm-light,Wpf,Mvvm Light,我在EventToCommand中遇到了一些问题,它的行为与我对CaptureMouse的预期不符 我有一个ResizeGrip,我已经在上面定义了几个EventToCommand: <ResizeGrip Name="ResizeGrip" HorizontalAlignment="Right" VerticalAlignment="Bottom" Cursor="SizeNWSE"> <i:Interaction.Triggers> <i:EventTrig
<ResizeGrip Name="ResizeGrip" HorizontalAlignment="Right" VerticalAlignment="Bottom" Cursor="SizeNWSE">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding ResizeStartCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<cmd:EventToCommand Command="{Binding ResizeStopCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<cmd:EventToCommand Command="{Binding ResizeCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ResizeGrip>
OnRequestResizeStart和OnRequestResizeStop命令工作正常,OnRequestResize工作正常。。。但只有当我真的超过了尺寸控制时。CaptureMouse似乎并没有实际将所有鼠标事件发送到ResizeGrip
这是EventToCommand的一个限制,还是需要发生一些特殊的事情
谢谢你的帮助 为什么要为此使用命令而不是标准事件处理程序?这显然是属于代码隐藏的UI逻辑,而不是ViewModel ****更新** 如果您在ControlTemplate中使用它,您可以将控件的代码视为您的代码隐藏。要连接事件,可以使用TemplatePart模式并连接到OnApplyTemplate中的特殊命名元素。为了方便提供大小(或VM需要的任何其他内容),您可以添加一个DP来存储适当的值,并将VM属性绑定到该值。如果需要从VM设置初始大小之类的操作,这也允许进行双向绑定
// this doesn't enforce the name but suggests it
[TemplatePart(Name = "PART_Resizer", Type = typeof(ResizeGrip))]
public class MyContainer : ContentControl
{
private ResizeGrip _grip;
public static readonly DependencyProperty ContainerDimensionsProperty = DependencyProperty.Register(
"ContainerDimensions",
typeof(Size),
typeof(MyContainer),
new UIPropertyMetadata(Size.Empty, OnContainerDimensionsChanged));
private static void OnContainerDimensionsChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
MyContainer myContainer = dObj as MyContainer;
if (myContainer != null)
{
Size newValue = (Size)e.NewValue;
if (newValue != Size.Empty)
{
myContainer.Width = newValue.Width;
myContainer.Height = newValue.Height;
}
}
}
public Size ContainerDimensions
{
get { return (Size)GetValue(ContainerDimensionsProperty); }
set { SetValue(ContainerDimensionsProperty, value); }
}
static MyContainer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyContainer), new FrameworkPropertyMetadata(typeof(MyContainer)));
}
public override void OnApplyTemplate()
{
_grip = Template.FindName("PART_Resizer", this) as ResizeGrip;
if (_grip != null)
{
_grip.MouseLeftButtonDown += Grip_MouseLeftButtonDown;
// other handlers
}
SizeChanged += MyContainer_SizeChanged;
base.OnApplyTemplate();
}
void MyContainer_SizeChanged(object sender, SizeChangedEventArgs e)
{
// update your DP
}
...
}
要使用它,您需要确保模板中的元素具有与属性匹配的正确类型和名称
<local:MyContainer ContainerDimensions="{Binding Path=SavedSize}">
<local:MyContainer.Template>
<ControlTemplate TargetType="{x:Type local:MyContainer}">
<Grid>
<Border Background="{TemplateBinding Background}">
<ContentPresenter/>
</Border>
<ResizeGrip x:Name="PART_Resizer" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="20" Height="20"/>
</Grid>
</ControlTemplate>
</local:MyContainer.Template>
</local:MyContainer>
您现在可以将此模板放在任何位置,因为它没有声明任何事件处理程序,因此独立于代码隐藏文件。现在,您已经将所有UI逻辑封装在特定于UI的类中,但仍然可以通过绑定来访问VM中所需的数据
如果您仔细想想,这是您通常与内置控件交互的方式。如果您使用扩展器,您不希望将ToggleButton的单击传递到VM中,并尝试使控件从此处展开,但您可能想知道扩展器是打开的还是关闭的,因此有一个IsExpanded属性,您可以绑定并保存并加载为数据。我在使用事件与命令时没有问题,因为我还在学习用WPF和MVVM模式组织代码的最佳方法。但是我仍然需要ViewModel来知道发生了什么,这样我就可以更新正在调整大小的对象模型。让代码隐藏与底层模型交互的最佳方式是什么?另一个有趣的问题是——ResizeGrip是在ResourceDictionary中的样式(当前)中定义的。在这种情况下,我在定义事件时遇到了很多问题。谢谢你的指点,John。我能够按照您的建议进行一切设置,并且基于我之前编写的代码,所有绑定仍然有效。不需要依赖财产。。。至少现在是这样。也让我大吃一惊!不幸的是,这并没有解决我遇到的问题,因为ResizeGrip仍然会在指针“掉”下来时丢失鼠标捕获(在速度较慢的计算机上)。但这有助于缩小潜在的原因!
// this doesn't enforce the name but suggests it
[TemplatePart(Name = "PART_Resizer", Type = typeof(ResizeGrip))]
public class MyContainer : ContentControl
{
private ResizeGrip _grip;
public static readonly DependencyProperty ContainerDimensionsProperty = DependencyProperty.Register(
"ContainerDimensions",
typeof(Size),
typeof(MyContainer),
new UIPropertyMetadata(Size.Empty, OnContainerDimensionsChanged));
private static void OnContainerDimensionsChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
MyContainer myContainer = dObj as MyContainer;
if (myContainer != null)
{
Size newValue = (Size)e.NewValue;
if (newValue != Size.Empty)
{
myContainer.Width = newValue.Width;
myContainer.Height = newValue.Height;
}
}
}
public Size ContainerDimensions
{
get { return (Size)GetValue(ContainerDimensionsProperty); }
set { SetValue(ContainerDimensionsProperty, value); }
}
static MyContainer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyContainer), new FrameworkPropertyMetadata(typeof(MyContainer)));
}
public override void OnApplyTemplate()
{
_grip = Template.FindName("PART_Resizer", this) as ResizeGrip;
if (_grip != null)
{
_grip.MouseLeftButtonDown += Grip_MouseLeftButtonDown;
// other handlers
}
SizeChanged += MyContainer_SizeChanged;
base.OnApplyTemplate();
}
void MyContainer_SizeChanged(object sender, SizeChangedEventArgs e)
{
// update your DP
}
...
}
<local:MyContainer ContainerDimensions="{Binding Path=SavedSize}">
<local:MyContainer.Template>
<ControlTemplate TargetType="{x:Type local:MyContainer}">
<Grid>
<Border Background="{TemplateBinding Background}">
<ContentPresenter/>
</Border>
<ResizeGrip x:Name="PART_Resizer" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="20" Height="20"/>
</Grid>
</ControlTemplate>
</local:MyContainer.Template>
</local:MyContainer>