一、目的:封裝Zoom效果到Behavior中,方便實現鼠標滾輪定點放大縮小,鼠標拖動等效果
二、實現
1、鼠標滾輪定點放大縮小
2、鼠標拖動平移
3、恢復初始位置
4、設置縮放是否應用在整個容器中
5、設置居中對齊還是居左對齊
6、通過行爲直接加載
三、示例
四、實現過程
1、如下定義Behavior
/// <summary> Zoom帶有鼠標移動平移和滾輪定點放大效果 </summary>
public class ZoomWithWheelAndMoveBehavior : Behavior<FrameworkElement>
{
// Message:外部需要嵌套Grid
Grid parent;
// Message:Zoom控件
ZoomableCanvas zoomable;
/// <summary> 是否在父容器中也使用平移和縮放 </summary>
public bool UseInParent
{
get { return (bool)GetValue(UseInParentProperty); }
set { SetValue(UseInParentProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UseInParentProperty =
DependencyProperty.Register("UseInParent", typeof(bool), typeof(ZoomWithWheelAndMoveBehavior), new PropertyMetadata(default(bool), (d, e) =>
{
ZoomWithWheelAndMoveBehavior control = d as ZoomWithWheelAndMoveBehavior;
if (control == null) return;
control.RefreshEvent();
}));
public bool IsCenterInZoom
{
get { return (bool)GetValue(IsCenterInZoomProperty); }
set { SetValue(IsCenterInZoomProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCenterInZoomProperty =
DependencyProperty.Register("IsCenterInZoom", typeof(bool), typeof(ZoomWithWheelAndMoveBehavior), new PropertyMetadata(default(bool), (d, e) =>
{
ZoomWithWheelAndMoveBehavior control = d as ZoomWithWheelAndMoveBehavior;
if (control == null) return;
control.RefreshLocation();
}));
public bool IsReturn
{
get { return (bool)GetValue(IsReturnProperty); }
set { SetValue(IsReturnProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsReturnProperty =
DependencyProperty.Register("IsReturn", typeof(bool), typeof(ZoomWithWheelAndMoveBehavior), new PropertyMetadata(default(bool), (d, e) =>
{
ZoomWithWheelAndMoveBehavior control = d as ZoomWithWheelAndMoveBehavior;
if (control == null) return;
control.RefreshReturn();
}));
void RefreshReturn()
{
if (zoomable == null) return;
zoomable.Scale=1;
zoomable.Offset =new Point(0,0);
}
/// <summary> 更新UseInParent刷新事件 </summary>
void RefreshEvent()
{
if (this.parent == null || this.zoomable == null) return;
if (!UseInParent)
{
parent.MouseWheel -= OnMouseWheel;
parent.MouseMove -= OnMouseMove;
}
else
{
zoomable.MouseWheel -= OnMouseWheel;
zoomable.MouseMove -= OnMouseMove;
}
if (UseInParent)
{
parent.MouseWheel += OnMouseWheel;
parent.MouseMove += OnMouseMove;
}
else
{
zoomable.MouseWheel += OnMouseWheel;
zoomable.MouseMove += OnMouseMove;
}
}
void RefreshLocation()
{
if (this.zoomable == null) return;
if (this.IsCenterInZoom)
{
var width = this.zoomable.ActualWidth - this.AssociatedObject.Width;
var height = this.zoomable.ActualHeight - this.AssociatedObject.Height;
Canvas.SetTop(this.AssociatedObject, height / 2);
Canvas.SetLeft(this.AssociatedObject, width / 2);
}
else
{
Canvas.SetTop(this.AssociatedObject, 0);
Canvas.SetLeft(this.AssociatedObject, 0);
}
}
protected override void OnAttached()
{
parent = AssociatedObject.GetParent<Grid>();
parent.Children.Remove(AssociatedObject);
zoomable = new ZoomableCanvas();
zoomable.Children.Add(AssociatedObject);
zoomable.Loaded += Zoomable_Loaded;
parent.Children.Add(zoomable);
if (UseInParent)
{
parent.MouseWheel += OnMouseWheel;
parent.MouseMove += OnMouseMove;
}
else
{
zoomable.MouseWheel += OnMouseWheel;
zoomable.MouseMove += OnMouseMove;
}
}
private void Zoomable_Loaded(object sender, RoutedEventArgs e)
{
this.RefreshLocation();
}
protected override void OnDetaching()
{
if (UseInParent)
{
parent.MouseWheel -= OnMouseWheel;
parent.MouseMove -= OnMouseMove;
}
else
{
zoomable.MouseWheel -= OnMouseWheel;
zoomable.MouseMove -= OnMouseMove;
}
zoomable.Loaded -= Zoomable_Loaded;
}
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var x = Math.Pow(2, e.Delta / 3.0 / Mouse.MouseWheelDeltaForOneLine);
zoomable.Scale *= x;
// Adjust the offset to make the point under the mouse stay still.
var position = (Vector)e.GetPosition(parent);
zoomable.Offset = (Point)((Vector)(zoomable.Offset + position) * x - position);
e.Handled = true;
}
private Point LastMousePosition;
private void OnMouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this.parent);
if (e.LeftButton == MouseButtonState.Pressed)
{
this.zoomable.Offset -= position - LastMousePosition;
}
LastMousePosition = position;
}
}
2、如下設置Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions>
<h:Row>
<h:FCheckBox x:Name="cbx_inparent" Content="UseInParent" IsChecked="True"/>
<h:FCheckBox x:Name="cbx_center" Content="IsCenterInZoom" Grid.Column="1" IsChecked="True"/>
<h:FCheckBox x:Name="cbx_return" Content="Return" Grid.Column="2" IsChecked="True"/>
<Label Style="{StaticResource S.Label.Flash}" Content="提示:嘗試鼠標拖動和鼠標滾輪進行操作" HorizontalAlignment="Left" Foreground="Red" Grid.Column="3" Grid.ColumnSpan="3" FontSize="{StaticResource S.FontSize.Header}"/>
</h:Row>
<Grid ClipToBounds="True" Background="Transparent" Grid.Row="1">
<h:ObjectPropertyForm Grid.Row="1" Title="學生信息" SelectObject="{StaticResource S.Student.HeBianGu}">
<h:Interaction.Behaviors>
<h:ZoomWithWheelAndMoveBehavior UseInParent="{Binding ElementName=cbx_inparent,Path=IsChecked,Mode=TwoWay}"
IsCenterInZoom="{Binding ElementName=cbx_center,Path=IsChecked,Mode=TwoWay}"
IsReturn="{Binding ElementName=cbx_return,Path=IsChecked,Mode=TwoWay}"/>
</h:Interaction.Behaviors>
</h:ObjectPropertyForm>
</Grid>
</Grid>
其中行爲代碼如下:
<h:Interaction.Behaviors>
<h:ZoomWithWheelAndMoveBehavior UseInParent="{Binding ElementName=cbx_inparent,Path=IsChecked,Mode=TwoWay}"
IsCenterInZoom="{Binding ElementName=cbx_center,Path=IsChecked,Mode=TwoWay}"
IsReturn="{Binding ElementName=cbx_return,Path=IsChecked,Mode=TwoWay}"/>
</h:Interaction.Behaviors>
通過以上幾部可以實現Zoom效果
其中Zoom控件部分使用的開源項目特此說明:
https://github.com/mdrabick/StiZoomableCanvas
五、下載地址
GitHub下載地址:https://github.com/HeBianGu/WPF-ControlBase.git