WPF InkCanvas Undo/Redo

HoneyBee 186 Reputation points
2022-02-14T06:53:04.85+00:00

I am implementing various functions using InkCanvas.

Now, the last task left for me is to implement Undo and Redo functions.

There are so many different hints.
I am trying to apply it.

Below are the most known hint codes.

public partial class MainWindow : Window  
{  
    System.Windows.Ink.StrokeCollection _added;  
    System.Windows.Ink.StrokeCollection _removed;  
    private bool handle = true;  
    public MainWindow()  
    {  
        InitializeComponent();  
        inkCanvas1.Strokes.StrokesChanged += Strokes_StrokesChanged;  
    }  
  
    private void Strokes_StrokesChanged(object sender, System.Windows.Ink.StrokeCollectionChangedEventArgs e)  
    {  
        if(handle)  
        {  
            _added = e.Added;  
            _removed = e.Removed;  
        }  
    }  
  
  
    private void Undo(object sender, RoutedEventArgs e)  
    {  
        handle = false;  
        inkCanvas1.Strokes.Remove(_added);  
        inkCanvas1.Strokes.Add(_removed);  
        handle = true;  
    }  
  
    private void Redo(object sender, RoutedEventArgs e)  
    {  
        handle = false;  
        inkCanvas1.Strokes.Add(_added);  
        inkCanvas1.Strokes.Remove(_removed);  
        handle = true;  
    }  
}  

However, this code cannot be repeated.

It is not possible to continuously execute Undo or Redo consecutively.

I draw strokes and erase them with an eraser.

Can I get a hint about the Undo and Redo functions that control all of this?

----------------------------------------------------

After posting a question, I pondered a little more and wrote the code below.

Problems occur in Undo and Redo.

Draw a picture and erase some (I only delete the Stroke, I do not use the point eraser).

write again

If you run Undo, it works normally.

And if you repeat Redo at this time, the result will be messed up from then on.

Could you please help me figure out what's wrong with my code?

Or is there a better way?

namespace InkCanvasTest  
{  
    /// <summary>  
    /// Interaction logic for MainWindow.xaml  
    /// </summary>  
    public partial class MainWindow : Window  
    {  
        public Stack<DoStroke> DoStrokes { get; set; }  
  
        public Stack<DoStroke> UndoStrokes { get; set; }  
  
        private bool handle = true;  
  
        public MainWindow()  
        {  
            InitializeComponent();  
  
            DoStrokes = new Stack<DoStroke>();  
  
            UndoStrokes = new Stack<DoStroke>();  
  
            inkCanvas.DefaultDrawingAttributes.FitToCurve = true;  
            inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb(255, 255, 255, 255);  
  
            inkCanvas.Strokes.StrokesChanged += Strokes_StrokesChanged;  
        }  
  
        private void Strokes_StrokesChanged(object sender, System.Windows.Ink.StrokeCollectionChangedEventArgs e)  
        {  
            if(handle)  
            {  
                DoStrokes.Push(new DoStroke  
                {  
                    ActionFlag = e.Added.Count > 0 ? "ADD" : "REMOVE",  
                    Stroke = e.Added.Count > 0 ? e.Added[0] : e.Removed[0]  
                });  
            }  
        }  
  
        public void Undo()  
        {  
            handle = false;  
  
            if(DoStrokes.Count > 0)  
            {  
                DoStroke @do = DoStrokes.Pop();  
                if(@do.ActionFlag.Equals("ADD"))  
                {  
                    inkCanvas.Strokes.Remove(@do.Stroke);  
                }  
                else  
                {  
                    inkCanvas.Strokes.Add(@do.Stroke);  
                }  
  
                UndoStrokes.Push(@do);  
            }  
            handle = true;  
        }  
        public void Redo()  
        {  
            handle = false;  
            if (UndoStrokes.Count > 0)  
            {  
                DoStroke @do = UndoStrokes.Pop();  
                if(@do.ActionFlag.Equals("ADD"))  
                {  
                    inkCanvas.Strokes.Add(@do.Stroke);  
                }  
                else  
                {  
                    inkCanvas.Strokes.Remove(@do.Stroke);  
                }  
            }  
            handle = true;  
        }  
  
  
        private void Button_Click(object sender, RoutedEventArgs e)  
        {  
            switch((sender as Button).Tag)  
            {  
                case "Test1":  
                    inkCanvas.EditingMode = InkCanvasEditingMode.Ink;  
                    break;  
  
                case "Test2":  
                    inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;  
                    break;  
  
                case "Test3":  
                    inkCanvas.Strokes.Clear();  
                    break;  
  
                case "Undo":  
                    Undo();  
                    break;  
                case "Redo":  
                    Redo();  
                    break;  
            }  
        }  
  
