我遇到的问题是EventToCommand的行为与CaptureMouse的预期不符。
我有一个ResizeGrip,我已经定义了几个EventToCommand:
<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>
处理函数在类的构造函数中设置:
ResizeStartCommand = new RelayCommand<MouseButtonEventArgs>(
(e) => OnRequestResizeStart(e));
ResizeStopCommand = new RelayCommand<MouseButtonEventArgs>(
(e) => OnRequestResizeStop(e));
ResizeCommand = new RelayCommand<MouseEventArgs>(
(e) => OnRequestResize(e),
param => CanResize);
最后,我完成了所有调整大小的逻辑:
void OnRequestResizeStart(MouseButtonEventArgs e)
{
bool r = Mouse.Capture((UIElement)e.Source);
Console.WriteLine("mouse down: " + r.ToString());
}
void OnRequestResizeStop(MouseButtonEventArgs e)
{
((UIElement)e.Source).ReleaseMouseCapture();
_canResize = false;
}
void OnRequestResize(MouseEventArgs e)
{
Console.WriteLine("mouse move");
}
bool CanResize
{ get { return _canResize; } }
OnRequestResizeStart&amp; OnRequestResizeStop命令工作正常,OnRequestResize工作......但只有当我实际上在ResizeGrip上。似乎CaptureMouse实际上并没有将所有鼠标事件发送到ResizeGrip。
这是EventToCommand的限制,还是需要特殊的事情?
感谢您的帮助!
答案 0 :(得分:0)
为什么使用命令代替标准事件处理程序?这显然是属于代码隐藏的UI逻辑,而不是ViewModel。
****更新**
如果您在ControlTemplate中使用它,则可以将Control的代码视为代码隐藏。要执行事件的连接,可以使用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中所需的数据。
如果你仔细想想,这就是你通常与内置控件交互的方式。如果您使用Expander,您不希望将ToggleButton的点击传递到您的VM并尝试从那里扩展控件,但您可能想知道Expander是打开还是关闭,因此您有一个IsExpanded属性可以绑定并保存和加载为数据。