MVVM Light EventToCommand不与CaptureMouse一起使用

时间:2010-08-04 17:08:04

标签: wpf mvvm-light

我遇到的问题是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的限制,还是需要特殊的事情?

感谢您的帮助!

1 个答案:

答案 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属性可以绑定并保存和加载为数据。