MVVM 軟體架構模式 - 透過 AttachedBehaviorCommand 偵測滑鼠狀態
這篇使用 Behavior 來實作滑鼠行為並附加到 FrameworkElement 類型元件上,這類別代表 WPF 架構層級實作,基本上包含所有 WPF 的控件
首先實作滑鼠偵測代理人的介面 interface 與滑鼠偵測使用的參數類別
using System;
namespace MVVM
{
public interface IMouseCaptureProxy
{
event EventHandler Capture;
event EventHandler Release;
void OnMouseDown(object sender, MouseCaptureArgs e);
void OnMouseMove(object sender, MouseCaptureArgs e);
void OnMouseUp(object sender, MouseCaptureArgs e);
}
public class MouseCaptureArgs
{
public double X { get; set; }
public double Y { get; set; }
public bool LeftButton { get; set; }
public bool RightButton { get; set; }
}
}
以下就是繼承 Behavior 並實作滑鼠偵測的行為方法,主要重載實作 OnAttached 與 OnDetaching 這兩個方法,以便在之後使用時,用 OnAttached 處理附加在作用的 FrameworkElement 時的操作,並使用 OnDetaching 執行解除附加 FrameworkElement 時的操作
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace MVVM
{
public class MouseCaptureBehavior : Behavior<FrameworkElement>
{
public static readonly
DependencyProperty ProxyProperty =
DependencyProperty.RegisterAttached("Proxy",
typeof(IMouseCaptureProxy),
typeof(MouseCaptureBehavior),
new PropertyMetadata(null, OnProxyChanged));
public static void SetProxy(DependencyObject source, IMouseCaptureProxy value)
{
source.SetValue(ProxyProperty, value);
}
public static IMouseCaptureProxy GetProxy(DependencyObject source)
{
return (IMouseCaptureProxy)source.GetValue(ProxyProperty);
}
private static void OnProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is IMouseCaptureProxy)
{
(e.OldValue as IMouseCaptureProxy).Capture -= OnCapture;
(e.OldValue as IMouseCaptureProxy).Release -= OnRelease;
}
if (e.NewValue is IMouseCaptureProxy)
{
(e.NewValue as IMouseCaptureProxy).Capture += OnCapture;
(e.NewValue as IMouseCaptureProxy).Release += OnRelease;
}
}
static void OnCapture(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.CaptureMouse();
}
static void OnRelease(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.ReleaseMouseCapture();
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreviewMouseDown += OnMouseDown;
this.AssociatedObject.PreviewMouseMove += OnMouseMove;
this.AssociatedObject.PreviewMouseUp += OnMouseUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.PreviewMouseDown -= OnMouseDown;
this.AssociatedObject.PreviewMouseMove -= OnMouseMove;
this.AssociatedObject.PreviewMouseUp -= OnMouseUp;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs
{
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseDown(this, args);
}
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs
{
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseMove(this, args);
}
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs
{
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseUp(this, args);
}
}
}
}
在開始實作這個滑鼠操作行為前,一樣別忘了在前臺 XAML 上加入以下的命名空間
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
這裡將 Behavior 附加在 Canvas 上做範例
<!-- Canvas must have a background, even if it's Transparent -->
<Canvas Background="White">
<i:Interaction.Behaviors>
<behaviors:MouseCaptureBehavior Proxy="{Binding}" />
</i:Interaction.Behaviors>
</Canvas>
在 ViewModel 部分實作 ImouseCaptureProxy 介面的三個方法(OnMouseDown/OnMouseMove/OnMouseUp),並觸發 Behavior 時響應的 Capture/Release 事件;要注意的一點是兩個事件的發送者與三個方法的 Handler 應該是同一個!當不使用傳遞給 Capture/Release 的事件參數時,事件參數設定為 null
public class XXViewModel : ViewModelBase, IMouseCaptureProxy
{
public event EventHandler Capture;
public event EventHandler Release;
public void OnMouseDown(object sender, MouseCaptureArgs e) {...}
public void OnMouseMove(object sender, MouseCaptureArgs e) {...}
public void OnMouseUp(object sender, MouseCaptureArgs e) {...}
}