[update] IccInkCanvas with IccBoard

This commit is contained in:
Dubi906w 2024-09-21 22:07:30 +08:00
parent a6775bd324
commit ab24d8240e
14 changed files with 742 additions and 42 deletions

View File

@ -94,5 +94,11 @@
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
</ItemGroup> </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" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -4,9 +4,17 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:InkCanvasForClass.IccInkCanvas.Demo" xmlns:local="clr-namespace:InkCanvasForClass.IccInkCanvas.Demo"
xmlns:iccInkCanvas="clr-namespace:InkCanvasForClass.IccInkCanvas;assembly=InkCanvasForClass.IccInkCanvas"
mc:Ignorable="d" mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"> Title="MainWindow" Height="850" Width="800">
<Grid> <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> </Grid>
</Window> </Window>

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,6 +13,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using InkCanvasForClass.IccInkCanvas.Settings;
namespace InkCanvasForClass.IccInkCanvas.Demo { namespace InkCanvasForClass.IccInkCanvas.Demo {
/// <summary> /// <summary>
@ -21,5 +23,12 @@ namespace InkCanvasForClass.IccInkCanvas.Demo {
public MainWindow() { public MainWindow() {
InitializeComponent(); 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;
}
} }
} }

View 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>

View 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;
}
}
}

View 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;
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View 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);
}
}
}
}

View File

@ -48,9 +48,12 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="IccInkCanvas.xaml.cs"> <Compile Include="Settings\BoardSettings.cs" />
<DependentUpon>IccInkCanvas.xaml</DependentUpon> <Compile Include="IccBoard.xaml.cs">
<DependentUpon>IccBoard.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="IccInkCanvas.cs" />
<Compile Include="IccStroke.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -64,6 +67,8 @@
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile> </Compile>
<Compile Include="Settings\EditingMode.cs" />
<Compile Include="Settings\Nib.cs" />
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
@ -74,10 +79,11 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="IccInkCanvas.xaml"> <Page Include="IccBoard.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -0,0 +1,6 @@
# IccInkCanvas
`InkCanvasForClass.IccInkCanvas`
## 方法

View 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;
}
}

View 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
}
}

View 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
}
}