You need to do things a little differently for an editor to a viewer
your Model needs to be updated to a ViewModel as it will be changing ( you can use the model to build the ViewModel, however in this case there is so little functionality in the Model that you might as well use the FIleInfo class as your model)
public class ImageEditor: IDisposable,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<FileInfo> images = new List<FileInfo>();
private FileInfo _ImageFile;
public static readonly PropertyChangedEventArgs FilenameProperty = new PropertyChangedEventArgs(nameof(ImageFile));
public FileInfo ImageFile
{
get { return _ImageFile; }
set
{
_ImageFile = value;
Strokes.Clear();
PropertyChanged?.Invoke(this, ImageFrameProperty);
}
}
private int selectedIndex;
public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs(nameof(SelectedIndex));
public int SelectedIndex
{
get { return selectedIndex; }
private set
{
if (value < images.Count && value > -1)
{
selectedIndex = value;
PropertyChanged?.Invoke(this, SelectedIndexProperty);
ImageFile = images[selectedIndex];
Load();
}
}
}
MemoryStream mem;
private BitmapSource _ImageFrame;
public static readonly PropertyChangedEventArgs ImageFrameProperty = new PropertyChangedEventArgs(nameof(ImageFrame));
public BitmapSource ImageFrame
{
get { return _ImageFrame; }
set
{
_ImageFrame = value;
PropertyChanged?.Invoke(this, ImageFrameProperty);
}
}
public StrokeCollection Strokes { get; } = new StrokeCollection();
public void Open(FileInfo file)
{
images.Add(file);
SelectedIndex = images.Count - 1;
}
public void Next()
{
SelectedIndex++;
}
public void Back()
{
SelectedIndex--;
}
public void Load()
{
ImageFile.Refresh();
if (ImageFile.Exists)
{
if (mem != null)
{
mem.Dispose();
}
using (var stream = ImageFile.OpenRead())
{
mem = new MemoryStream();
stream.CopyTo(mem);
}
ImageFrame = BitmapFrame.Create(mem);
}
}
public void Dispose()
{
if (mem != null)
{
mem.Dispose();
}
ImageFrame = null;
}
public void Save()
{
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(ImageFrame, new Rect(0, 0, ImageFrame.Width, ImageFrame.Height));
foreach (var item in Strokes)
{
item.Draw(drawingContext);
}
drawingContext.Close();
Strokes.Clear();
var width = ImageFrame.Width;
var height = ImageFrame.Height;
var bitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(drawingVisual);
ImageFrame = bitmap;
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(ImageFrame));
using (var stream = ImageFile.OpenWrite())
{
encoder.Save(stream);
}
}
}
}
note: as we are using a memory stream with out the using statement then its a good idea to us the IDisposable interface for clean up
what this is doing is creating a memory resident bitmap then using that as a Frame, this removes the openRead lock on on the file that you get with a normal bitmap with a URI
next we have the Editor itself
<Window x:Class="ImageDemo.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:ImageDemo"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Closing="Window_Closing" >
<Window.DataContext>
<local:ImageEditor x:Name="editor" />
</Window.DataContext>
<DockPanel>
<Menu DockPanel.Dock="Top" >
<MenuItem Header="File" >
<MenuItem Command="ApplicationCommands.Open" />
<MenuItem Command="ApplicationCommands.Save" />
</MenuItem>
<MenuItem Command="ApplicationCommands.Undo" />
<ComboBox SelectedValue="{Binding DefaultDrawingAttributes.Color, ElementName=inkCanvas}">
<Color>White</Color>
<Color>Black</Color>
<Color>Yellow</Color>
<Color>Red</Color>
<Color>Cyan</Color>
<Color>SpringGreen</Color>
<ComboBox.ItemTemplate>
<DataTemplate>
<Rectangle Width="15" Height="15">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Mode=OneWay}" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Menu>
<Button Content="<" Click="Back_Click"/>
<Button Content=">" DockPanel.Dock="Right" Click="Next_Click"/>
<Grid HorizontalAlignment="Left" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Image Source="{Binding ImageFrame}" Stretch="None"/>
<InkCanvas x:Name="inkCanvas" Background="Transparent" Strokes="{Binding Strokes}" >
<InkCanvas.DefaultDrawingAttributes>
<DrawingAttributes x:Name="DrawSetting" />
</InkCanvas.DefaultDrawingAttributes>
</InkCanvas>
</Grid>
</DockPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, execSave, hasChanged));
CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, execOpen));
CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, execUndo, hasChanged));
}
private void execOpen(object sender, ExecutedRoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if(dlg.ShowDialog() ?? false)
{
editor.Open(new System.IO.FileInfo(dlg.FileName));
}
e.Handled = true;
}
private void hasChanged(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = editor.Strokes.Count > 0;
e.Handled = true;
}
private void execUndo(object sender, ExecutedRoutedEventArgs e)
{
editor.Strokes.Remove(editor.Strokes.Last());
e.Handled = true;
}
private void execSave(object sender, ExecutedRoutedEventArgs e)
{
editor.Save();
e.Handled = true;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
editor.Dispose();
}
private void Next_Click(object sender, RoutedEventArgs e)
{
editor.Next();
}
private void Back_Click(object sender, RoutedEventArgs e)
{
editor.Back();
}
}
Note if you are doing any scaling/stretching on the image then you need to reverse the operation on the Strokes before rendering them or or the edits will be in relation to the screen not the image
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…