InkCanvas是WPF中進行墨跡繪制的控制元件,本文介紹下InkCanvas控制元件是如何進行選擇操作的,文中有誤的地方希望大家進行批評指正,
InkCanvas的選擇效果
使用WPF可以輕松實作白板功能,只需要添加一個InkCanvas控制元件,修改InkCanvas的EditingMode屬性可以控制InkCanvas的操作模式,如書寫、選擇、擦除等模式,
如下demo在視窗中添加一個InkCanvas,然后添加一個Button實作書寫與選擇模式的切換,
// xaml
<Grid>
<InkCanvas x:Name="inkCanvas"/>
<Button x:Name="btnChangeMode" Content="select" Click="Button_Click"
Width="50" Height="30" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10"/>
</Grid>
// cs
private void Button_Click(object sender, RoutedEventArgs e)
{
if(btnChangeMode.Content == "select")
{
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
btnChangeMode.Content = "write";
}
else
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
btnChangeMode.Content = "select";
}
}
運行demo,書寫后點擊按鈕進行選擇,可以看到InkCanvas的選擇操作如下圖所示:

從圖中可以看出,InkCanvas的選擇效果有如下特點:
- 選中后筆跡高亮;
- 選中后顯示選擇框;
- 拖動選擇框,選擇框隨著滑鼠移動,但選擇的筆跡并未移動,
接下來看下WPF是如何實作這種選擇操作的,
InkCanvas選擇模式的實作
首先,InkCanvas的編輯功能(書寫、擦除、選擇等)是通過EditingCoordinator管理的,該類包含一系列的EditingBehavior,實作選擇程序的為LassoSelectionBehavior類,實作選擇后對選擇框操作的為SelectionEditor與SelectionEditingBehavior,本文主要介紹選擇后對選擇框的操作程序,選擇程序以及筆跡的高亮顯示打算單獨寫一篇文章進行介紹,
在InkCanvas中,與選擇功能相關的物件有InkCanvasSelection、InkCanvasSelectionAdorner及InkCanvasFeedbackAdorner,后兩者為裝飾器,裝飾器的介紹可參考官方檔案,
先看InkCanvasSelectionAdorner類,直接看其OnRender方法,代碼如下,首先繪制了選擇框的背景,然后繪制了選擇框(矩形虛線效果),最后繪制了選擇框上的9個小矩形按鈕,按鈕可以進行拖動調節,具體實作邏輯可以看代碼,本文不贅述,
protected override void OnRender(DrawingContext drawingContext)
{
DrawBackground(drawingContext);
Rect rectWireFrame = GetWireFrameRect()
if(!rectWireFrame.IsEmpty)
{
drawingContext.DrawRectangle(null, _adornerBorderPen, rectWireFrame);
DrawHandles(drawingContext, rectWireFrame);
}
}
再看InkCanvasFeedbackAdorner類,同樣看OnRender方法,代碼如下,其僅繪制了矩形虛線選擇框,通過這兩個類的OnRender方法,結合上文中的影片,可以知道選中后使用InkCanvasSelectionAdorner進行裝飾,對選擇框的操作(拖動)使用InkCanvasFeedbackAdorner進行裝飾,
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawRectangle(null, _adornerBorderPen,
new Rect(CornerResizeHandleSize / 2, CornerResizeHandleSize / 2,
_frameSize.Width - CornerResizeHandleSize, _frameSize.Height - CornerResizeHandleSize));
}
接下來看下這兩個Adorner是對誰進行裝飾的,首先看InkCanvas的OnPreApplyTemplate方法,代碼如下,注釋部分是InkCanvas的Visual Tree,可以了解到InkCanvas的內部結構,再看下SelectionAdorner的初始化,可以看出是對InnerCanvas進行裝飾,InnerCanvas是InkCanvas的內部容器,放置筆跡及其它UIElement,SelectionAdorner添加了對ActiveEditingMode的系結,當Mode為None時,隱藏,否則顯示,FeedbackAdorner的裝飾物件通過其建構式可以看出,也是裝飾的InnerCanvas,
internal override void OnPreApplyTemplate()
{
base.OnPreApplyTemplate();
// Build our visual tree here.
// <InkCanvas>
// <AdornerDecorator>
// <InkPresenter>
// <InnerCanvas/>
// <ContainerVisual/>
// <HostVisual/>
// </InkPresenter>
// <AdornerLayer>
// <InkCanvasSelectionAdorner/>
// <InkCanvasFeedbackAdorner/>
// </AdornerLayer>
// </AdornerDecorator>
// </InkCanvas>
if(_localAdornerDecorator == null)
{
_localAdornerDecorator = new AdornerDecorator();
InkPresenter inkPresenter = InkPresenter;
AddVisualChild(_localAdornerDecorator);
_localAdornerDecorator.Child = inkPresenter;
inkPresenter.Child = InnerCanvas;
_localAdornerDecorator.AdornerLayer.Add(SelectionAdorner);
}
}
internal InkCanvasSelectionAdorner SelectionAdorner
{
get
{
if(_selectionAdorner == null)
{
_selectionAdorner = new InkCanvasSelectionAdorner(InnerCanvas);
Binding activedEditingModeBinding = new Binding();
activedEditingModeBinding.Path = new PropertyPath(InkCanvas.ActiveEditingModeProperty);
activedEditingModeBinding.Mode = BindingMode.OneWay;
activedEditingModeBinding.Source = this;
activedEditingModeBinding.Converter = new ActiveEditingMode2VisibilityConverter();
_selectionAdorner.SetBinding(UIElement.VisibilityProperty, activedEditingModeBinding);
}
return _selectionAdorner;
}
}
// InkCanvasFeedbackAdorner
internal InkCanvasFeedbackAdorner(InkCanvas inkCanvas)
: base((inkCanvas != null ? inkCanvas.InnerCanvas : null))
{...}
最后,我們看下對選擇框進行的操作是如何實作的,選擇后會激活SelectionEditingBehavior,在其OnActivate方法中,系結了SelectionAdorner的MouseMove/MouseUp/LostMouseCapture事件,并呼叫InkCanvasSelection.StartFeedbackAdorner()方法對FeedbackAdorner進行初始化,將其添加到AdornerLayer中,然后通過回應MouseMove,呼叫InkSelection.UpdateFeedbackAdorner()方法更新FeedbackAdorner的位置,最后在MouseUp回應中釋放FeedbackAdorner,刪減代碼如下,具體的實作邏輯可以看WPF原始碼,
protected override void OnActive()
{
// ...
InkCanvas.InkCanvasSelection.StartFeedbackAdorner(_selectionRect, _hitResult);
InkCanvas.SelectionAdorner.AddHandler(Mouse.MouseUpEvent, new MouseButtonEventHandler(OnMouseUp));
InkCanvas.SelectionAdorner.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnMouseMove));
InkCanvas.SelectionAdorner.AddHandler(Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture));
}
private void onm ouseMove(object sender, MouseEventArgs args)
{
// ...
InkCanvas.InkCanvasSelection.UpdateFeedbackAdorner(newRect);
// ...
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/271808.html
標籤:WPF
上一篇:【翻譯】WPF 中附加行為的介紹 Introduction to Attached Behaviors in WPF
