WPF InkCanvas selection mode, save all/selected strokes, load strokes file, save inkcanvas as image

//xaml
<Window x:Class="WpfApp416.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:WpfApp416"
        mc:Ignorable="d"
        WindowState="Maximized"
        KeyDown="Window_KeyDown"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ToolBar Grid.Row="0">
            <!--<Button Content="Edit Mode" Click="EditModeClick" Width="150"/>
            <Button Content="Erase Mode" Click="EraseModeClick" Width="150"/>-->
            <Button Content="Select Mode" Click="SelectModeClick" Width="150"/>
            <Button Content="Remove Selected" Click="RemoveSelectedClick" Width="150"/>
            <Button Content="Change Color" Click="ChangeColorClick" Width="150"/>
            <Button Content="Save All" Click="SaveAllClick" Width="150"/>
            <Button Content="Save Selected" Click="SaveSelectedClick" Width="150"/>
            <Button Content="Load Strokes" Click="LoadStrokesClick" Width="150"/>
            <Button Content="Save As Picture" Click="SavePictureClick" Width="150"/>
        </ToolBar>
        <InkCanvas x:Name="inkCvs" Grid.Row="1" SelectionChanged="inkCvs_SelectionChanged"
                   Gesture="inkCvs_Gesture" >           
            <Image Source="https://img1.baidu.com/it/u=3991277133,2041185316&amp;fm=253"
                   Width="{Binding ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}" 
                   Height="{Binding ActualHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}"/>
        </InkCanvas>
    </Grid>
</Window>


//cs
using System;
using System.Collections.Generic;
using System.IO;
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.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using Newtonsoft.Json;

namespace WpfApp416
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitInkCanvas();
            
        }

        private void InitInkCanvas()
        {
            inkCvs.DefaultDrawingAttributes.Color = Colors.Red;
            inkCvs.DefaultDrawingAttributes.Width = 10;
            inkCvs.DefaultDrawingAttributes.Height = 10;
            //inkCvs.DefaultDrawingAttributes.IsHighlighter = true;
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.C)
            {
                var msgResult=MessageBox.Show("Are you sure to exit the window?","Exit",
                    MessageBoxButton.YesNo,MessageBoxImage.Question, MessageBoxResult.Yes);
                if(msgResult == MessageBoxResult.Yes)
                {
                    this.Close();
                }
            }
        }

        private void EditModeClick(object sender, RoutedEventArgs e)
        {
            inkCvs.EditingMode = InkCanvasEditingMode.Ink;
        }

        private void EraseModeClick(object sender, RoutedEventArgs e)
        {
            inkCvs.EditingMode = InkCanvasEditingMode.EraseByStroke;
        }

        private void SelectModeClick(object sender, RoutedEventArgs e)
        {
            inkCvs.EditingMode=InkCanvasEditingMode.Select;
        }

        private void ChangeColorClick(object sender, RoutedEventArgs e)
        {
            UCColorPicker ucColor = new UCColorPicker();
            //ucColor.Show();
            
            if(ucColor.ShowDialog()==true)
            {
                Color cl = new Color();
                cl.A = ucColor.UCAlpha;
                cl.R = ucColor.UCRed;
                cl.G = ucColor.UCGreen;
                cl.B = ucColor.UCBlue;

                inkCvs.DefaultDrawingAttributes.Color = cl;
            }
           

        }

        private void inkCvs_SelectionChanged(object sender, EventArgs e)
        {
            

        }

        private void SaveAllClick(object sender, RoutedEventArgs e)
        {
            var allStrokes = inkCvs.Strokes;
            if (allStrokes != null && allStrokes.Any())
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "ISF Files|*.isf|All Files|*.*";
                dialog.FileName = $"All{DateTime.Now.ToString("yyyyMMddHHmmddssffff")}_{Guid.NewGuid().ToString("N")}.isf";
                if (dialog.ShowDialog() == true)
                { 
                    if (dialog.ShowDialog() == true)
                    {
                        using (FileStream fs = new FileStream(dialog.FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                        {
                            allStrokes.Save(fs);
                            MessageBox.Show($"Saved in {dialog.FileName}", "Save All Successfully!", MessageBoxButton.OK);
                        }
                    }
                }
            }
        }

        private void SaveSelectedClick(object sender, RoutedEventArgs e)
        {
            var selectedStrokes = inkCvs.GetSelectedStrokes();
            if (selectedStrokes != null && selectedStrokes.Any())
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "ISF Files|*.isf|All Files|*.*";
                dialog.FileName = $"Selected{DateTime.Now.ToString("yyyyMMddHHmmddssffff")}_{Guid.NewGuid().ToString("N")}.isf";
                
                if(dialog.ShowDialog()==true)
                {
                    using (FileStream fs = new FileStream(dialog.FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    {
                        selectedStrokes.Save(fs);
                        MessageBox.Show($"Saved in {dialog.FileName}", "Save Selected Successfully!", MessageBoxButton.OK);
                    }
                }
            }
        }

        private void RemoveSelectedClick(object sender, RoutedEventArgs e)
        {
            var seletcedStrokes=inkCvs.GetSelectedStrokes();
            if(seletcedStrokes != null && seletcedStrokes.Any())
            {
                inkCvs.Strokes.Remove(seletcedStrokes);
            }
        }

        private void inkCvs_Gesture(object sender, InkCanvasGestureEventArgs e)
        {

        }

        private void LoadStrokesClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "ISF Files|*.isf|All Files|*.*";
            if (dialog.ShowDialog()==true)
            {
                if(File.Exists(dialog.FileName))
                {
                    using (FileStream fs = new FileStream(dialog.FileName, FileMode.Open))
                    {
                        StrokeCollection strokes = new StrokeCollection(fs);
                        inkCvs.Strokes = strokes;
                    }
                }
            }
        }

        private void SavePictureClick(object sender, RoutedEventArgs e)
        {
            RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)inkCvs.ActualWidth,
    (int)inkCvs.ActualHeight, 96d, 96d, PixelFormats.Pbgra32);
            inkCvs.Measure(new Size((int)inkCvs.ActualWidth, (int)inkCvs.ActualHeight));
            inkCvs.Arrange(new Rect(new Size((int)inkCvs.ActualWidth, (int)inkCvs.ActualHeight)));
            renderBitmap.Render(inkCvs);

            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "JPG Files|*.jpg|PNG Files|*.png|All Files|*.*";
            dialog.FileName = $"Image_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.jpg";
            if(dialog.ShowDialog()==true)
            {
                using (FileStream fs = new FileStream(dialog.FileName, FileMode.Create))
                {
                    PngBitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                    encoder.Save(fs);
                    MessageBox.Show($"Saved in {dialog.FileName}", "Save Image Successfully!", MessageBoxButton.OK);
                }
            }
        }
    }
}



