首页 > 其他分享 >wpf inkcanvas 多点触控 解决部分屏幕画笔笔迹在绘制时会多出一段的问题

wpf inkcanvas 多点触控 解决部分屏幕画笔笔迹在绘制时会多出一段的问题

时间:2024-09-10 14:26:36浏览次数:11  
标签:inkCanvas inkcanvas 触控 Device base ._ device wpf DefaultDrawingAttributes

添加一个支持多点触摸的画布

/// <summary>
    /// 支持多点触摸的InkCanvas
    /// </summary
    public class MultiTouchCanvas : FrameworkElement
    {
        private InkCanvasProxy _inkCanvas = new InkCanvasProxy();
        private Grid transparentOverlay = new Grid();
        private StrokeType _strokeType = StrokeType.Stroke;
        private Dictionary<object, StrokeCollection> _strokes = new Dictionary<object, StrokeCollection>();
        private Dictionary<object, Stroke> _currentStroke = new Dictionary<object, Stroke>();

        private Color _penColor = Colors.Green;
        public Color PenColor{
            get{
                return this._inkCanvas.DefaultDrawingAttributes.Color;
            }
            set{
                this._penColor = value;
                this._inkCanvas.DefaultDrawingAttributes.Color = value;
                if (this.PenType == StrokeType.HighlighterStroke)
                {
                    value.ScA = System.Convert.ToSingle(0.5);
                    this._inkCanvas.DefaultDrawingAttributes.Color = value;
                }
            }
        }

        private double _penWidth = 4.0;
        public double PenWidth
        {
            get { return this._penWidth; }
            set
            {
                this._penWidth = value;
                this._inkCanvas.DefaultDrawingAttributes.Width = value;
                this._inkCanvas.DefaultDrawingAttributes.Height = value;
            }
        }

        private double _eraseWidth = 30.0;
        public double EraseWidth
        {
            get
            {
                return this._eraseWidth;
            }
            set
            {
                this._eraseWidth = value;
                this._inkCanvas.DefaultDrawingAttributes.Height = value;
                this._inkCanvas.DefaultDrawingAttributes.Width = value;
            }
        }

        public StrokeType PenType
        {
            get { return this._strokeType; }
            set
            {
                this._strokeType = value;
                if (this._strokeType == StrokeType.Stroke || this._strokeType == StrokeType.HighlighterStroke)
                {
                    this._inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
                    this._inkCanvas.DefaultDrawingAttributes.Width = this.PenWidth;
                    this._inkCanvas.DefaultDrawingAttributes.Height = this.PenWidth;
                    this._inkCanvas.DefaultDrawingAttributes.Color = this.PenColor;
                }

                if (this._strokeType == StrokeType.HighlighterStroke)
                {
                    Color dColor = this.PenColor;
                    dColor.ScA = System.Convert.ToSingle(0.5);
                    this._inkCanvas.DefaultDrawingAttributes.Color = dColor;
                }

                if (this._strokeType == StrokeType.EraseByPoint)
                {
                    this._inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
                    this._inkCanvas.DefaultDrawingAttributes.Width = this.EraseWidth;
                    this._inkCanvas.DefaultDrawingAttributes.Height = this.EraseWidth;
                }
            }
        }

        public Brush Background
        {
            get { return this._inkCanvas.Background; }
            set
            {
                this._inkCanvas.Background = value;
            }
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return 2; //grid + inkcanvas
            }
        }
            
        public MultiTouchCanvas(){
            base.IsManipulationEnabled = true;
            this.transparentOverlay.Background = Brushes.Transparent;
            base.IsEnabled =true;

            this.InitInkCanvasPropertys();
            this._inkCanvas.DefaultDrawingAttributes = new DrawingAttributes();
            this._inkCanvas.DefaultDrawingAttributes.Color = Colors.Green;
            this._inkCanvas.DefaultDrawingAttributes.Width = 4;
            this._inkCanvas.DefaultDrawingAttributes.Height = 4;

            this.PenType = StrokeType.Stroke;
        }

        public void ClearStrokes(object device)
        {
            if (this._strokes.ContainsKey(device) && this._inkCanvas.Strokes != null && this._inkCanvas.Strokes.Count > 0)
            {
                StrokeCollection sc = this._strokes[device];
                this._inkCanvas.Strokes.Remove(sc);
                this._strokes.Remove(device);
            }
        }

        public StrokeCollection GetStrokes(object device)
        {
            return this._strokes.ContainsKey(device) ? this._strokes[device] : null;
        }

        #region Event handle
        protected override void OnPreviewTouchDown(TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(this);
            if (this._inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
            {
                this._startStroke(e.Device, tp.Position);
            }
            else
            {
                if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
                {
                    this._removeStroke(e.Device, tp.Position);
                }
            }

            e.Handled = true;
            base.Focusable = true;
            base.Focus();
            base.Focusable = false;
            e.TouchDevice.Capture(this);
        }

        protected override void OnPreviewTouchMove(TouchEventArgs e)
        {
            _handleTouchMove(e);
        }

        protected override void OnTouchUp(TouchEventArgs e)
        {
            e.TouchDevice.Capture(null); //
        }

        protected override void onm ouseDown(MouseButtonEventArgs e)
        {
            if (base.Visibility == System.Windows.Visibility.Visible)
            {
                if (this._inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
                {
                    this._startStroke(e.Device, e.GetPosition(this));
                }
                else
                {
                    if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
                    {
                        this._removeStroke(e.Device, e.GetPosition(this));
                    }
                }

                e.MouseDevice.Capture(this);
            }
        }

        protected override void onm ouseMove(MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
                {
                    this._removeStroke(e.Device, e.GetPosition(this));
                    return;
                }
                if (this._strokes.ContainsKey(e.Device) && this._currentStroke.ContainsKey(e.Device))
                {
                    this._addPointToStroke(e.Device, e.GetPosition(this));
                }
                else
                {
                    this._startStroke(e.Device, e.GetPosition(this));
                }
            }
        }

        protected override void onm ouseUp(MouseButtonEventArgs e)
        {
            e.MouseDevice.Capture(null);
        }
        #endregion

        protected override Visual GetVisualChild(int index)
        {
            switch (index)
            {
                case 0: return this._inkCanvas;
                case 1: return this.transparentOverlay;
                default:
                    throw new ArgumentOutOfRangeException("index");
            }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            this._inkCanvas.Measure(availableSize);
            this.transparentOverlay.Measure(availableSize);
            return this._inkCanvas.DesiredSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this._inkCanvas.Arrange(new Rect(finalSize));
            this.transparentOverlay.Arrange(new Rect(finalSize));
            return base.ArrangeOverride(finalSize);

        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            base.AddVisualChild(this._inkCanvas);
            base.AddVisualChild(this.transparentOverlay);
        }
        private  void _handleTouchMove(TouchEventArgs e)
        {
            if(this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint){
                this._removeStroke(e.Device , e.GetTouchPoint(this).Position);
                e.Handled = true;
                return ;
            }

            if(this._strokes.ContainsKey(e.Device) && this._currentStroke.ContainsKey(e.Device))
            {
                Stroke stroke = this._currentStroke[e.Device];
                StylusPointCollection sps = stroke.StylusPoints;
                if(sps != null){
                    TouchPoint tp = e.GetTouchPoint(this);
                    Point p = tp.Position;
                    this._addPointToStroke( e.Device , p);
                }
            }
            else
            {
                TouchPoint tp = e.GetTouchPoint(this);
                this._startStroke( e.Device ,tp.Position);
            }
            e.Handled = true;
        }

        private void _addPointToStroke(object device, Point position)
        {
            Stroke stroke = this._currentStroke[device];
            if (stroke != null)
            {
                StylusPointCollection spc = stroke.StylusPoints;
                if (spc != null)
                {
                    spc.Add(new StylusPoint(position.X, position.Y, 0.5f));
                }
            }
        }
            
        private void _removeStroke(object device, Point position)
        {
            for (int i = 0; i < this._inkCanvas.Strokes.Count; i++)
            {
                Stroke stroke = this._inkCanvas.Strokes[i];
                StrokeCollection sc = stroke.GetEraseResult(new Rect(position.X, position.Y, this._inkCanvas.DefaultDrawingAttributes.Width, this._inkCanvas.DefaultDrawingAttributes.Height));
                this._inkCanvas.Strokes.Replace(stroke, sc);
            }
        }

        private void _startStroke(object device, Point inputPosition)
        {
            StylusPointCollection stylusPointCollection = new StylusPointCollection();
            stylusPointCollection.Add(new StylusPoint(inputPosition.X, inputPosition.Y, 0.5f));

            if (stylusPointCollection.Count > 0)
            {
                Stroke stroke = new Stroke(stylusPointCollection);
                stroke.DrawingAttributes.Width = this._inkCanvas.DefaultDrawingAttributes.Width;
                stroke.DrawingAttributes.Height = this._inkCanvas.DefaultDrawingAttributes.Height;
                stroke.DrawingAttributes.Color = this._inkCanvas.DefaultDrawingAttributes.Color;
                this._inkCanvas.Strokes.Add(stroke);//添加到canvas
                if (this._currentStroke.ContainsKey(device))
                {
                    this._currentStroke.Remove(device);
                }
                this._currentStroke.Add(device, stroke);

                if (this._strokes.ContainsKey(device))
                {
                    this._strokes[device].Add(this._currentStroke[device]);
                    return;
                }

                this._strokes.Add(device, new StrokeCollection { this._currentStroke[device] });
            }
        }

        private void InitInkCanvasPropertys(){
            this._inkCanvas.Focusable = base.Focusable;
            this._inkCanvas.Background = Brushes.Transparent;
            this._inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
            this._inkCanvas.UseCustomCursor = true;
        }
    }

