MainPage 〜きまぐれ ぷろぐらま語録〜
 

2025年5月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27
28
29 30 31

最近の記事
Blog移転

エアポート2010を見た

花椒油

Steve Jobs,1955-2011

鉄球に無数のとげが付いた武器

M55→M42(T)が欲しい

健康診断done

回転寿司のルール

相模湖リゾート・プレジャー・フ ..

過去ログ 【表示/最小化】

広告欄

 

Blogを移転させました。
新Blogは次のURLです。
https://kerokero.org/wp/




【WPF】InkCanvasにアンドゥ機能を追加
※続きがあります→翌日の記事

InkCanvasにアンドゥ機能が欲しくて、帰宅後に習作で作ってみた。

ちなみに、InkCanvasは線を描くとStrokesに線が追加されるので、
線を消す方向のアンドゥは、それを操作するだけで結構簡単。

一方で、消しゴム的に消去すると、既存の線が分裂して、
Strokesに再登録されるので、前述の方法だと破綻してしまう。
そのため、消しゴムモードを交えてのアンドゥが結構やっかいっぽい。

そこで、線が追加されたときのイベントと、
消しゴムで分断されたときのイベントを使って、
線分を力業で保存してアンドゥを実装してみた。

コアの部分のコードはこんな:
public class UndoInkCanvas : InkCanvas {
    /// <summary>
    /// アンドゥバッファ
    /// </summary>
    private List<System.Windows.Ink.StrokeCollection> UndoBufferList = new List<System.Windows.Ink.StrokeCollection>();
    /// <summary>
    /// 直前が描画だったか、消しゴムだったかの列挙体
    /// </summary>
    private enum LAST_STROKE_TYPE {
        STROKE,
        ERASER
    };
    /// <summary>
    /// 直前が描画だったか、消しゴムだったかの判断をするためのフラグ
    /// </summary>
    private LAST_STROKE_TYPE LastStrokeType = LAST_STROKE_TYPE.STROKE;
    /// <summary>
    /// 静的コンストラクタ
    /// </summary>
    static UndoInkCanvas() {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(UndoInkCanvas), new FrameworkPropertyMetadata(typeof(UndoInkCanvas)));
    }
    /// <summary>
    /// 通常のコンストラクタ
    /// </summary>
    public UndoInkCanvas() {
        this.StrokeCollected += new InkCanvasStrokeCollectedEventHandler(UndoInkCanvas_StrokeCollected);
        this.StrokeErased += new RoutedEventHandler(UndoInkCanvas_StrokeErased);
    }
    /// <summary>
    /// キャンバスのストロークを全消去します。
    /// </summary>
    public void ClearCanvas() {
        this.UndoBufferList.Clear();
        this.Strokes.Clear();
    }
    /// <summary>
    /// ストロークの取り消し
    /// </summary>
    public void UndoStroke() {
        if (this.UndoBufferList.Count() > 1) {
            //直前の描画結果を破棄
            this.UndoBufferList.RemoveAt(this.UndoBufferList.Count() - 1);
            //直前の描画前の状態を復活
            this.Strokes.Clear();
            foreach (var stroke in this.UndoBufferList.Last()) {
                this.Strokes.Add(stroke);
            }
        } else {
            //Undoリストが無いので、全部消去
            this.UndoBufferList.Clear();
            this.Strokes.Clear();
        }
    }
    /// <summary>
    /// ストロークが追加されたイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void UndoInkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
        this.UndoBufferList.Add(this.Strokes.Clone());
        LastStrokeType = LAST_STROKE_TYPE.STROKE;
    }
    /// <summary>
    /// 消しゴムが実行されたイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void UndoInkCanvas_StrokeErased(object sender, RoutedEventArgs e) {
        if (LastStrokeType == LAST_STROKE_TYPE.STROKE) {
            //直前が描画ならば、履歴の追加
            this.UndoBufferList.Add(this.Strokes.Clone());
        } else {
            //削除イベントが連続しているので、直前の描画結果を更新
            if (this.UndoBufferList.Count() > 0) {
                this.UndoBufferList[this.UndoBufferList.Count() - 1] = this.Strokes.Clone();
            }
        }
        LastStrokeType = LAST_STROKE_TYPE.ERASER;
    }
}

かなりエレファント。

もう少しエレガントな方法はないかなぁ・・・

とくに、消しゴムで何回も消しても1回のアンドゥで元に戻るのを、何とかしたいなぁ。

StylusUpイベントとかでやると出来るとは思うケド、
MouseUpやTouchUpでもやらないといけなくて、
もっとエレファントなコードになるだろうし・・・

※続きがあります→翌日の記事

00:24, Wednesday, Jan 26, 2011 ¦ 固定リンク ¦ 携帯


■コメント

■コメントを書く

※コメントの受け付けは終了しました

△ページのトップへ
 

最新のつぶやき

サイト内検索
カスタム検索

旅行関係の記事等
九州・広島旅行記 2009(かみちゅ!)
大阪出張(KANON)
大阪出張(ハルヒ)
三宅島旅行記'05
三宅島旅行記'08
三宅島旅行記'10
伊豆半島小旅行

最近のコメント

最近のトラックバック

IPv4枯渇時計

携帯で読む
   URLを携帯に送る





[Valid RSS]

Powered by CGI RESCUE