none
Carpetas, archivos, resources.... dudas!! RRS feed

  • Pregunta

  • Bueno les cuento lo que me pasa tengo una carpeta en mi proyecto /Imagenes/Formas/, ahi adentro tengo unas 20 imagenes. Mi idea es cargarla en base a acciones del usuario. Todas estan en Build Action, Resources

    Para acceder a una imagen en particular lo hago de la siguiente forma:

     

    BitmapImage img = new BitmapImage();

    img.BeginInit();

    img.UriSource =

    new Uri(@"/Imagenes/Formas/0_1_0.png", UriKind.Relative);

    image0.Source = img;

    img.EndInit();

    Mi duda es como puedo hacer para leer todos los archivos de /Imagenes/Formas/. En winform podria usar la clase directory,
    pero en wpf es complicado por el path relativo. Por lo tanto me gustaria saber como puedo hacer para leer todos los archivos
    /Imagenes/Formas/, o como puedo hacer para leer todos los nombres de imagenes del Resource.

    martes, 13 de julio de 2010 2:27

Respuestas

  • Hola, mira espero que te ayude.

    XAML :

    <Window x:Class="WpfAppPruebas.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="Window1" Height="366" Width="600">
     <Grid Loaded="Grid_Loaded">
      <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" SelectionChanged="listBox1_SelectionChanged" />
      <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" />
     </Grid>
    </Window>
    C# :
    using System.IO;
    
    namespace WpfAppPruebas
    {
     /// <summary>
     /// Interaction logic for Window1.xaml
     /// </summary>
     public partial class Window1 : Window
     {
      public Window1()
      {
       InitializeComponent();
      }
        
      ImageSourceConverter iSc = new ImageSourceConverter();
      string strpathAb = System.IO.Path.GetDirectoryName(System.IO.Path.GetDirectoryName(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location))) + "\\imagenes\\";
      
      private void ListaArchivos()
      {
       FileInfo[] file = new System.IO.DirectoryInfo(strpathAb).GetFiles();
       foreach (FileInfo item in file)
       {
        listBox1.Items.Add(item.Name);
       }   
      }
    
      private void Grid_Loaded(object sender, RoutedEventArgs e)
      {
       ListaArchivos();
      }
    
      private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {   
       ImageSource imgS = (ImageSource)iSc.ConvertFromString(strpathAb + listBox1.SelectedValue.ToString());
       image1.Source = imgS; 
      }
     }
    }
    
    
    martes, 13 de julio de 2010 5:09
  • Hola Leitosalma

    En las imagenes, ponles el Build Action a Content y Copy to Output Directory a Copy Always o Copy if newer.

    Con esto se copiarán al directorio de destino y si estás desplegando la aplicación con ClickOnce, se incluiran con el mismo.

    Un gran saludo!


    MCTS .NET Framework 3.5 Windows Forms Application Development
    MCTS .NET Framework 3.5 Windows Presentation Foundation
    Visita mi Blog
    Sigueme en Twitter
    martes, 13 de julio de 2010 14:59
    Moderador
  • Hola Leitosalma.

    La opcion de los compañeros Josue y Rafael, es para incluir las imagenes en el directorio de salida de la compilacion. 

    Si lo que quieres usar las imagenes desde los recursos (Con la opcion 'Resources' en la opcion de compilacion de las imagenes) no puedes realizarlo de esa forma.

    Asi pues, si quieres añadir las imagenes en los recursos del ejecutable final, debes usar un codigo como este:

    codigo xaml:

     

     <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" />
     <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" 
      Source="{Binding ElementName=listBox1, Path=SelectedValue}" />
    

     

    en el codigo xaml he enlazado el source de la imagen con el valor de un item del listBox.

    codigo .cs:

     

    // obtener el ensamblado en ejecucion
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    
    // crear el resource manager para acceder a los recursos
    // los recursos compilados como 'Resource' se encuentran en 'nombreEnsamblado.g' como nombre base 
    // (mediante la herramienta reflector se puede verificar)
    System.Resources.ResourceManager resourceMgr = new System.Resources.ResourceManager(assembly.GetName().Name + ".g", assembly);
    System.Resources.ResourceSet resourceSet = resourceMgr.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
    
    // obtener las imagenes que estan en el directorio "images" de la solucion
    var names = resourceSet.OfType<System.Collections.DictionaryEntry>().Where((w) => w.Key.ToString().StartsWith("images/"));
    
    // enlazar al listBox
    listBox1.ItemsSource = names;
    listBox1.SelectedValuePath = "Value";
    listBox1.DisplayMemberPath = "Key";
    
    // este metodo reduce espacio de trabajo de una aplicacion en ejecucion
    // si no se van a buscar mas veces los recursos, es recomendable usarla
    // mas info: http://msdn.microsoft.com/es-es/library/system.resources.resourcemanager.releaseallresources.aspx
    resourceMgr.ReleaseAllResources();
    

     

    En este codigo se puede ver que se accede a los recursos del ejecutable y se obtienen solo las imagenes, asociandolas al ListBox.

    Este codigo lo puedes poner cuando quieras cargar las imagenes en el ListBox.

    Con esto ya tendrias el proyecto funcional, cada vez que selecciones un item del listBox se mostrara la imagen.

    NOTAS: esta solucion como ejemplo esta bien, pero debes de tener en cuenta que todas las imagenes son cargadas en memoria, con lo que si tienes demasiadas no seria optimo, en lugar de cargar todas las imagenes, tienes la opcion de mostrar en el listBox solo el nombre de la imagen, y luego mediante un converter, acceder al recurso y cargar la imagen.

    como en el ejemplo siguiente:

    codigo xaml:

    añadir namespace al control window:

    xmlns:local="clr-namespace:Prueba_WPF"

    (Obviamente en lugar de 'Prueba_WPF' debe ir el nombre de tu namespace donde residira la clase 'Converter' que luego definiremos.

    añadir el recurso a la clase converter:

     <Window.Resources>
      <local:pathToImageConverter x:Key="pathToImageConverter"/>
     </Window.Resources>

    ahora los controles quedaran de la siguiente forma:

      <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" />
      <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" 
        Source="{Binding ElementName=listBox1, Path=SelectedValue, Converter={StaticResource pathToImageConverter}}" />

    Solo cambia el converter en el binding del source para que llame a la clase conversora al realizar el binding.

     

    Codigo .cs:

    
    // obtener el ensamblado en ejecucion
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    
    // crear el resource manager para acceder a los recursos
    // los recursos compilados como 'Resource' se encuentran en 'nombreEnsamblado.g' como nombre base 
    // (mediante la herramienta reflector se puede verificar)
    System.Resources.ResourceManager resourceMgr = new System.Resources.ResourceManager(assembly.GetName().Name + ".g", assembly);
    System.Resources.ResourceSet resourceSet = resourceMgr.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
    
    // obtener las imagenes que estan en el directorio "images" de la solucion
    var names = resourceSet.OfType<System.Collections.DictionaryEntry>().Where((w) => w.Key.ToString().StartsWith("images/"));
    
    // enlazar al listBox
    listBox1.ItemsSource = names;
    listBox1.SelectedValuePath = "Key";
    listBox1.DisplayMemberPath = "Key";
    
    // este metodo reduce espacio de trabajo de una aplicacion en ejecucion
    // si no se van a buscar mas veces los recursos, es recomendable usarla
    // mas info: http://msdn.microsoft.com/es-es/library/system.resources.resourcemanager.releaseallresources.aspx
    resourceMgr.ReleaseAllResources();

    Como se puede observar solo ha cambiado el selectedValuePath en el cual establecemos la clave tambien (que contendra el nombre de la imagen).

     

    ahora viene la clase conversora:

    /// <summary>
    /// clase para convertir una ruta de recurso en una imagen (ImageSource) para enlazar
    /// </summary>
    public class pathToImageConverter : IValueConverter
    {
    
     public object Convert(object _values, Type _targetType, object _parameter, System.Globalization.CultureInfo _culture)
     {
      // cargar la imagen desde los recursos
      try
      {    
       // crear la ruta a los recursos
       string uri = @"/" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ";component/" + _values.ToString();
    
       // obtener el stream de la imagen desde los recursos
       System.Windows.Resources.StreamResourceInfo sri = Application.GetResourceStream(new Uri(uri, UriKind.Relative));
    
       // crear un BitmapImage para ofrecer como ImageSource
       BitmapImage bi = new BitmapImage();
       bi.BeginInit();
       bi.StreamSource = sri.Stream;
       bi.EndInit();
        
       // retornar el BitmapImage
       return bi;
      }
      // establecere el valor anterior defecto en caso de excepcion
      catch (Exception) { return null; }
     }
    
     public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
     {
      throw new NotImplementedException();
     }
    }

    Solo hay que tener en cuenta el namespace donde se inserte esta clase para asociarlo al codigo xaml y poder vincular el resource de la clase.

    Y con esto tendriamos cargado en el ListView solo el nombre de las imagenes y al hacer click en un item, se realiza un binding mediante un conversor que carga la ruta de la imagen de los recursos, de modo que disminuye la memoria usada pero tambien disminuye la velocidad (bueno para este ejemplo ambos casos se diferencian poco, pero en una gran aplicacion se debe cuidar este tipo de cosas).

    Ya contaras que tal.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    martes, 13 de julio de 2010 17:12

Todas las respuestas

  • Hola, mira espero que te ayude.

    XAML :

    <Window x:Class="WpfAppPruebas.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="Window1" Height="366" Width="600">
     <Grid Loaded="Grid_Loaded">
      <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" SelectionChanged="listBox1_SelectionChanged" />
      <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" />
     </Grid>
    </Window>
    C# :
    using System.IO;
    
    namespace WpfAppPruebas
    {
     /// <summary>
     /// Interaction logic for Window1.xaml
     /// </summary>
     public partial class Window1 : Window
     {
      public Window1()
      {
       InitializeComponent();
      }
        
      ImageSourceConverter iSc = new ImageSourceConverter();
      string strpathAb = System.IO.Path.GetDirectoryName(System.IO.Path.GetDirectoryName(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location))) + "\\imagenes\\";
      
      private void ListaArchivos()
      {
       FileInfo[] file = new System.IO.DirectoryInfo(strpathAb).GetFiles();
       foreach (FileInfo item in file)
       {
        listBox1.Items.Add(item.Name);
       }   
      }
    
      private void Grid_Loaded(object sender, RoutedEventArgs e)
      {
       ListaArchivos();
      }
    
      private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {   
       ImageSource imgS = (ImageSource)iSc.ConvertFromString(strpathAb + listBox1.SelectedValue.ToString());
       image1.Source = imgS; 
      }
     }
    }
    
    
    martes, 13 de julio de 2010 5:09
  • Hola! gracias por la respuesta, mira me funciono cuando estaba trabajando en el visual studio. Pero cuando publique la aplicacion me tiro una excepcion que no encontro la carpeta de imagenes, supongo que esto es por que no se copio al path donde se ejecuto la aplicacion.

     

    La excepcion es:

    Could not find a part of the path '\AppData\Local\Apps\2.0\L7BGJV88.KCZ\imagenes'.

    martes, 13 de julio de 2010 13:25
  • Hola Leitosalma

    En las imagenes, ponles el Build Action a Content y Copy to Output Directory a Copy Always o Copy if newer.

    Con esto se copiarán al directorio de destino y si estás desplegando la aplicación con ClickOnce, se incluiran con el mismo.

    Un gran saludo!


    MCTS .NET Framework 3.5 Windows Forms Application Development
    MCTS .NET Framework 3.5 Windows Presentation Foundation
    Visita mi Blog
    Sigueme en Twitter
    martes, 13 de julio de 2010 14:59
    Moderador
  • Asi es, haz lo que dice Josue :D
    martes, 13 de julio de 2010 15:25
  • Hola Leitosalma.

    La opcion de los compañeros Josue y Rafael, es para incluir las imagenes en el directorio de salida de la compilacion. 

    Si lo que quieres usar las imagenes desde los recursos (Con la opcion 'Resources' en la opcion de compilacion de las imagenes) no puedes realizarlo de esa forma.

    Asi pues, si quieres añadir las imagenes en los recursos del ejecutable final, debes usar un codigo como este:

    codigo xaml:

     

     <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" />
     <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" 
      Source="{Binding ElementName=listBox1, Path=SelectedValue}" />
    

     

    en el codigo xaml he enlazado el source de la imagen con el valor de un item del listBox.

    codigo .cs:

     

    // obtener el ensamblado en ejecucion
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    
    // crear el resource manager para acceder a los recursos
    // los recursos compilados como 'Resource' se encuentran en 'nombreEnsamblado.g' como nombre base 
    // (mediante la herramienta reflector se puede verificar)
    System.Resources.ResourceManager resourceMgr = new System.Resources.ResourceManager(assembly.GetName().Name + ".g", assembly);
    System.Resources.ResourceSet resourceSet = resourceMgr.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
    
    // obtener las imagenes que estan en el directorio "images" de la solucion
    var names = resourceSet.OfType<System.Collections.DictionaryEntry>().Where((w) => w.Key.ToString().StartsWith("images/"));
    
    // enlazar al listBox
    listBox1.ItemsSource = names;
    listBox1.SelectedValuePath = "Value";
    listBox1.DisplayMemberPath = "Key";
    
    // este metodo reduce espacio de trabajo de una aplicacion en ejecucion
    // si no se van a buscar mas veces los recursos, es recomendable usarla
    // mas info: http://msdn.microsoft.com/es-es/library/system.resources.resourcemanager.releaseallresources.aspx
    resourceMgr.ReleaseAllResources();
    

     

    En este codigo se puede ver que se accede a los recursos del ejecutable y se obtienen solo las imagenes, asociandolas al ListBox.

    Este codigo lo puedes poner cuando quieras cargar las imagenes en el ListBox.

    Con esto ya tendrias el proyecto funcional, cada vez que selecciones un item del listBox se mostrara la imagen.

    NOTAS: esta solucion como ejemplo esta bien, pero debes de tener en cuenta que todas las imagenes son cargadas en memoria, con lo que si tienes demasiadas no seria optimo, en lugar de cargar todas las imagenes, tienes la opcion de mostrar en el listBox solo el nombre de la imagen, y luego mediante un converter, acceder al recurso y cargar la imagen.

    como en el ejemplo siguiente:

    codigo xaml:

    añadir namespace al control window:

    xmlns:local="clr-namespace:Prueba_WPF"

    (Obviamente en lugar de 'Prueba_WPF' debe ir el nombre de tu namespace donde residira la clase 'Converter' que luego definiremos.

    añadir el recurso a la clase converter:

     <Window.Resources>
      <local:pathToImageConverter x:Key="pathToImageConverter"/>
     </Window.Resources>

    ahora los controles quedaran de la siguiente forma:

      <ListBox Margin="12,12,0,12" Name="listBox1" HorizontalAlignment="Left" Width="309" />
      <Image HorizontalAlignment="Right" Margin="0,12,12,0" Name="image1" Stretch="Fill" Width="124" Height="150" VerticalAlignment="Top" 
        Source="{Binding ElementName=listBox1, Path=SelectedValue, Converter={StaticResource pathToImageConverter}}" />

    Solo cambia el converter en el binding del source para que llame a la clase conversora al realizar el binding.

     

    Codigo .cs:

    
    // obtener el ensamblado en ejecucion
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    
    // crear el resource manager para acceder a los recursos
    // los recursos compilados como 'Resource' se encuentran en 'nombreEnsamblado.g' como nombre base 
    // (mediante la herramienta reflector se puede verificar)
    System.Resources.ResourceManager resourceMgr = new System.Resources.ResourceManager(assembly.GetName().Name + ".g", assembly);
    System.Resources.ResourceSet resourceSet = resourceMgr.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true);
    
    // obtener las imagenes que estan en el directorio "images" de la solucion
    var names = resourceSet.OfType<System.Collections.DictionaryEntry>().Where((w) => w.Key.ToString().StartsWith("images/"));
    
    // enlazar al listBox
    listBox1.ItemsSource = names;
    listBox1.SelectedValuePath = "Key";
    listBox1.DisplayMemberPath = "Key";
    
    // este metodo reduce espacio de trabajo de una aplicacion en ejecucion
    // si no se van a buscar mas veces los recursos, es recomendable usarla
    // mas info: http://msdn.microsoft.com/es-es/library/system.resources.resourcemanager.releaseallresources.aspx
    resourceMgr.ReleaseAllResources();

    Como se puede observar solo ha cambiado el selectedValuePath en el cual establecemos la clave tambien (que contendra el nombre de la imagen).

     

    ahora viene la clase conversora:

    /// <summary>
    /// clase para convertir una ruta de recurso en una imagen (ImageSource) para enlazar
    /// </summary>
    public class pathToImageConverter : IValueConverter
    {
    
     public object Convert(object _values, Type _targetType, object _parameter, System.Globalization.CultureInfo _culture)
     {
      // cargar la imagen desde los recursos
      try
      {    
       // crear la ruta a los recursos
       string uri = @"/" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ";component/" + _values.ToString();
    
       // obtener el stream de la imagen desde los recursos
       System.Windows.Resources.StreamResourceInfo sri = Application.GetResourceStream(new Uri(uri, UriKind.Relative));
    
       // crear un BitmapImage para ofrecer como ImageSource
       BitmapImage bi = new BitmapImage();
       bi.BeginInit();
       bi.StreamSource = sri.Stream;
       bi.EndInit();
        
       // retornar el BitmapImage
       return bi;
      }
      // establecere el valor anterior defecto en caso de excepcion
      catch (Exception) { return null; }
     }
    
     public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
     {
      throw new NotImplementedException();
     }
    }

    Solo hay que tener en cuenta el namespace donde se inserte esta clase para asociarlo al codigo xaml y poder vincular el resource de la clase.

    Y con esto tendriamos cargado en el ListView solo el nombre de las imagenes y al hacer click en un item, se realiza un binding mediante un conversor que carga la ruta de la imagen de los recursos, de modo que disminuye la memoria usada pero tambien disminuye la velocidad (bueno para este ejemplo ambos casos se diferencian poco, pero en una gran aplicacion se debe cuidar este tipo de cosas).

    Ya contaras que tal.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    martes, 13 de julio de 2010 17:12
  • Buenisimooo!! gracias por todos los ejemplos los estoy probando y viendo como lo incorporo a mi proyecto!

     

    david te queria hacer una consulta! perdon no he visto mucho linq en esta linea vos pedis dentro del resource set todos los valores que empiecen con images/, ahora habria alguna forma de en vez de guardarlos en un DictionaryEntry podamos guardar solo los values en una lista de string.

    var names = resourceSet.OfType<System.Collections.DictionaryEntry>().Where((w) => w.Key.ToString().StartsWith("images/"));

    miércoles, 14 de julio de 2010 1:28
  • Hola Leitosalma.

    Si, la linea que comentas se hace uso del metodo extensor 'where' que es una de las caracteristicas del proyecto LINQ.

    para obtener los values o keys del Dictionary entry tienes que usar el select. Algo como esto:

    string[] keys = names.Select( (w) => w.Key).ToList<string>();

    string[] values = names.Select( (w) => w.Value).ToList<string>();

     

    Estas lineas no las he verificado, pero deberia ser algo parecido. En la primera seleccionamos la propiedad 'key' del dictionaryEntry y en la segunda el 'value' y luego retornamos una lista con ello.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    miércoles, 14 de julio de 2010 6:22