添加辅助的代理类型

public enum StrokeType
    {
        Stroke,
        HighlighterStroke,
        EraseByPoint
    }

    public class InkCanvasProxy : InkCanvas
    {
        public InkCanvasProxy()
            : base()
        {
           // base.IsHitTestVisible = false;
          //  base.StylusPlugIns.Remove(base.DynamicRenderer);
        }
    }

测试

<Window x:Class="Metro.G.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="600"
        xmlns:ll="clr-namespace:Metro.G">
    <Canvas Name="container" >
        <ll:MultiTouchCanvas Width="600" Height="600" x:Name="_canvas"></ll:MultiTouchCanvas>
   </Canvas>
</Window>

[参考]
多点触摸画板(MultiTouchCanvas)

标签:inkCanvas,inkcanvas,触控,Device,base,._,device,wpf,DefaultDrawingAttributes
From: https://www.cnblogs.com/ives/p/18406324

相关文章

  • WPF DataGridTemplateColumn.CellTemplate Command CommandParameter
    <DataGridTemplateColumnHeader="Image"><DataGridTemplateColumn.CellTemplate><DataTemplate><ButtonCommand="{BindingDataContext.EnterCmd,RelativeSource={RelativeSourceFindAn......
  • 不可不知的WPF几何图形(Geometry)
    在软件行业,经常会听到一句话“文不如表,表不如图”说明了图形在软件应用中的重要性。同样在WPF开发中,为了程序美观或者业务需要,经常会用到各种个样的图形。今天以一些简单的小例子,简述WPF开发中几何图形(Geometry)相关内容,仅供学习分享使用,如有不足之处,还请指正。 什么是几何图形(G......
  • WPF Generic eventhandler for various event
    publicMainWindow(){InitializeComponent();this.AddHandler(ListBox.SelectionChangedEvent,newSelectionChangedEventHandler(GenericHandler));this.AddHandler(Button.ClickEvent,newRoutedEventHandler(GenericHandler));}......
  • .NET 8 + WPF 企业级工作流系统
    合集-.NET开源工具(11) 1..NET开源快捷的数据库文档查询和生成工具07-312..NET结果与错误处理利器FluentResults08-013..NET+WPF桌面快速启动工具GeekDesk08-194.Gradio.NET支持.NET8简化Web应用开发08-265..NET开源实时监控系统-WatchDog08-276.实用接地......
  • WPF ListBox ListBoxItem Selected background style
    <Windowx:Class="WpfApp340.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft......
  • 【转】[C#][WPF] 旋转的播放按钮
    转自:http://www.dmskin.com运行后播放按钮就会一直旋转,而暂停按钮不动。<Windowx:Class="WPF.Views.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quo......
  • .NET 8 + WPF 企业级工作流系统
    前言推荐一款基于.NET8、WPF、Prism.DryIoc、MVVM设计模式、Blazor以及MySQL数据库构建的企业级工作流系统的WPF客户端框架-AIStudio.Wpf.AClient6.0。项目介绍框架采用了Prism框架来实现MVVM模式,不仅简化了MVVM的典型应用场景,还充分利用了依赖注入(DI)、消息传递以及容......
  • DevExpress WPF中文教程:如何解决排序、过滤遇到的常见问题?(一)
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。无论是Office办公软件的衍伸产品,还是以数据为中心......
  • WPF Customize Button ControlTemplate TextBlock
    //xaml<UserControlx:Class="WpfApp332.BtnTbk"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="......
  • WPF UniformGrid contain children auto resize
    //xaml<Windowx:Class="WpfApp332.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......