WPFヘルパー:FreeDraw.cs – Canvas上にフリーハンドで線を引くヘルパー(Attached Property)

コンピュータ

FreeDraw.cs

using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Maywork.WPF.Helpers;

public static class FreeDraw
{
    // Canvasごとの内部状態
    class DrawState
    {
        public bool IsDrawing;
        public PathFigure? Figure;
        public PathGeometry? Geometry;
        public Path? Path;
    }

    // Canvas → State
    static readonly ConditionalWeakTable<Canvas, DrawState> states = new();

    static DrawState GetState(Canvas canvas)
        => states.GetOrCreateValue(canvas);

    // 描画モードON/OFF
    public static readonly DependencyProperty DrawEnabledProperty =
        DependencyProperty.RegisterAttached(
            "DrawEnabled",
            typeof(bool),
            typeof(FreeDraw),
            new PropertyMetadata(false, OnChanged));

    public static void SetDrawEnabled(UIElement element, bool value)
        => element.SetValue(DrawEnabledProperty, value);

    public static bool GetDrawEnabled(UIElement element)
        => (bool)element.GetValue(DrawEnabledProperty);

    static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not Canvas canvas)
            return;

        bool enabled = (bool)e.NewValue;

        if (enabled)
        {
            canvas.MouseDown += MouseDown;
            canvas.MouseMove += MouseMove;
            canvas.MouseUp += MouseUp;
        }
        else
        {
            canvas.MouseDown -= MouseDown;
            canvas.MouseMove -= MouseMove;
            canvas.MouseUp -= MouseUp;
        }
    }

    static void MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (sender is not Canvas canvas)
            return;

        var state = GetState(canvas);

        state.IsDrawing = true;

        Point p = e.GetPosition(canvas);

        state.Figure = new PathFigure
        {
            StartPoint = p
        };

        state.Geometry = new PathGeometry();
        state.Geometry.Figures.Add(state.Figure);

        state.Path = new Path
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            Data = state.Geometry
        };

        canvas.Children.Add(state.Path);

        canvas.CaptureMouse();
    }

    static void MouseMove(object sender, MouseEventArgs e)
    {
        if (sender is not Canvas canvas)
            return;

        var state = GetState(canvas);

        if (!state.IsDrawing || state.Figure is null)
            return;

        Point p = e.GetPosition(canvas);

        state.Figure.Segments.Add(new LineSegment(p, true));
    }

    static void MouseUp(object sender, MouseButtonEventArgs e)
    {
        if (sender is not Canvas canvas)
            return;

        var state = GetState(canvas);

        state.IsDrawing = false;

        canvas.ReleaseMouseCapture();
    }
}
/*
// 使い方

XAML
xmlns:h="clr-namespace:Maywork.WPF.Helpers"

<Canvas
    Background="White"
    h:FreeDraw.DrawEnabled="True"/>

*/

Download

コメント