2016-07-29 17 views
1

Ich arbeite in WPF-Anwendung mit MVVM. Laden und Bearbeiten eines Bildes in imageSlider gibt mir Fehler und Ausnahmen.Bild bearbeiten Ausnahmen

Lade Bild: Insufficient memory to handle Bearbeiten Bild: Sharing violation

Ansichtsmodell:

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 

}` 

Modell:

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> 

Der obige Code funktioniert gut mit dem ImageSlider (Next und Back). Aber während der Bearbeitung öffnet es sich in mspaint und wenn ich es speichern, wirft mich Sharing violation error. Müde paar Lösungen geschrieben in SO here und here. Wenn ich diese versuche, kann ich das Bild bearbeiten und ohne Fehler speichern. Aber wenn ich das Schieberegler-Steuerelement (Next und Back) verwende und wenn es schwerere Bilder lädt, wirft es mich häufig Insufficient memory to handle Ausnahme.

Also jetzt muss ich diese beiden Probleme beseitigen. Freundlich helfen.

+0

Sie bereits erhalten ein Rendering [Antwort] (http://stackoverflow.com/questions/29097171/sharing-violation-on-image-view-and-edit-wpf), ändern Sie einfach Bild laden Mechanismus a t 'ImageItem' auf einem dort angegebenen, so wird es eine Lesedatei erstellen und seine Kopie im Speicher erstellen. Über den Speicher ist WPF bei der Bildverarbeitung aufgrund seines Bildgewichts ein totaler "Mist". Ihr 5 MB jpeg könnte im Speicher auf ~ 100 MB anwachsen, also ist es besser, das Bild aus der Datei zu laden, wenn jemand darauf wechselt und nur den Dateipfad des Bildes zu einem anderen Zeitpunkt hält. – Shakra

+0

@Shakra Also könnte ich bei meiner Implementierung falsch liegen. Die Verarbeitung von BitmapSource, BitmapImage und ImageItem kann das Problem sein. Kannst du bitte den Weg posten? – iamCR

Antwort

1

Sie müssen die Dinge ein wenig anders für einen Editor für einen Betrachter

Ihr Modell tun muss, um ein Ansichtsmodell aktualisiert werden, wie es zu ändern wird (Sie das Modell verwenden, können die Ansichtsmodell, aber in diesem bauen Falls es in dem Modell, das Sie könnte genauso gut verwenden, um die Fileinfo-Klasse als Modell)

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

Notiz so wenig Funktionalität ist: wie wir verwenden einen Speicherstrom mit aus der Anweisung using dann ist es eine gute Idee zu uns die IDisposable-Schnittstelle zum Aufräumen

was dies tut ist ein speicherresidentes Bitmap Erstellen dann als Rahmen mit, dass dies die Openread Sperre auf auf der Datei entfernt, die Sie mit einem normalen Bitmap erhalten mit einem

URI

nächsten haben wir den Editor selbst

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

Hinweis, wenn Sie eine Skalierung tun/Stretching auf dem Bild, dann müssen Sie den Vorgang auf den Strokes umkehren, bevor sie oder oder die Änderungen werden in Bezug auf den Bildschirm nicht das Bild

+0

Beim Laden eines Bildes bekomme ich die 'FileFormatException' in der Zeile' ImageFrame = BitmapFrame.Create (mem) '. Der Fehler ist 'das Bild kann nicht decodiert werden. Der Bildkopf könnte beschädigt sein. – iamCR

+0

Der Fehler besagt, dass die Datei kein gültiges Image ist. Überprüfen Sie zuerst, ob der Pfad korrekt ist, wenn der Fehler ausgelöst wird. Wenn Ihr Pfad ungültig ist und auf die falsche Datei verweist, die ihn erklären könnte, müssen Sie den Speicher möglicherweise zurücksetzen stream position, es funktionierte in meinen Tests, aber manchmal muss 'mem.Position = 0;' zurückgesetzt werden, bevor der Frame erstellt – MikeT

+0

Arbeitete gut mit der zweiten Option. Aber das Speichern/Rückgängig ist nach der Bearbeitung nicht aktiviert. Kannst du es sehen? Ich frage mich, wie das mit 'mspaint' funktioniert. Macht es? – iamCR