        private void CheckBox_Checked(object sender, RoutedEventArgs e)  
        {  
            inkCanvas.DefaultDrawingAttributes.FitToCurve = true;  
        }  
  
        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)  
        {  
            inkCanvas.DefaultDrawingAttributes.FitToCurve = false;  
        }  
    }  
  
    public struct DoStroke  
    {  
        public string ActionFlag { get; set; }  
        public System.Windows.Ink.Stroke Stroke { get; set; }  
    }  
}  


<Window x:Class="InkCanvasTest.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
        xmlns:local="clr-namespace:InkCanvasTest"  
        mc:Ignorable="d"  
        Title="" Height="450" Width="800">  
    <Grid>  
        <Grid.Background>  
            <SolidColorBrush Color="#0E321D"/>  
        </Grid.Background>  
        <Grid.ColumnDefinitions>  
            <ColumnDefinition Width="*"/>  
            <ColumnDefinition Width="100" />  
        </Grid.ColumnDefinitions>  
  
        <Grid.RowDefinitions>  
            <RowDefinition Height="50" />  
            <RowDefinition Height="50" />  
            <RowDefinition Height="50" />  
            <RowDefinition Height="50" />  
            <RowDefinition Height="50" />  
            <RowDefinition Height="50" />  
            <RowDefinition Height="*" />  
        </Grid.RowDefinitions>  
  
          
        <InkCanvas x:Name="inkCanvas"   
                   Grid.Column="0" Grid.ColumnSpan="2"  
                   Grid.Row="0" Grid.RowSpan="7"  
                   Width="Auto" Height="Auto" EditingMode="Ink"  
                   IsHitTestVisible="True"  
                   Background="Transparent"  
                   UseCustomCursor="True"  
                   Cursor="Pen"/>  
  
        <Button x:Name="btn_Test1"  
                Grid.Row="0"  
                Grid.Column="1"  
                Width="100"  
                Height="50"  
                Content="펜모드"  
                Cursor="Hand"  
                Tag="Test1"  
                Click="Button_Click" />  
  
        <Button x:Name="btn_Test2"  
                Grid.Row="1"  
                Grid.Column="1"  
                Width="100"  
                Height="50"  
                Content="지우개모드"   
                Tag="Test2"  
                Click="Button_Click" />  
  
        <Button x:Name="btn_Test3"  
                Grid.Row="2"  
                Grid.Column="1"  
                Width="100"  
                Height="50"  
                Content="모두지우기"  
                Tag="Test3"  
                Click="Button_Click" />  
  
        <Button x:Name="btn_Test4"  
                Grid.Row="3"  
                Grid.Column="1"  
                Width="100"  
                Height="50"  
                Content="UnDo"  
                Tag="Undo"  
                Click="Button_Click" />  
  
        <Button x:Name="btn_Test5"  
                Grid.Row="4"  
                Grid.Column="1"  
                Width="100"  
                Height="50"  
                Content="ReDo"  
                Tag="Redo"  
                Click="Button_Click" />  
  
        <CheckBox Grid.Row="5"  
                  Grid.Column="1"  
                  Foreground="White"  
                  VerticalAlignment="Center"  
                  HorizontalAlignment="Center"  
                  IsChecked="True"  
                  Content="부드럽게"   
                  Checked="CheckBox_Checked"  
                  Unchecked="CheckBox_Unchecked"/>  
    </Grid>  
</Window>  
  
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,676 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Hui Liu-MSFT 40,266 Reputation points Microsoft Vendor
    2022-02-16T09:08:45.92+00:00

    You could probably call DoStrokes.Push(@do); in method Redo(). In a similar way as is done in the Undo-method.

    public void Redo()  
             {  
                 handle = false;  
                 if (UndoStrokes.Count > 0)  
                 {  
                     DoStroke @do = UndoStrokes.Pop();  
                     if(@do.ActionFlag.Equals("ADD"))  
                     {  
                         inkCanvas.Strokes.Add(@do.Stroke);  
                     }  
                     else  
                     {  
                         inkCanvas.Strokes.Remove(@do.Stroke);  
                     }  
                     UndoStrokes.Push(@do);  
                 }  
                 handle = true;  
             }  
    

    The result:
    174923-5.gif


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.