图像编辑例外

本文关键字:编辑 图像 | 更新日期: 2023-09-27 17:59:19

我正在使用MVVM的WPF应用程序中工作。在imageSlider中加载和编辑图像会出现错误和异常。

加载图像:Insufficient memory to handle编辑图像:Sharing violation

ViewModel:

public class ImageList : INotifyPropertyChanged
    {
        #region Fields
    private ObservableCollection<ImageItem> images = new ObservableCollection<ImageItem>();
    private int selectedIndex;
    private ImageItem currentImage;
    #endregion Fields
    #region Properties
    public ObservableCollection<ImageItem> Images
    {
        get { return images; }
        set { images = value; }
    }
    public int SelectedIndex
    {
        get { return selectedIndex; }
        set
        {
            if(value < Images.Count && value > -1)
            {
                selectedIndex = value; OnPropertyChanged();
                CurrentImage = Images[selectedIndex];
            }
        }
    }
    public ImageItem CurrentImage
    {
        get { return currentImage; }
        set { currentImage = value; OnPropertyChanged(); }
    }
    #endregion Properties
    #region Public Methods
    public void Next()
    {
        SelectedIndex ++;
    }
    public void Back()
    {
        SelectedIndex--;
    }
    #endregion Public Methods
    #region Methods
    public void AddNewImage()
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.gif, *.png, *.bmp, *.tif) | *.jpg; *.jpeg; *.jpe; *.gif; *.png, *.bmp, *.tif";
        dlg.ShowDialog();
        if(dlg.FileName != "")
        {
            Images.Add(new ImageItem() { URI = new Uri(dlg.FileName) });
            SelectedIndex = Images.Count - 1;
        }
    }
    #endregion Methods
    #region Constructors
    public ImageList()
    {
        _canExecute = true;
    }
    #endregion Constructors
    #region Commands
    private ICommand _clickCommand;
    public ICommand ClickCommand
    {
        get
        {
            return _clickCommand ?? (_clickCommand = new CommandHandler(() => AddNewImage(), _canExecute));
        }
    }
    private bool _canExecute;
    private ICommand nextCommand;
    public ICommand NextCommand
    {
        get
        {
            if (nextCommand == null)
            {
                nextCommand = new CommandHandler(()=> OnNextCommand(), true);
            }
            return nextCommand;
        }
        set { nextCommand = value; }
    }
    private void OnNextCommand()
    {
        Next();
    }
    private ICommand backCommand;
    public ICommand BackCommand
    {
        get
        {
            if(backCommand == null)
            {
                backCommand = new CommandHandler(() => OnBackCommand(), true);
            }
            return backCommand;
        }
        set { backCommand = value; }
    }
    private void OnBackCommand()
    {
        Back();
    }
 private ICommand _clickCommand;
        public ICommand ClickCommand
        {
            get
            {
                return _clickCommand ?? (_clickCommand = new CommandHandler(() => AddNewImage(), _canExecute));
            }
        }
        private ICommand _EditImageCommand;
        public ICommand EditImageCommand
        {
            get
            {
                return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(obj => EditImage(obj), _canExecute));
            }
        }
    #endregion Commands
 public void EditImage(object obj)
        {
            string ss = ((ImageItem)obj).URI.AbsolutePath;
            Process proc = Process.Start(ss);
            if (proc != null)
            {
                proc.EnableRaisingEvents = true;
                ProcessStartInfo startInfo = new ProcessStartInfo();
                //startInfo.Verb = "edit";
                startInfo.FileName = ("mspaint.exe");
                proc.StartInfo = startInfo;
            }
        }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion INotifyPropertyChanged
}`

型号:

 public class ImageItem
    {
        public Uri URI { get; set; }
        private BitmapSource _Source;
        public BitmapSource Source
        {
            get
            {
                try
                {
                    if (_Source == null) _Source = new BitmapImage(URI);//lazy loading
                }
                catch (Exception)
                {
                    _Source = null;
                }
                return _Source;
            }
        }
        public void Save(string filename)
        {
            var img = BitmapFrame.Create(Source);
            var encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(img);
            using (var saveStream = System.IO.File.OpenWrite(filename))
                encoder.Save(saveStream);
        }

    }

XAML

<DockPanel>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="200"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="30"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="30"></ColumnDefinition>
            </Grid.ColumnDefinitions>
        <Button Content="&lt;" Command="{Binding BackCommand}" Width="25" Grid.Row="0" Grid.Column="0"/>
            <Button DockPanel.Dock="Right" Content="&gt;" Command="{Binding NextCommand}" Width="25" Grid.Row="0" Grid.Column="2"/>
            <Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1">
                <Image.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Edit Image" Command="{Binding EditImageCommand, Source={StaticResource SliderViewModel}}" CommandParameter="{Binding CurrentImage}"></MenuItem>
                    </ContextMenu>
                </Image.ContextMenu>
            </Image>
            <Button Content="Add" Command="{Binding ClickCommand}" Height="30" Width="50" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4"></Button>
            <!--<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="2" Grid.Column="1"/>-->
        </Grid>
    </DockPanel>

上面的代码适用于imageSlider(下一步和上一步)。但在编辑过程中,它会在mspaint中打开,当我保存时,它会抛出Sharing violation error。厌倦了在SO这里和这里发布的一些解决方案。当尝试这些时,我可以编辑图像并保存,没有任何错误。但是,当使用Slider控件(Next和Back)以及加载较重的图像时,它经常会引发Insufficient memory to handle异常。

所以现在我需要消除这两个问题。请帮忙。

图像编辑例外

对于编辑器和查看器,您需要做一些不同的事情

您的模型需要更新为ViewModel,因为它将发生变化(您可以使用该模型来构建ViewModel,但是在这种情况下,模型中的功能太少,您还可以使用FIleInfo类作为您的模型)

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);
            }
        }
    }
}

注意:由于我们使用的是没有using语句的内存流,因此使用IDisposable接口清理是个好主意

它所做的是创建一个驻留在内存中的位图,然后将其用作帧,这将删除使用URI 的普通位图所获得的文件上的openRead锁定

接下来我们有编辑器本身

<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="&lt;" Click="Back_Click"/>
        <Button Content="&gt;" 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();
    }
}

请注意,如果您正在对图像进行任何缩放/拉伸,则需要在渲染笔划之前反转笔划上的操作,或者编辑将与屏幕而非图像有关