2009-03-27 5 views
3

J'écris une application WPF Image Manager. J'ai un ListBox avec le ItemsTemplate suivant:Supprimer une image liée à un contrôle

 <Grid x:Name="grid" Width="150" Height="150" Background="{x:Null}"> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="*"/> 
       <RowDefinition Height="27.45"/> 
      </Grid.RowDefinitions> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="150"/> 
      </Grid.ColumnDefinitions> 
      <Border Margin="5,5,5,5.745" Grid.RowSpan="2" Background="#FF828282" BorderBrush="{DynamicResource ListBorder}" CornerRadius="5,5,5,5" BorderThickness="1,1,2,2" x:Name="border"> 
       <Grid> 
        <Viewbox Margin="0,0,0,21.705"> 
         <Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName}" /> 
        </Viewbox> 
        <TextBlock Height="Auto" Text="{Binding Path=Name}" TextWrapping="Wrap" x:Name="PictureText" HorizontalAlignment="Left" Margin="70,0,0,0" VerticalAlignment="Bottom" /> 
       </Grid> 
      </Border> 
     </Grid> 

Notez que le contrôle « d'image » est liée à la propriété « FullName », qui est une chaîne représentant le chemin absolu à un JPG.

Plusieurs fonctionnalités de l'application nécessitent que je modifie le fichier JPG (déplacer, renommer ou supprimer). Lorsque j'essaie de le faire (en essayant actuellement de déplacer le fichier), je reçois une exception d'E/S: "Le processus ne peut pas accéder au fichier parce qu'il est utilisé par un autre processus." Le processus de verrouillage du fichier est mon application WPF.

J'ai fait quelques recherches en ligne et trouvé plusieurs messages indiquant que les images en particulier ont du mal à abandonner leurs ressources. Je l'ai essayé ce qui suit:

  1. Réglage de la ListBox.Source à null
  2. Ajout de 10 secondes le temps d'attente avant de tenter le déménagement.
  3. Emission de GC.Collect().
  4. Déplacement de l'opération vers un autre thread .

Que puis-je essayer d'autre? J'ai pensé trouver une référence à l'objet Image dans le ItemsTemplate et essayer de disposer de l'Image, mais je n'arrive pas à comprendre comment obtenir la référence. Une solution possible que j'ai lu à propos de était de créer des copies des images plutôt que les images réelles, mais depuis la liaison est au nom de fichier et non l'image réelle, je ne sais pas si je pouvais faire ce travail.

Toute aide ou suggestion serait grandement appréciée.

Répondre

6

Mon Intuipic application permet aux utilisateurs de supprimer des images, aussi. Je devais écrire this converter pour y arriver. Code pertinent:

//create new stream and create bitmap frame 
BitmapImage bitmapImage = new BitmapImage(); 
bitmapImage.BeginInit(); 
bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read); 
bitmapImage.DecodePixelWidth = (int) _decodePixelWidth; 
bitmapImage.DecodePixelHeight = (int) _decodePixelHeight; 
//load the image now so we can immediately dispose of the stream 
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
bitmapImage.EndInit(); 

//clean up the stream to avoid file access exceptions when attempting to delete images 
bitmapImage.StreamSource.Dispose(); 
+0

J'ai essayé ceci (sans DecodePixelWidth/Height), mais le fichier est toujours verrouillé. Il doit y avoir quelque chose d'autre - je vais devoir commencer à chercher d'autres problèmes. Un objet FileInfo créerait-il un verrou? –

+0

Je pense que vous devrez éliminer les causes potentielles une par une. –

+0

J'ai épuisé tout ce que je pouvais trouver. Je vais reconstruire l'application suivante, en vérifiant étape par étape pour le coupable. –

2

Découvrez cet article ici.

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1/

Exemple

Fondamentalement, vous aurez à précharger l'image en utilisant un flux. Je voudrais créer un PreLoadImageConverter, quelque chose comme ça, je ne l'ai pas testé.

<Grid> 
    <Grid.Resources> 
    <local:PreLoadImageConverter x:Key="imageLoadingConverter" /> 
    </Grid.Resources> 
    <Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName, Converter={StaticResource imageLoadingConverter}}" /> 
</Grid> 

PreLoadImageConverter.cs

public class PreLoadImageConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
    if (value == null) return null; 
    string imagePath = value.ToString(); 

    ImageSource imageSource; 
    using (var stream = new MemoryStream()) 
    { 
     Bitmap bitmap = new Bitmap(imagePath); 
     bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); 
     PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); 
     imageSource = bitmapDecoder.Frames[0]; 
     imageSource.Freeze(); 
    } 
    return imageSource; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
    throw new Exception("The method or operation is not implemented."); 
    } 
} 
+0

J'ai essayé ceci, mais je n'arrive toujours pas à déplacer le fichier. J'ai ajouté un using() autour du Bitmap, pensant peut-être qu'il n'a pas encore été disposé, mais les mêmes résultats. –

+0

Cela semble étrange, pouvez-vous déplacer ou supprimer le fichier lorsque l'application ne fonctionne pas? Est-il possible que le fichier est toujours verrouillé à partir d'une instance précédente? – bendewey

+0

Oui, je peux le déplacer manuellement. –

3

Je marqué la réponse de Kent comme une réponse, et j'aurais marqué aussi bien de bendewey, parce que je les deux dans la solution finale.

Le fichier a été définitivement verrouillé car le nom de fichier était tout ce qui était lié, de sorte que le contrôle Image a ouvert le fichier réel pour produire l'image.

Pour résoudre ce problème, j'ai créé un convertisseur de valeur comme bendewey a suggéré, et puis j'utiliser (la plupart) sous forme de code suggestion de Kent pour revenir une nouvelle BitmapImage:

[ValueConversion(typeof(string), typeof(BitmapImage))] 
public class PathToBitmapImage : IValueConverter 
{ 
    public static BitmapImage ConvertToImage(string path) 
    { 
     if (!File.Exists(path)) 
      return null; 

     BitmapImage bitmapImage = null; 
     try 
     { 
      bitmapImage = new BitmapImage(); 
      bitmapImage.BeginInit(); 
      bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read); 
      bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
      bitmapImage.EndInit(); 
      bitmapImage.StreamSource.Dispose(); 
     } 
     catch (IOException ioex) 
     { 
     } 
     return bitmapImage; 
    } 

    #region IValueConverter Members 

    public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if (value == null || !(value is string)) 
      return null; 

     var path = value as string; 

     return ConvertToImage(path); 
    } 

    public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion 
} 

Comme les commentaires ci-dessus suggèrent, cependant , cela n'a pas résolu le problème. J'ai été absent sur d'autres projets et récemment retourné à celui-ci revigoré pour trouver la solution.

J'ai créé un autre projet qui a uniquement testé ce code, et bien sûr, cela a fonctionné. Cela m'a dit qu'il y avait plus de mal dans le programme original.

Longue histoire courte, l'image a été générée en trois endroits que je pensais avait été adressée:

1) Le ImageList, maintenant lié à l'aide du convertisseur. 2) Image principale liée à la propriété ImageList SelectedItem. 3) La fenêtre contextuelle DeleteImage, qui était liée à l'aide du convertisseur.

Il s'avère que le problème était en # 2. En liant à SelectedItem, j'ai supposé par erreur que je liais l'image nouvellement rendue (basée sur le convertisseur). En réalité, l'objet SelectedItem était en fait le nom du fichier. Cela signifiait que l'image principale était de nouveau construite en accédant directement au fichier. Ainsi, la solution consistait à lier le contrôle Image principal à la propriété SelectedItem ET à utiliser le convertisseur.

Questions connexes