[update] IccInkCanvas with IccBoard
This commit is contained in:
parent
a6775bd324
commit
ab24d8240e
@ -94,5 +94,11 @@
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\InkCanvasForClass.IccInkCanvas\InkCanvasForClass.IccInkCanvas.csproj">
|
||||
<Project>{43929d8f-5630-4786-b75d-e203ea3e992f}</Project>
|
||||
<Name>InkCanvasForClass.IccInkCanvas</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -4,9 +4,17 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:InkCanvasForClass.IccInkCanvas.Demo"
|
||||
xmlns:iccInkCanvas="clr-namespace:InkCanvasForClass.IccInkCanvas;assembly=InkCanvasForClass.IccInkCanvas"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
Title="MainWindow" Height="850" Width="800">
|
||||
<Grid>
|
||||
|
||||
<StackPanel Orientation="Vertical" Margin="8">
|
||||
<TextBlock FontSize="24">IccInkCanvasDemo</TextBlock>
|
||||
<iccInkCanvas:IccBoard Name="IccBoard" Width="512" Height="512"/>
|
||||
<WrapPanel Orientation="Horizontal">
|
||||
<Button Content="EditingMode = None" Click="EditingModeChangeToNone_ButtonClick"/>
|
||||
<Button Content="EditingMode = Writing" Click="EditingModeChangeToWriting_ButtonClick"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,6 +13,7 @@ using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using InkCanvasForClass.IccInkCanvas.Settings;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas.Demo {
|
||||
/// <summary>
|
||||
@ -21,5 +23,12 @@ namespace InkCanvasForClass.IccInkCanvas.Demo {
|
||||
public MainWindow() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void EditingModeChangeToNone_ButtonClick(object sender, RoutedEventArgs e) {
|
||||
IccBoard.EditingMode = EditingMode.None;
|
||||
}
|
||||
private void EditingModeChangeToWriting_ButtonClick(object sender, RoutedEventArgs e) {
|
||||
IccBoard.EditingMode = EditingMode.Writing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
InkCanvasForClass.IccInkCanvas/IccBoard.xaml
Normal file
16
InkCanvasForClass.IccInkCanvas/IccBoard.xaml
Normal file
@ -0,0 +1,16 @@
|
||||
<UserControl x:Class="InkCanvasForClass.IccInkCanvas.IccBoard"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:InkCanvasForClass.IccInkCanvas"
|
||||
mc:Ignorable="d">
|
||||
<Grid Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualWidth}"
|
||||
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualHeight}">
|
||||
<Canvas Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualWidth}"
|
||||
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualHeight}"
|
||||
ClipToBounds="True" x:Name="InkCanvasHostCanvas" x:FieldModifier="private">
|
||||
<local:IccInkCanvas x:Name="InkCanvas" x:FieldModifier="private" Loaded="IccInkCanvas_Loaded"/>
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</UserControl>
|
129
InkCanvasForClass.IccInkCanvas/IccBoard.xaml.cs
Normal file
129
InkCanvasForClass.IccInkCanvas/IccBoard.xaml.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using InkCanvasForClass.IccInkCanvas.Settings;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas {
|
||||
/// <summary>
|
||||
/// IccBoard.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class IccBoard : UserControl {
|
||||
|
||||
public BoardSettings BoardSettings { get; private set; } = new BoardSettings();
|
||||
|
||||
#region Properties
|
||||
|
||||
private bool _isEditingModePropertyAccessdByCodeBehind = false;
|
||||
public EditingMode EditingMode {
|
||||
get => (EditingMode)GetValue(EditingModeProperty);
|
||||
set {
|
||||
if (value == EditingMode.ShapeDrawing) throw new Exception("EditingMode.ShapeDrawing 不能被用户手动设定");
|
||||
_isEditingModePropertyAccessdByCodeBehind = true;
|
||||
SetValue(EditingModeProperty, value);
|
||||
_isEditingModePropertyAccessdByCodeBehind = false;
|
||||
UpdateEditingMode();
|
||||
}
|
||||
}
|
||||
public static readonly System.Windows.DependencyProperty EditingModeProperty =
|
||||
System.Windows.DependencyProperty.Register(
|
||||
nameof(EditingMode),
|
||||
typeof(EditingMode),
|
||||
typeof(IccBoard),
|
||||
new FrameworkPropertyMetadata(EditingMode.Writing,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
propertyChangedCallback: OnEditingModePropertyChanged));
|
||||
private static void OnEditingModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var iccboard = d as IccBoard;
|
||||
if (iccboard != null && iccboard._isEditingModePropertyAccessdByCodeBehind) return;
|
||||
iccboard?.UpdateEditingMode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EditingMode
|
||||
|
||||
private void UpdateEditingMode() {
|
||||
if (EditingMode == EditingMode.None || EditingMode == EditingMode.NoneWithHitTest)
|
||||
InkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
IsHitTestVisible = EditingMode != EditingMode.None;
|
||||
if (EditingMode == EditingMode.Writing) InkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
private static readonly System.Windows.RoutedEvent EditingModeChangedEvent = EventManager.RegisterRoutedEvent(
|
||||
name: "EditingModeChanged",
|
||||
routingStrategy: RoutingStrategy.Bubble,
|
||||
handlerType: typeof(System.Windows.RoutedEventHandler),
|
||||
ownerType: typeof(IccBoard));
|
||||
private static readonly System.Windows.RoutedEvent ActiveEditingModeChangedEvent = EventManager.RegisterRoutedEvent(
|
||||
name: "ActiveEditingModeChanged",
|
||||
routingStrategy: RoutingStrategy.Bubble,
|
||||
handlerType: typeof(System.Windows.RoutedEventHandler),
|
||||
ownerType: typeof(IccBoard));
|
||||
|
||||
public event System.Windows.RoutedEventHandler EditingModeChanged {
|
||||
add => AddHandler(EditingModeChangedEvent, value);
|
||||
remove => RemoveHandler(EditingModeChangedEvent, value);
|
||||
}
|
||||
public event System.Windows.RoutedEventHandler ActiveEditingModeChanged {
|
||||
add => AddHandler(ActiveEditingModeChangedEvent, value);
|
||||
remove => RemoveHandler(ActiveEditingModeChangedEvent, value);
|
||||
}
|
||||
|
||||
private void RaiseEditingModeChangedEvent() {
|
||||
RoutedEventArgs routedEventArgs = new RoutedEventArgs(routedEvent: EditingModeChangedEvent);
|
||||
RaiseEvent(routedEventArgs);
|
||||
}
|
||||
private void RaiseActiveEditingModeChangedEvent() {
|
||||
RoutedEventArgs routedEventArgs = new RoutedEventArgs(routedEvent: ActiveEditingModeChangedEvent);
|
||||
RaiseEvent(routedEventArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IccBoard() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void IccInkCanvas_Loaded(object sender, RoutedEventArgs e) {
|
||||
var ic = (IccInkCanvas)sender;
|
||||
|
||||
// 启动时自动修改 InkCanvas 的大小
|
||||
var screenW = SystemParameters.PrimaryScreenWidth;
|
||||
var screenH = SystemParameters.PrimaryScreenHeight;
|
||||
|
||||
var fullWidth = screenW * 257;
|
||||
var fullHeight = screenH * 417;
|
||||
|
||||
var left = 0 - screenW * 128;
|
||||
var top = 0 - screenH * 208;
|
||||
|
||||
ic.Width = fullWidth;
|
||||
ic.Height = fullHeight;
|
||||
Canvas.SetLeft(ic, left);
|
||||
Canvas.SetTop(ic, top);
|
||||
|
||||
ic.DefaultDrawingAttributes.Width = BoardSettings.NibWidth;
|
||||
ic.DefaultDrawingAttributes.Height = BoardSettings.NibHeight;
|
||||
ic.DefaultDrawingAttributes.Color = BoardSettings.NibColor;
|
||||
|
||||
ic.BoardSettings = BoardSettings;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
211
InkCanvasForClass.IccInkCanvas/IccInkCanvas.cs
Normal file
211
InkCanvasForClass.IccInkCanvas/IccInkCanvas.cs
Normal file
@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input.StylusPlugIns;
|
||||
using System.Windows.Media;
|
||||
using InkCanvasForClass.IccInkCanvas.Settings;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas {
|
||||
class CustomDynamicRenderer : DynamicRenderer {
|
||||
|
||||
private Point prevPoint;
|
||||
|
||||
private IccInkCanvas _inkCanvas;
|
||||
|
||||
private List<StylusPoint> pointsList = new List<StylusPoint>();
|
||||
|
||||
private void ClearPointsList() {
|
||||
pointsList.Clear();
|
||||
}
|
||||
|
||||
private void PushPoint(StylusPoint point) {
|
||||
pointsList.Add(point);
|
||||
if (pointsList.Count > 15) {
|
||||
pointsList.RemoveRange(0,pointsList.Count - 15);
|
||||
}
|
||||
}
|
||||
|
||||
public CustomDynamicRenderer(IccInkCanvas iccInkCanvas) {
|
||||
_inkCanvas = iccInkCanvas;
|
||||
}
|
||||
|
||||
protected override void OnStylusDown(RawStylusInput rawStylusInput) {
|
||||
prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
|
||||
ClearPointsList();
|
||||
var pts = rawStylusInput.GetStylusPoints();
|
||||
if (pts.Count == 1) {
|
||||
PushPoint(pts.Single());
|
||||
} else if (pts.Count > 1) {
|
||||
PushPoint(pts[pts.Count-1]);
|
||||
}
|
||||
Trace.WriteLine(pointsList.Count);
|
||||
base.OnStylusDown(rawStylusInput);
|
||||
}
|
||||
|
||||
protected override void OnStylusMove(RawStylusInput rawStylusInput) {
|
||||
base.OnStylusMove(rawStylusInput);
|
||||
var pts = rawStylusInput.GetStylusPoints();
|
||||
if (pts.Count == 1) {
|
||||
PushPoint(pts.Single());
|
||||
} else if (pts.Count > 1) {
|
||||
PushPoint(pts[pts.Count-1]);
|
||||
}
|
||||
Trace.WriteLine(pointsList.Count);
|
||||
}
|
||||
|
||||
protected override void OnDraw(DrawingContext drawingContext,
|
||||
StylusPointCollection stylusPoints,
|
||||
Geometry geometry, Brush fillBrush) {
|
||||
if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful) {
|
||||
try {
|
||||
var sp = new StylusPointCollection();
|
||||
var n = pointsList.Count - 1;
|
||||
var pressure = 0.1;
|
||||
var x = 10;
|
||||
if (n == 1) return;
|
||||
if (n >= x) {
|
||||
for (var i = 0; i < n - x; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)0.5;
|
||||
point.X = pointsList[i].X;
|
||||
point.Y = pointsList[i].Y;
|
||||
sp.Add(point);
|
||||
}
|
||||
|
||||
for (var i = n - x; i <= n; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
|
||||
point.X = pointsList[i].X;
|
||||
point.Y = pointsList[i].Y;
|
||||
sp.Add(point);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i <= n; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
|
||||
point.X = pointsList[i].X;
|
||||
point.Y = pointsList[i].Y;
|
||||
sp.Add(point);
|
||||
}
|
||||
}
|
||||
|
||||
var da = DrawingAttributes.Clone();
|
||||
da.Width -= 0.5;
|
||||
da.Height -= 0.5;
|
||||
var stk = new Stroke(sp, da);
|
||||
stk.Draw(drawingContext);
|
||||
} catch {}
|
||||
} else {
|
||||
base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class IccInkCanvas : InkCanvas {
|
||||
|
||||
CustomDynamicRenderer customDynamicRenderer;
|
||||
|
||||
public BoardSettings BoardSettings { get; set; } = new BoardSettings();
|
||||
|
||||
public IccInkCanvas() {
|
||||
customDynamicRenderer = new CustomDynamicRenderer(this);
|
||||
DynamicRenderer = customDynamicRenderer;
|
||||
|
||||
// 通过反射移除InkCanvas自带的默认 Delete按键事件
|
||||
var commandBindingsField =
|
||||
typeof(CommandManager).GetField("_classCommandBindings", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var bnds = commandBindingsField.GetValue(null) as HybridDictionary;
|
||||
var inkCanvasBindings = bnds[typeof(InkCanvas)] as CommandBindingCollection;
|
||||
var enumerator = inkCanvasBindings.GetEnumerator();
|
||||
while (enumerator.MoveNext()) {
|
||||
var item = (CommandBinding)enumerator.Current;
|
||||
if (item.Command == ApplicationCommands.Delete) {
|
||||
var executedField =
|
||||
typeof(CommandBinding).GetField("Executed", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var canExecuteField =
|
||||
typeof(CommandBinding).GetField("CanExecute", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
executedField.SetValue(item, new ExecutedRoutedEventHandler((sender, args) => { }));
|
||||
canExecuteField.SetValue(item, new CanExecuteRoutedEventHandler((sender, args) => { }));
|
||||
}
|
||||
}
|
||||
|
||||
// 为IccInkCanvas注册自定义的 Delete按键Command并Invoke OnDeleteCommandFired。
|
||||
CommandManager.RegisterClassCommandBinding(typeof(IccInkCanvas), new CommandBinding(ApplicationCommands.Delete,
|
||||
(sender, args) => {
|
||||
DeleteKeyCommandFired?.Invoke(this, new RoutedEventArgs());
|
||||
}, (sender, args) => {
|
||||
args.CanExecute = GetSelectedStrokes().Count != 0;
|
||||
}));
|
||||
}
|
||||
|
||||
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e) {
|
||||
IccStroke customStroke = new IccStroke(e.Stroke.StylusPoints, e.Stroke.DrawingAttributes);
|
||||
if (e.Stroke is IccStroke) {
|
||||
this.Strokes.Add(e.Stroke);
|
||||
} else {
|
||||
this.Strokes.Remove(e.Stroke);
|
||||
this.Strokes.Add(customStroke);
|
||||
}
|
||||
|
||||
if (BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful) {
|
||||
try {
|
||||
var stylusPoints = new StylusPointCollection();
|
||||
var n = customStroke.StylusPoints.Count - 1;
|
||||
var pressure = 0.1;
|
||||
var x = 10;
|
||||
if (n == 1) return;
|
||||
if (n >= x) {
|
||||
for (var i = 0; i < n - x; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)0.5;
|
||||
point.X = customStroke.StylusPoints[i].X;
|
||||
point.Y = customStroke.StylusPoints[i].Y;
|
||||
stylusPoints.Add(point);
|
||||
}
|
||||
|
||||
for (var i = n - x; i <= n; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
|
||||
point.X = customStroke.StylusPoints[i].X;
|
||||
point.Y = customStroke.StylusPoints[i].Y;
|
||||
stylusPoints.Add(point);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i <= n; i++) {
|
||||
var point = new StylusPoint();
|
||||
|
||||
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
|
||||
point.X = customStroke.StylusPoints[i].X;
|
||||
point.Y = customStroke.StylusPoints[i].Y;
|
||||
stylusPoints.Add(point);
|
||||
}
|
||||
}
|
||||
|
||||
customStroke.StylusPoints = stylusPoints;
|
||||
} catch { }
|
||||
}
|
||||
|
||||
InkCanvasStrokeCollectedEventArgs args =
|
||||
new InkCanvasStrokeCollectedEventArgs(customStroke);
|
||||
base.OnStrokeCollected(args);
|
||||
}
|
||||
|
||||
public event EventHandler<RoutedEventArgs> DeleteKeyCommandFired;
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<UserControl x:Class="InkCanvasForClass.IccInkCanvas.IccInkCanvas"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:InkCanvasForClass.IccInkCanvas"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas {
|
||||
/// <summary>
|
||||
/// IccInkCanvas.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class IccInkCanvas : UserControl {
|
||||
public IccInkCanvas() {
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
214
InkCanvasForClass.IccInkCanvas/IccStroke.cs
Normal file
214
InkCanvasForClass.IccInkCanvas/IccStroke.cs
Normal file
@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas {
|
||||
|
||||
static class ToPointsHelper {
|
||||
public static Point[] ToPoints(this IEnumerable<StylusPoint> stylusPoints)
|
||||
{
|
||||
List<Point> pointList = new List<Point>();
|
||||
foreach (StylusPoint stylusPoint in stylusPoints)
|
||||
pointList.Add(stylusPoint.ToPoint());
|
||||
return pointList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ShapeDrawingType {
|
||||
Line,
|
||||
DottedLine,
|
||||
DashedLine,
|
||||
ArrowOneSide,
|
||||
ArrowTwoSide,
|
||||
Rectangle,
|
||||
Ellipse,
|
||||
PieEllipse,
|
||||
Triangle,
|
||||
RightTriangle,
|
||||
Diamond,
|
||||
Parallelogram,
|
||||
FourLine,
|
||||
Staff,
|
||||
Axis2D,
|
||||
Axis2DA,
|
||||
Axis2DB,
|
||||
Axis2DC,
|
||||
Axis3D,
|
||||
Hyperbola,
|
||||
HyperbolaF,
|
||||
Parabola,
|
||||
ParabolaA,
|
||||
ParabolaAF,
|
||||
Cylinder,
|
||||
Cube,
|
||||
Cone,
|
||||
EllipseC,
|
||||
RectangleC
|
||||
}
|
||||
|
||||
public static class ShapeDrawingHelper {
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的两个点计算角度
|
||||
/// </summary>
|
||||
/// <param name="firstPoint"></param>
|
||||
/// <param name="lastPoint"></param>
|
||||
/// <returns></returns>
|
||||
public static double CaculateRotateAngleByGivenTwoPoints(Point firstPoint, Point lastPoint) {
|
||||
var vec1 = new double[] {
|
||||
lastPoint.X - firstPoint.X ,
|
||||
lastPoint.Y - firstPoint.Y
|
||||
};
|
||||
var vec_base = new double[] { 0, firstPoint.Y };
|
||||
var cosine = (vec_base[0] * vec1[0] + vec_base[1] * vec1[1]) /
|
||||
(Math.Sqrt(Math.Pow(vec_base[0],2) + Math.Pow(vec_base[1],2)) *
|
||||
Math.Sqrt(Math.Pow(vec1[0],2) + Math.Pow(vec1[1],2)));
|
||||
var angle = Math.Acos(cosine);
|
||||
var isIn2And3Quadrant = lastPoint.X <= firstPoint.X;
|
||||
var rotateAngle = Math.Round(180 + 180 * (angle / Math.PI) * (isIn2And3Quadrant ? 1 : -1), 0);
|
||||
return rotateAngle;
|
||||
}
|
||||
|
||||
|
||||
public static List<Point> DistributePointsOnLine(Point start, Point end, double interval=16) {
|
||||
List<Point> points = new List<Point>();
|
||||
|
||||
double dx = end.X - start.X;
|
||||
double dy = end.Y - start.Y;
|
||||
double distance = Math.Sqrt(dx * dx + dy * dy);
|
||||
int numPoints = (int)(distance / interval);
|
||||
|
||||
for (int i = 0; i <= numPoints; i++) {
|
||||
double ratio = (interval * i) / distance;
|
||||
double x = start.X + ratio * dx;
|
||||
double y = start.Y + ratio * dy;
|
||||
points.Add(new Point(x, y));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
public class ArrowLineConfig {
|
||||
public int ArrowWidth { get; set; } = 20;
|
||||
public int ArrowHeight { get; set; } = 7;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IccStroke : Stroke {
|
||||
|
||||
public IccStroke(StylusPointCollection stylusPoints, DrawingAttributes drawingAttributes)
|
||||
: base(stylusPoints, drawingAttributes) { }
|
||||
|
||||
public static Guid StrokeShapeTypeGuid = new Guid("6537b29c-557f-487f-800b-cb30a8f1de78");
|
||||
public static Guid StrokeIsShapeGuid = new Guid("40eff5db-9346-4e42-bd46-7b0eb19d0018");
|
||||
|
||||
public StylusPointCollection RawStylusPointCollection { get; set; }
|
||||
|
||||
public ShapeDrawingHelper.ArrowLineConfig ArrowLineConfig { get; set; } =
|
||||
new ShapeDrawingHelper.ArrowLineConfig();
|
||||
|
||||
/// <summary>
|
||||
/// 根据这个属性判断当前 Stroke 是否是原始输入
|
||||
/// </summary>
|
||||
public bool IsRawStylusPoints = true;
|
||||
|
||||
/// <summary>
|
||||
/// 根据这个属性决定在绘制 Stroke 时是否需要在直线形状中,在两点构成直线上分布点,用于墨迹的范围框选。
|
||||
/// </summary>
|
||||
public bool IsDistributePointsOnLineShape = true;
|
||||
|
||||
/// <summary>
|
||||
/// 指示该墨迹是否来自一个完整墨迹被擦除后的一部分墨迹,仅用于形状墨迹。
|
||||
/// </summary>
|
||||
public bool IsErasedStrokePart = false;
|
||||
|
||||
// 自定义的墨迹渲染
|
||||
protected override void DrawCore(DrawingContext drawingContext,
|
||||
DrawingAttributes drawingAttributes) {
|
||||
if (!(this.ContainsPropertyData(StrokeIsShapeGuid) &&
|
||||
(bool)this.GetPropertyData(StrokeIsShapeGuid) == true)) {
|
||||
base.DrawCore(drawingContext, drawingAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.DashedLine ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.Line ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.DottedLine ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.ArrowOneSide ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.ArrowTwoSide) {
|
||||
if (StylusPoints.Count < 2) {
|
||||
base.DrawCore(drawingContext, drawingAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
var pts = new List<Point>(this.StylusPoints.ToPoints());
|
||||
if (IsDistributePointsOnLineShape && (
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.DashedLine ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.Line ||
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.DottedLine) && IsRawStylusPoints) {
|
||||
IsRawStylusPoints = false;
|
||||
RawStylusPointCollection = StylusPoints.Clone();
|
||||
var pointList = new List<Point> { new Point(StylusPoints[0].X, StylusPoints[0].Y) };
|
||||
pointList.AddRange(ShapeDrawingHelper.DistributePointsOnLine(new Point(StylusPoints[0].X, StylusPoints[0].Y),new Point(StylusPoints[1].X, StylusPoints[1].Y)));
|
||||
pointList.Add(new Point(StylusPoints[1].X, StylusPoints[1].Y));
|
||||
StylusPoints = new StylusPointCollection(pointList);
|
||||
}
|
||||
StreamGeometry geometry = new StreamGeometry();
|
||||
using (StreamGeometryContext ctx = geometry.Open()) {
|
||||
ctx.BeginFigure(pts[0], false , false);
|
||||
pts.RemoveAt(0);
|
||||
ctx.PolyLineTo(pts,true, true);
|
||||
}
|
||||
var pen = new Pen(new SolidColorBrush(DrawingAttributes.Color),
|
||||
(drawingAttributes.Width + drawingAttributes.Height) / 2) {
|
||||
DashCap = PenLineCap.Round,
|
||||
StartLineCap = PenLineCap.Round,
|
||||
EndLineCap = PenLineCap.Round,
|
||||
};
|
||||
if ((int)this.GetPropertyData(StrokeShapeTypeGuid) != (int)ShapeDrawingType.Line &&
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) != (int)ShapeDrawingType.ArrowOneSide &&
|
||||
(int)this.GetPropertyData(StrokeShapeTypeGuid) != (int)ShapeDrawingType.ArrowTwoSide)
|
||||
pen.DashStyle = (int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.DottedLine ? DashStyles.Dot : DashStyles.Dash;
|
||||
|
||||
if ((int)this.GetPropertyData(StrokeShapeTypeGuid) == (int)ShapeDrawingType.ArrowOneSide && IsRawStylusPoints) {
|
||||
IsRawStylusPoints = false;
|
||||
pts = new List<Point>(this.StylusPoints.ToPoints());
|
||||
RawStylusPointCollection = StylusPoints.Clone();
|
||||
double w = ArrowLineConfig.ArrowWidth, h = ArrowLineConfig.ArrowHeight;
|
||||
var theta = Math.Atan2(pts[0].Y - pts[1].Y, pts[0].X - pts[1].X);
|
||||
var sint = Math.Sin(theta);
|
||||
var cost = Math.Cos(theta);
|
||||
var pointList = new List<Point> {
|
||||
new Point(pts[0].X, pts[0].Y),
|
||||
};
|
||||
if (IsDistributePointsOnLineShape) pointList.AddRange(ShapeDrawingHelper.DistributePointsOnLine(new Point(pts[0].X, pts[0].Y),new Point(pts[1].X, pts[1].Y)));
|
||||
pointList.AddRange(new List<Point> {
|
||||
new Point(pts[1].X, pts[1].Y),
|
||||
new Point(pts[1].X + (w * cost - h * sint), pts[1].Y + (w * sint + h * cost)),
|
||||
new Point(pts[1].X, pts[1].Y),
|
||||
new Point(pts[1].X + (w * cost + h * sint), pts[1].Y - (h * cost - w * sint)),
|
||||
});
|
||||
StylusPoints = new StylusPointCollection(pointList);
|
||||
var _pts = new List<Point>(this.StylusPoints.ToPoints());
|
||||
using (StreamGeometryContext ctx = geometry.Open()) {
|
||||
ctx.BeginFigure(_pts[0], false , false);
|
||||
_pts.RemoveAt(0);
|
||||
ctx.PolyLineTo(_pts,true, true);
|
||||
}
|
||||
drawingContext.DrawGeometry(new SolidColorBrush(DrawingAttributes.Color),pen, geometry);
|
||||
return;
|
||||
|
||||
}
|
||||
drawingContext.DrawGeometry(new SolidColorBrush(Colors.Transparent),pen, geometry);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -48,9 +48,12 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IccInkCanvas.xaml.cs">
|
||||
<DependentUpon>IccInkCanvas.xaml</DependentUpon>
|
||||
<Compile Include="Settings\BoardSettings.cs" />
|
||||
<Compile Include="IccBoard.xaml.cs">
|
||||
<DependentUpon>IccBoard.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="IccInkCanvas.cs" />
|
||||
<Compile Include="IccStroke.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@ -64,6 +67,8 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Compile Include="Settings\EditingMode.cs" />
|
||||
<Compile Include="Settings\Nib.cs" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
@ -74,10 +79,11 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="IccInkCanvas.xaml">
|
||||
<Page Include="IccBoard.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
6
InkCanvasForClass.IccInkCanvas/README.md
Normal file
6
InkCanvasForClass.IccInkCanvas/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# IccInkCanvas
|
||||
|
||||
`InkCanvasForClass.IccInkCanvas`
|
||||
|
||||
## 方法
|
||||
|
42
InkCanvasForClass.IccInkCanvas/Settings/BoardSettings.cs
Normal file
42
InkCanvasForClass.IccInkCanvas/Settings/BoardSettings.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas.Settings {
|
||||
public class BoardSettings {
|
||||
public BoardSettings() {}
|
||||
|
||||
/// <summary>
|
||||
/// 笔尖长度
|
||||
/// </summary>
|
||||
public double NibWidth { get; set; } = 4.00;
|
||||
|
||||
/// <summary>
|
||||
/// 笔尖高度
|
||||
/// </summary>
|
||||
public double NibHeight { get; set; } = 4.00;
|
||||
|
||||
/// <summary>
|
||||
/// 笔尖大小,适合笔尖类型为普通笔时使用
|
||||
/// </summary>
|
||||
public double NibSize {
|
||||
get => (NibWidth + NibHeight) / 2;
|
||||
set => NibWidth = NibHeight = value;
|
||||
}
|
||||
|
||||
public NibType NibType { get; set; } = NibType.Default;
|
||||
|
||||
/// <summary>
|
||||
/// 笔尖颜色
|
||||
/// </summary>
|
||||
public Color NibColor { get; set; } = Colors.Black;
|
||||
|
||||
/// <summary>
|
||||
/// 笔锋样式类型,默认有笔锋
|
||||
/// </summary>
|
||||
public StrokeNibStyle StrokeNibStyle { get; set; } = StrokeNibStyle.Beautiful;
|
||||
}
|
||||
}
|
54
InkCanvasForClass.IccInkCanvas/Settings/EditingMode.cs
Normal file
54
InkCanvasForClass.IccInkCanvas/Settings/EditingMode.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas.Settings {
|
||||
|
||||
/// <summary>
|
||||
/// IccBoard的编辑模式
|
||||
/// </summary>
|
||||
public enum EditingMode {
|
||||
/// <summary>
|
||||
/// 仅显示墨迹,不会接收任何输入事件,没有HitTest
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 仅显示墨迹,不会接收任何输入事件,有HitTest
|
||||
/// </summary>
|
||||
NoneWithHitTest,
|
||||
/// <summary>
|
||||
/// 书写模式,该模式下允许临时切换到橡皮擦模式
|
||||
/// </summary>
|
||||
Writing,
|
||||
/// <summary>
|
||||
/// 墨迹擦模式
|
||||
/// </summary>
|
||||
StrokeErasing,
|
||||
/// <summary>
|
||||
/// 板擦模式
|
||||
/// </summary>
|
||||
GeometryErasing,
|
||||
/// <summary>
|
||||
/// 区域擦除模式
|
||||
/// </summary>
|
||||
AreaErasing,
|
||||
/// <summary>
|
||||
/// 墨迹选择模式
|
||||
/// </summary>
|
||||
Select,
|
||||
/// <summary>
|
||||
/// 仅显示墨迹,仅接受手势输入
|
||||
/// </summary>
|
||||
Gestures,
|
||||
/// <summary>
|
||||
/// 形状绘制模式,该模式不能被用户直接设置
|
||||
/// </summary>
|
||||
ShapeDrawing,
|
||||
/// <summary>
|
||||
/// 漫游模式
|
||||
/// </summary>
|
||||
RoamingMode
|
||||
}
|
||||
}
|
36
InkCanvasForClass.IccInkCanvas/Settings/Nib.cs
Normal file
36
InkCanvasForClass.IccInkCanvas/Settings/Nib.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace InkCanvasForClass.IccInkCanvas.Settings {
|
||||
|
||||
/// <summary>
|
||||
/// 定义了笔尖类型
|
||||
/// </summary>
|
||||
public enum NibType {
|
||||
/// <summary>
|
||||
/// 默认笔
|
||||
/// </summary>
|
||||
Default,
|
||||
/// <summary>
|
||||
/// 荧光笔
|
||||
/// </summary>
|
||||
Highlighter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 笔锋样式
|
||||
/// </summary>
|
||||
public enum StrokeNibStyle {
|
||||
/// <summary>
|
||||
/// 无笔锋
|
||||
/// </summary>
|
||||
Solid,
|
||||
/// <summary>
|
||||
/// 有笔锋,基于固定点集算法计算
|
||||
/// </summary>
|
||||
Beautiful
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user