//usercontrol.xaml
<Window x:Class="WpfApp416.UCColorPicker"
             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:WpfApp416"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Window.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="FontSize" Value="30"/>
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="FontSize" Value="30"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
        </Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="FontSize" Value="30"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Alpha:" HorizontalAlignment="Right"/>
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding UCAlpha,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Red:"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding UCRed,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Green:"/>
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding UCGreen,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="3" Grid.Column="0" Text="Blue:"/>
        <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding UCBlue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        <Button Grid.Row="4" Grid.Column="1" Content="OK" Click="ColorClick"/>
    </Grid>
</Window>


//usercontrol.xaml.cs
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 WpfApp416
{
    /// <summary>
    /// Interaction logic for UCColorPicker.xaml
    /// </summary>
    public partial class UCColorPicker : Window
    {
        public UCColorPicker()
        {
            InitializeComponent();
            this.DataContext = this;
        }



        public byte UCAlpha
        {
            get { return (byte)GetValue(UCAlphaProperty); }
            set { SetValue(UCAlphaProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UCAlpha.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UCAlphaProperty =
            DependencyProperty.Register("UCAlpha", typeof(byte), 
                typeof(UCColorPicker), new PropertyMetadata((byte)255));




        public byte UCRed
        {
            get { return (byte)GetValue(UCRedProperty); }
            set { SetValue(UCRedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UCRed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UCRedProperty =
            DependencyProperty.Register("UCRed", typeof(byte),
                typeof(UCColorPicker), new PropertyMetadata((byte)0));




        public byte UCGreen
        {
            get { return (byte)GetValue(UCGreenProperty); }
            set { SetValue(UCGreenProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UCGreen.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UCGreenProperty =
            DependencyProperty.Register("UCGreen", typeof(byte), 
                typeof(UCColorPicker), new PropertyMetadata((byte)0));





        public byte UCBlue
        {
            get { return (byte)GetValue(UCBlueProperty); }
            set { SetValue(UCBlueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UCBlue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UCBlueProperty =
            DependencyProperty.Register("UCBlue", typeof(byte), 
                typeof(UCColorPicker), new PropertyMetadata((byte)0));

        private void ColorClick(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }
    }
}

 

 

//Select

 

 

//Remove Selected

 

 

//Save as image

 

posted @ 2024-09-26 11:58  FredGrit  阅读(13)  评论(0编辑  收藏  举报