none
Conceito de threads RRS feed

  • Pergunta

  • Pessoal, tenho um programa que precisa ler umas imagens de um pen-drive e antes de exibí-las, faz um redimensionamento para não ficar tão pesado. O código funciona, mas tem o incoveniente de atualizar o form somente depois que todo redimensionamento acontece. Em outras palavras, parece que o from congelou. Como soluciono para que a cada imagem seja carregada assim que é redimensionada?

    Vi que o caminho é fazer threads para acelerar esta etapa. Colarei os códigos das duas funções abaixo e agradeço quem puder me orientar.

    Abraços!

    string DiretorioFotos = "";

    public void FazMiniaturas()
    {            
    string extensao = "*.jpg";

    string[] DiretoriosSplited = DiretorioFotos.Split(new char[] { '|' });
    // foreach dos diretorios
    foreach (string DiretorioPhotos in DiretoriosSplited)
    {
        if (DiretorioPhotos != "")
        {
            //foreach dos arquivos
            foreach (string arquivos in System.IO.Directory.GetFiles(DiretorioPhotos, extensao))            
            {
                //Retira o caminho qualificado e obtem somente o nome do arquivo
                string nomeArquivo = System.IO.Path.GetFileName(arquivos);

                var OrigemFoto = Image.FromFile(arquivos);
                var LarguraReferencia = (double)OrigemFoto.Width / (double)Screen.PrimaryScreen.Bounds.Width * 5;
                var AlturaReferencia = (double)OrigemFoto.Height / (double)Screen.PrimaryScreen.Bounds.Height * 5;
                int MiniLargura, MiniAltura;

                if (LarguraReferencia < AlturaReferencia)
                {
                    MiniLargura = (int)((double)OrigemFoto.Width / LarguraReferencia);
                    MiniAltura = (int)((double)OrigemFoto.Height / LarguraReferencia);
                }
                else
                {
                    MiniLargura = (int)((double)OrigemFoto.Width / AlturaReferencia);
                    MiniAltura = (int)((double)OrigemFoto.Height / AlturaReferencia);
                }

                var Miniatura = new Bitmap(MiniLargura, MiniAltura);
                Graphics g = Graphics.FromImage(Miniatura);
                g.DrawImage(OrigemFoto, new Rectangle(0, 0, MiniLargura, MiniAltura));

                Miniatura.Save(@"C:\miniaturas\" + nomeArquivo, System.Drawing.Imaging.ImageFormat.Jpeg);
                g.Dispose();
            }
        }
    }


    private void CarregaFotos()
    {
        int x = 0, y = 50, quantidade=1;

        string extensao = "*.jpg";
        string[] DiretoriosSplited = DiretorioFotos.Split(new char[] { '|' });
        // foreach dos diretorios
        foreach (string DiretorioPhotos in DiretoriosSplited)
        {
            if (DiretorioPhotos != "")
            {
                //foreach dos arquivos
                foreach (string arquivos in Directory.GetFiles(DiretorioPhotos, extensao))
                {
                    string nomeArquivo = Path.GetFileName(arquivos);
                    PictureBox pb = new PictureBox();
                    panelFotos.Controls.Add(pb);                    
                    //Cada pictureBox é associado aos arquivos encontrados no diretorio selecionado
                    pb.Tag = arquivos;
                    //Evento do click para selecionar/deselecionar foto
                    pb.Click += new EventHandler(pb_Click);

                    pb.Size = new Size(300, 400);
                    pb.SizeMode = PictureBoxSizeMode.Zoom;
                    pb.BorderStyle = BorderStyle.Fixed3D;
                    pb.Image = Image.FromFile(@"C:\miniaturas\" + nomeArquivo);                        
                    pb.Location = new Point(x, y);

                    x += 300;
                    if (quantidade % 4 == 0)
                    {
                        x = 0;
                        y += 410;
                    }   

                    lblQuantidade.Text = quantidade.ToString() + " Fotos";  //quantidade.ToString() + " Fotos";                        
                    quantidade++;                        

                    Label lblNomeArq = new Label();
                    pb.Controls.Add(lblNomeArq);
                    lblNomeArq.Text = nomeArquivo;
                    lblNomeArq.Location = new Point(0, 10);
                    lblNomeArq.ForeColor = Color.Red;
                    lblNomeArq.Font = new Font("Microsoft Sans Serif", 12);
                    lblNomeArq.Size = new Size(pb.Width, 20);
                }
            }
        }
    }


    sexta-feira, 17 de maio de 2013 19:52

Todas as Respostas

  • Bom dia..

    Se vale de dica, talvez você pudesse usar o Async na chamado do seu método do form, assim você não ficaria preso até terminar. Mas isso só vale com o VS2012, se não for o caso ai teria que usar thread mesmo.

    Colei um exemplo abaixo, é bem simples.

    public Form1()

    InitializeComponent();

    button1.Click += async (sender, e) => { await Teste(); };

    }

    async Task Teste()

    {

    await Task.Delay(10000);
            MessageBox.Show("Isso e um teste!!!");
    }


    quarta-feira, 22 de maio de 2013 13:38
  • Márcio S Silva, obrigado pela ajuda.

    Estou usando o Visual Studio 2008 a versão do .NET é 3.5. Pode dizer como faço a thread que sugere para este caso? Em todas as vezes que tentei apareceu como resposta:

    "Operação entre threads inválida: controle 'panel Fotos' acessado de um thread que não aquele no qual foi criado."

    Eu não sei como consertar este problema. Alguém pode ajudar?

    quinta-feira, 23 de maio de 2013 16:25
  • Murilo, Talvez colocar a parte que redimensiona em uma thread para não prender:

    Thread th = new Thread(new ThreadStart(FazMiniaturas));

    th.Start();

    E colocar no CarregaFotos um File.Exists para não dar kabum na hora que carregar, ou talvez fosse uma boa, você criar uma thread que chama o faz miniatura, e unir o "//foreach dos arquivos" para evitar o kabum. []'s

    sexta-feira, 24 de maio de 2013 18:27
  • Márcio, desculpe a demora. Surgiram outras prioridades no trabalho e agora estou retomando.

    O problema persiste na hora de fazer as threads: diz: "Operação entre threads inválida: controle 'panel Fotos' acessado de um thread que não aquele no qual foi criado."

    Li sobre o BackgroundWorker. É esse o caminho? E como uso Delegates?

    Obrigado.

    sexta-feira, 28 de junho de 2013 19:50
  • Murilo, 

    Sim.. é bem por aí.. Acessar controles por threads não dá para ser direto.. 

    Coloquei um exemplo abaixo, veja se te ajuda.. 


            delegate void SetControlValueCallback(Control oControl, string atributo, object valor);
            private void SetControlPropertyValue(Control oControl, string atributo, object valor)
            {
                if (oControl.InvokeRequired)
                {
                    SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
                    oControl.Invoke(d, new object[] { oControl, atributo, valor });
                }
                else
                {
                    Type t = oControl.GetType();
                    PropertyInfo[] props = t.GetProperties();
                    foreach (PropertyInfo p in props)
                    {
                        if (p.Name.ToUpper() == atributo.ToUpper())
                        {
                            p.SetValue(oControl, valor, null);
                        }
                    }
                }
            }
    

    Aí você modifica o componente pelo thread usando o método: Por exemplo, um TextBox, mudando o texto: 

    SetControlPropertyValue(txtNome, "Text", "Marcio");

    Veja se te ajuda.. Qualquer coisa grita aí.

    Abraços.


    Marcio S Silva | http://marsosi.blogspot.com | marsosil@ovi.com

    • Sugerido como Resposta Márcio S Silva sexta-feira, 28 de junho de 2013 20:02
    sexta-feira, 28 de junho de 2013 20:02
  • Marcio,

    continuo com problemas. Não entendi como proceder de acordo com o seu código. Eu preciso animar um Progressbar enquanto as fotos são redimensionadas. Então, o que preciso fazer?

    O argumento Control oControl do seu código devo substituir por

    AnimaBarra (Control progressBar1) ?

    Ao mesmo tempo preciso que o Label informe o número da foto que está sendo processado.

    Em todos os casos tenho como resposta que o controle foi acessado por outra Thread que não foi a que criou.

    Abraços!

    segunda-feira, 1 de julho de 2013 18:45
  • Murilo, 

    Coloquei um exemplo abaixo e na galeria utilizando uma parte do trecho do seu fazMiniatura, e trabalhando com threads para a  atualização do progressBar e o ListView, usando um ImageList. 

    Acho que pode ser mais ou menos que estava procurando. 

    Veja se te ajuda.. 

    http://code.msdn.microsoft.com/Threads-com-ListView-e-7231ab29

    Boa sorte, qualquer coisa estamos aí.

    Abraços.. 

    using System;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    using System.Reflection;
    using System.Threading;
    using System.IO;
    
    namespace ExemploDoProgressBar
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                btnCaminho.Click += new EventHandler(btnCaminho_Click);
            }
    
            void btnCaminho_Click(object sender, EventArgs e)
            {
                
                if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
    
                    txtCaminho.Text = folderBrowserDialog1.SelectedPath;
                    Thread th = new Thread(new ThreadStart( populaListView ));
                    th.Start();                
                }
            }
    
            void populaListView()
            {
                FazMiniaturas();
                atualizaView();
            }
    
            public void FazMiniaturas()
            {
                string extensao = "*.jpg";
    
                SetControlPropertyValue(progressBar1, "Minimum", 0);
                
                string[] arq = Directory.GetFiles(txtCaminho.Text, extensao);
                SetControlPropertyValue(progressBar1, "Maximum", arq.Count());
                SetControlPropertyValue(progressBar1, "Step", 1);
    
                int cont = 0;
    
                //foreach dos arquivos
                foreach (string arquivos in arq)
                {
                    cont++;
                    //Retira o caminho qualificado e obtem somente o nome do arquivo
                    string nomeArquivo = Path.GetFileName(arquivos);
    
                    var OrigemFoto = Image.FromFile(arquivos);
                    var LarguraReferencia = (double)OrigemFoto.Width / (double)Screen.PrimaryScreen.Bounds.Width * 5;
                    var AlturaReferencia = (double)OrigemFoto.Height / (double)Screen.PrimaryScreen.Bounds.Height * 5;
                    int MiniLargura, MiniAltura;
    
                    if (LarguraReferencia < AlturaReferencia)
                    {
                        MiniLargura = (int)((double)OrigemFoto.Width / LarguraReferencia);
                        MiniAltura = (int)((double)OrigemFoto.Height / LarguraReferencia);
                    }
                    else
                    {
                        MiniLargura = (int)((double)OrigemFoto.Width / AlturaReferencia);
                        MiniAltura = (int)((double)OrigemFoto.Height / AlturaReferencia);
                    }
    
                    var Miniatura = new Bitmap(MiniLargura, MiniAltura);
                    Graphics g = Graphics.FromImage(Miniatura);
                    g.DrawImage(OrigemFoto, new Rectangle(0, 0, MiniLargura, MiniAltura));
    
                    Miniatura.Save(@"C:\Miniatura\" + nomeArquivo, System.Drawing.Imaging.ImageFormat.Jpeg);
                    g.Dispose();
                    CallMethod(progressBar1, "PerformStep", null);                
    
                }            
            }
    
            delegate void ControlCallMethod(Control oControl, string metodo, object[] param);
            private void CallMethod(Control oControl, string metodo, object[] param)
            {
                if (oControl.InvokeRequired)
                {
                    ControlCallMethod d = new ControlCallMethod(CallMethod);
                    oControl.Invoke(d, new object[] { oControl, metodo, null });
                }
                else
                {
                    Type t = oControl.GetType();
    
                    MethodInfo[] method = t.GetMethods();
                    
                    foreach (MethodInfo p in method)
                    {
                        if (p.Name.ToUpper() == metodo.ToUpper())
                        {
                            p.Invoke(oControl, param);
                        }
                    }
                }
            }
    
            delegate void SetControlValueCallback(Control oControl, string atributo, object valor);
            private void SetControlPropertyValue(Control oControl, string atributo, object valor)
            {
                if (oControl.InvokeRequired)
                {
                    SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
                    oControl.Invoke(d, new object[] { oControl, atributo, valor });
                }
                else
                {
                    Type t = oControl.GetType();
                    PropertyInfo[] props = t.GetProperties();
                    foreach (PropertyInfo p in props)
                    {
                        if (p.Name.ToUpper() == atributo.ToUpper())
                        {
                            p.SetValue(oControl, valor, null);
                        }
                    }
                }
            }
    
            void atualizaView()
            {
                
                int cont = 0;
                string extensao = "*.jpg";
                string[] arq = Directory.GetFiles("c:\\Miniatura", extensao);
    
                foreach (string arquivos in arq)
                {
                    InsertIntoImagem(Image.FromFile(arquivos));
                    InsertIntoList(arquivos.ToString(), cont);
                    cont++;
                }
            }
    
            private delegate void InsertIntoListDelegate(string arquivo, int imagem);
            public void InsertIntoList(string arquivo, int imagem)
            {
                if (InvokeRequired)
                {
                    Invoke(new InsertIntoListDelegate(InsertIntoList), arquivo, imagem);
                }
                else
                {                
                    listView1.Items.Add(arquivo, imagem);
                }
            }
    
            private delegate void InsertIntoImagemDelegate(Image imagem);
            public void InsertIntoImagem( Image imagem)
            {
                if (InvokeRequired)
                {
                    Invoke(new InsertIntoImagemDelegate(InsertIntoImagem), imagem);
                }
                else
                {
                    imageList1.Images.Add(imagem);                
                }
            }
    
        }
    }
    


    Marcio S Silva | http://marsosi.blogspot.com | marsosil@ovi.com


    • Sugerido como Resposta Márcio S Silva quarta-feira, 3 de julho de 2013 03:21
    • Editado Márcio S Silva quarta-feira, 3 de julho de 2013 03:32 Faltou endereço da galeria.
    quarta-feira, 3 de julho de 2013 03:14
  • Boa tarde, Marcio!

    Cara, esse tal de thread é a coisa mais difícil que já vi. Não funciona, não dá certo.

    Tentei usar em conjunto o método que redimensiona as fotos e outro que carrega as fotos usando this.refresh(), mas é inútil. Esgota memória e a máquina fica completamente instável. Os erros mais absurdos aparecem.
    O caminho é usar threads mesmo, mas mesmo que eu estude, entenda o conceito, na hora de implementar, não funciona.

    Agradeço imensamente a ajuda, mas nem com o seu exemplo consegui progredir.

    Abraços!

    quinta-feira, 18 de julho de 2013 18:13