Jump to content
Fausto Luís

ANSWERED Preencher ListView usando Generics

Recommended Posts

Fausto Luís

Boas, 

 

Uso um form base para pesquisa, do qual outros forms são herdados; possui duas opções de pesquisa (por Id e Descrição), e uma ListView com o resultado da mesma. Recebe como parâmetros uma classe (modelo de dados já filtrado), e a ListView a preencher.

O que pretendo é criar um método genérico que faça esse preenchimento (valores "Valor do Id" e "Valor da Descrição"),, seja qual for a classe recebida.

 

Antecipadamente grato.

Fausto.

 

Código existente:
 

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace Services.Common
{
    public static class ListView_ListItems<T> where T : class
    {
        public static void ListItems(List<T> model, ListView lv2Populate)
        {
            try
            {
                lv2Populate.Items.Clear();

                foreach (var reg in model)
                {
                    ListViewItem item = new ListViewItem
                    {
                        Text = "Valor do Id"
                    };

                    item.SubItems.Add("Valor da Descrição");
                    lv2Populate.Items.Add(item);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message.ToString());
            }
        }
    }
}

.

Edited by Fausto Luís
ortografia...

Share this post


Link to post
Share on other sites
ruiribeiro

A minha sugestão é que implementes um interface, por exemplo:

    public interface IModel
    {
        int Id { get; set; }
        string Description { get; set; }
    }

    public class ModelType1 : IModel
    {
        public int Id { get; set; }
        public string Description { get; set; }

        public int SomeOtherProperty1 { get; set; }
    }

    public class ModelType2 : IModel
    {
        public int Id { get; set; }
        public string Description { get; set; }

        public int SomeOtherProperty2 { get; set; }
    }

O teu código terá que ser alterado para contemplar um genérico que implemente o interface

    public static class ListView_ListItems<T>
        where T : IModel
    {
        public static void ListItems(List<T> model, ListView lv2Populate)
        {
            try
            {
                lv2Populate.Items.Clear();

                foreach (T reg in model)
                {
                    ListViewItem item = new ListViewItem
                    {
                        Text = reg.Id.ToString()
                    };

                    item.SubItems.Add(reg.Description);
                    lv2Populate.Items.Add(item);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message.ToString());
            }
        }
    }

E para terminar, um exemplo de utilização com 2 listview e 2 listas de objetos que implementam o mesmo interface. O teu método tornou-se generico para qualquer lista de objetos, desde que implementem o interface.

        private void Form1_Load(object sender, EventArgs e)
        {

            List<ModelType1> listOfModelType1 = new List<ModelType1>();
            listOfModelType1.AddRange(new List<ModelType1> {
                new ModelType1 { Id = 1, Description = "ModelType1Description1", SomeOtherProperty1 = 1 },
                new ModelType1 { Id = 2, Description = "ModelType1Description2", SomeOtherProperty1 = 2 }
            });

            List<ModelType2> listOfModelType2 = new List<ModelType2>();
            listOfModelType2.AddRange(new List<ModelType2> {
                new ModelType2 { Id = 1, Description = "ModelType2Description1", SomeOtherProperty2 = 1 },
                new ModelType2 { Id = 2, Description = "ModelType2Description2", SomeOtherProperty2 = 2 }
            });


            ListView_ListItems<ModelType1>.ListItems(listOfModelType1, listView1);
            ListView_ListItems<ModelType2>.ListItems(listOfModelType2, listView2);
        }

Espero ter ajudado.


.NET/T-SQL, JAVA, PHP, Javascript Developer | Business Intelligence | Gestão de Sistemas de Informação Empresariais

Share this post


Link to post
Share on other sites
Fausto Luís

Boas, 

Funcionou muito bem, muito obrigado pela sua disponibilidade.

Junto o código final de um de 20 forms, onde foi implementada a melhoria!

Sem querer abusar... estou a migrar uma aplicação antiga (windows forms), com uma dimensão média, que usava o método das 3 camadas. Preciso de fazer melhorias, mas o código não é meu...

Estou a usar Repository Pattern, e desejaria usar a injeção de dependências (DI) nos forms (cerca de 70); procurei na net, cheguei a experimentar com o Simple Injector,, mas não resultou bem, por haver um número grande de forms 'filhos'/netos, ....

Sei que o uso do padrão MVP  poderia resolver, mas, além de não o dominar, iria perder demasiado tempo a alterar toda a aplicação.

Conhece algum link interessante? Acha que seria um bom tópico para um post?

Desde já os meus agradecimentos, 

Abraço e desejos de um Bom Ano.

Fausto

 

using Common.Utilities;
using Core.Models;
using Services.Common;
using Services.Presentation;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;

namespace UI
{
    public partial class frmPesquisaAutores : UI.frmPesquisaBase
    {
        private CultureInfo ci;
        private string sLanguage = "";
        private string codeMissingMsg;
        private string titleFormMsg;

        private AutoresService autoresSvc;

        public frmPesquisaAutores()
        {
            InitializeComponent();
			// TODO - implementar com DI / Unit od Work / DDD Light
            autoresSvc = new AutoresService();
        }

        private void frmPesquisaAutores_Load(object sender, EventArgs e)
        {
            sLanguage = Utilitarios.GetLanguage();
            ci = new CultureInfo(sLanguage);
            ResourceManager localizedMessages = GetRes(ci);

            titleFormMsg = localizedMessages.GetString("TituloPesquisaAutores", ci);
            Text = titleFormMsg;
            codeMissingMsg = localizedMessages.GetString("EmptyCodeMessages", ci);
        }

        private ResourceManager GetRes(CultureInfo ci)
        {
            Assembly a = Assembly.Load("PubliSoft");
            ResourceManager rm = new ResourceManager("UI.Lang.langres", a);
            return rm;
        }

        public override void Pesquisar()
        {
            List<Autor> autores = new List<Autor>();
            List<ModelType> listGenericModels = new List<ModelType>();
            string textoPesquisa = txtPesquisa.Text;

            if (rbtId.Checked)  // Pesquisa Por Id
            {
                if (string.IsNullOrEmpty(textoPesquisa))
                {
                    MessageBox.Show(codeMissingMsg, titleFormMsg);
                    txtPesquisa.Select();
                    return;
                }
                else
                {
                    autores = autoresSvc.Query($"Id = {int.Parse(textoPesquisa)}").ToList();
                }
            }
            else // Pesquisa por Descrição
            {
                autores = autoresSvc.Query($"NomeAutor LIKE '%{textoPesquisa}%'").ToList();
            }
              
			// Implementação de 'Generics' - 02/01/2020 - Portugal a Programar
            foreach (var autor in autores)
            {
                listGenericModels.Add(new ModelType { Id = autor.Id, Description = autor.NomeAutor });
            }
           
            ListView_ListItems<ModelType>.ListItems(listGenericModels, lstPesquisa);
        }
    }
}

 

Share this post


Link to post
Share on other sites
Carlos Eduardo Carvalho

otimo.

Edited by Carlos Eduardo Carvalho

Share this post


Link to post
Share on other sites
ruiribeiro

Boa noite Fausto

É possível implementar injecção de dependências em Windows Forms. Na prática, poderás faze-lo em qualquer classe, desde que implementes o padrão de Inversion of Control, ou seja, delegas a responsabilidade da configuração das dependências de uma classe (normalmente no seu construtor) numa framework, como por exemplo o Ninject, Autofac, Windsor, entre outros.

Há alguns anos que apenas utilizo Windows Forms para pequenos projetos, como conversores, parsers, etc... e muitas das vezes a relação custo/benefício faz com que não seja interessante implementar DI nesse tipo de projetos.

Relativamente ao teu caso, a minha sugestão é, já que vais ter que migrar essa aplicação antiga, porque não aproveitar essa oportunidade para aprender o padrão MVC ? Ou seja, os requisitos da aplicação e a sua modelagem já estão mais ou menos definidos, só terias que mudar a sua arquitectura. E sendo assim, poderias desde já iniciar a migração para uma aplicação AspNet Core 3, que apesar de ser multiplataforma, open source, tem logo à partida suporte para injecção de dependências "out of the box". É muito rápida, facilmente poderá ser colocada a rodar dentro de docker e neste momento é a aposta da microsoft no que diz respeito a frameworks de desenvolvimento.

Existem montes de recursos e material de aprendizagem na net (stackoverflow, github, etc.), mas nada melhor que os docs da microsoft: https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-3.1

Se eu estivesse no teu lugar (e já estive há alguns anos atrás) não perderia essa oportunidade.

Neste momento, quando tenho que iniciar um projecto novo, não me ocorre outra opção que não seja a abordagem pelo MVC e pelas suas inúmeras vantagens e facilidade de desenvolvimento, nomeadamente:

  • Separação de responsabilidades
  • Test driven development
  • Integração com diversas frameworks javascript
  • Url restful, que facilitam os SEO (Search Engine Optimization)
  • Injeção de dependências
  • Mocking nos testes unitários

Agora só dependerá das tuas motivações. Boa sorte !!!

 


.NET/T-SQL, JAVA, PHP, Javascript Developer | Business Intelligence | Gestão de Sistemas de Informação Empresariais

Share this post


Link to post
Share on other sites
Fausto Luís

Olá, Rui

Obrigado pelo teu feedback. Foi bastante esclarecedor.

Permite-me que faça um pequeno resumo da minha experiência profissional: tenho 59 ano e estou há um mês numa situação de pré-reformado. Trabalhei como analista-programador numa companhia de seguros durante mais de 25 anos;. No primeiros anos, participei em vários projetos em ambiente mainframe (com Cobol, principalmente), mas nos últimos anos, o paradigma teve de mudar na empresa, e teve que abraçar as tecnologias emergentes, orientadas principalmente para a Web.A minha primeira experiência com o .Net foi com a versão 1.0 (2001)! Comecei com C++, mas rapidamente "evoluí" para o C# ; também houve muito Java pelo meio.

Trabalhei bastante em ambiente Web (MVC, Spring, Struts); na empresa, eram mandatários os testes unitários e de integração,,o uso de IoC/DI (NUnit) e Mocking, etc. Havia pouco desenvolvimento em Windows Forms;; normalmente eram aplicações pequenas (ferramentas de back-end). Como freelancer, trabalhei bastante na área de saúde onde, aí sim, havia muito de Windows Forms mas também de ASP.Net, como ORM, prefiro o Dapper. Nunca fui grande fâ do EF, mas isso seria tema para outra 'conversa'.

O resumo ficou mais ou menos feito.

O MVC não resulta com Windows Forms; tentei de várias formas, sem resultados práticos. Para projetos de média/grande dimensão, a melhor arquitetura, no meu entender, é o MVP, onde a implementação de IoC/DI é feita de forma linear.

Em relação à aplicação que estou a migrar não é possível convertê-la para MVP, que não domino por completo; não estou a conseguir aplicar IoC/DI devido a toda a hierarquia de forms existentes, que são muitos; está a consumir-me muito tempo (nas tentativas...) e não posso ir por esse caminho, há que respeitar prazos (as deadlines, que dor de cabeça!); além disso, a aplicação não é minha, e a documentação é escassa. A minha questão/pedido foi, por isso, mais no âmbito académico... se me conseguires dar um exemplo simples, agradeço (p. exº Program/Main (ThreadScopedLifestyle?) --> Login --> MDI --> MDI childs --> User Controls, ...

Em relação ao .Net Core, vou aguardar mais um pouco; houve muitas alterações do 2.2 para o 3.0. A coisa ainda não está suficientemente estável, ainda há muito 'interop' pelo meio, para resolver questões de compatibilidade estão a tentar disponibilizar o SDK do windows mais fácil de usar, mas ainda não estão lá. Vou aguardar pelo 3.1, principalmente pelos designers para aplicações Desktop. Para aplicações novas, vou considerar seriamente o Core / .Net Standard.

Alonguei-me um pouco, desculpa. Obrigado pela tua paciência.

Fausto

  • Vote 1

Share this post


Link to post
Share on other sites
ruiribeiro

Obrigado pelo teu feedback sobre o teu percurso profisisional... sem duvida um trajeto muito interessante.

Cá vai a minha sugestão de implementação de DI em WindowsForms

Para te facilitar a mudança de IO Container no futuro, sugiro que faças um compositionroot que te vai ajudar a resolver as dependências nos diversos forms... neste exemplo vou utilizar o Ninject (adicionar pelo nuget)

 

1º passo

criação do compositionroot e um modulo ninject(apenas estas classes é que terão a dependencia com o Ninject)

using Ninject;
using Ninject.Modules;

namespace DIWindowsForms
{
    public class CompositionRoot
    {
        private static IKernel _ninjectKernel;

        public static void Wire(INinjectModule module)
        {
            _ninjectKernel = new StandardKernel(module);
        }

        public static T Resolve<T>()
        {
            return _ninjectKernel.Get<T>();
        }
    }
}

2º passo 

Criação de um modulo ninject para estabeleceres os binds dos interfaces com as classes

using Ninject.Modules;

namespace DIWindowsForms
{
    public class ApplicationModule : NinjectModule
    {
        public override void Load()
        {
            Bind(typeof(IRepository)).To(typeof(Repository));
        }
    }
}

3º passo 

Exemplo de criação de um interface para repositorios e sua respetiva implementação

namespace DIWindowsForms
{
    public interface IRepository
    {
        int ReturnRandom(int min, int max);
    }

    class Repository : IRepository
    {
        public int ReturnRandom(int min, int max)
        {
            Random rnd = new Random();
            return rnd.Next(min, max);
        }
    }

}

4º passo

No Program.cs, método main, arrancar a palicação pelo compositionroot

using System;
using System.Windows.Forms;

namespace DIWindowsForms
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            CompositionRoot.Wire(new ApplicationModule());

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(CompositionRoot.Resolve<Form1>());
        }
    }
}

5º passo

exemplo de utilização de DI em 2 forms

using System;
using System.Windows.Forms;

namespace DIWindowsForms
{
    public partial class Form1 : Form
    {
        private readonly IRepository _repository;

        public Form1(IRepository repository)
        {
            _repository = repository;
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            lblRandom.Text = _repository.ReturnRandom(1, 10).ToString();
        }

        private void btnOpenChild_Click(object sender, EventArgs e)
        {
            var frm2 = CompositionRoot.Resolve<Form2>();
            frm2.Show();
        }
    }
}
using System;
using System.Windows.Forms;

namespace DIWindowsForms
{
    public partial class Form2 : Form
    {
        private readonly IRepository _repository;
        public Form2(IRepository repository)
        {
            _repository = repository;
            InitializeComponent();
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            MessageBox.Show(_repository.ReturnRandom(1, 10).ToString());
        }
    }
}

Espero que te tenha ajudado de alguma forma.

 


.NET/T-SQL, JAVA, PHP, Javascript Developer | Business Intelligence | Gestão de Sistemas de Informação Empresariais

Share this post


Link to post
Share on other sites
Fausto Luís

Boa noite Rui, 

Sim, ajudou. Muito obrigado.

Experimentei com uma pequena aplicação que criei para uso pessoal, e a implementação foi pacífica. Li umas coisas sobre o NInject, que apenas conhecia de nome, e pareceu-me bastante poderoso.

Após os testes unitários (usando Mock) com umas centenas de 'registos', o programa ficou muito mais lento do que seria suposto. Parece que é algo de que a 'comunidade' se queixa… Ainda pensei que tivesse feito mal a implementação, mas não encontrei nada de errado: fiz o Bind aos Repositórios e aos Serviços:

    public class ApplicationModule : NinjectModule
    {
        public override void Load()
        {
            // Core (repositórios, interfaces, modelos de dados)

            Bind(typeof(IArrendamentoRepository)).To(typeof(ArrendamentoRepository));
            Bind(typeof(ICategoriaDespesaRepository)).To(typeof(CategoriaDespesaRepository));
            Bind(typeof(ICC_InquilinoRepository)).To(typeof(CC_InquilinoRepository));
            Bind(typeof(IContactRepository)).To(typeof(ContactRepository));
            Bind(typeof(IDespesaRepository)).To(typeof(DespesaRepository));
            Bind(typeof(IFracaoRepository)).To(typeof(FracaoRepository));
            Bind(typeof(IHelpManagerRepository)).To(typeof(HelpManagerRepository));
            Bind(typeof(IHistoricoAtualizacaoRendaRepository)).To(typeof(HistoricoAtualizacaoRendaRepository));
            Bind(typeof(IImovelRepository)).To(typeof(ImovelRepository));
            Bind(typeof(IInquilinoRepository)).To(typeof(InquilinoRepository));
            Bind(typeof(IProprietarioRepository)).To(typeof(ProprietarioRepository));
            Bind(typeof(IRecebimentoRepository)).To(typeof(RecebimentoRepository));
            Bind(typeof(ITipoContactoRepository)).To(typeof(TipoContactoRepository));
            Bind(typeof(ITipoDespesaRepository)).To(typeof(TipoDespesaRepository));
            Bind(typeof(ITipoPropriedadeRepository)).To(typeof(TipoPropriedadeRepository));
            Bind(typeof(ITipoRecebimentoRepository)).To(typeof(TipoRecebimentoRepository));

            Bind(typeof(IUtilizadorRepository)).To(typeof(UtilizadorRepository));
            Bind(typeof(IDataSecurityRepository)).To(typeof(DataSecurityRepository));
            Bind(typeof(IUserAuthenticationRepository)).To(typeof(UserAuthenticationRepository));

            // Serviços - Dependências do UI

            Bind(typeof(IUtilizadorService)).To(typeof(UtilizadorService));
            Bind(typeof(IDataSecurityService)).To(typeof(DataSecurityService));
            Bind(typeof(IUserAuthenticationService)).To(typeof(UserAuthenticationService));

            Bind(typeof(IUserService)).To(typeof(UserService));

            Bind(typeof(IArrendamentoService)).To(typeof(ArrendamentoService));
            Bind(typeof(ICategoriaDespesaService)).To(typeof(CategoriaDespesaService));
            Bind(typeof(ICC_InquilinoService)).To(typeof(CC_InquilinoService));
            Bind(typeof(IContactosService)).To(typeof(ContactosService));
            Bind(typeof(IDespesaService)).To(typeof(DespesaService));
            Bind(typeof(IFracaoService)).To(typeof(FracaoService));
            //Bind(typeof(IHelpManagerService)).To(typeof(HelpManagerService));
            //Bind(typeof(IHistoricoAtualizacaoRendaService)).To(typeof(HistoricoAtualizacaoRendaService));
            Bind(typeof(IImovelService)).To(typeof(ImovelService));
            Bind(typeof(IInquilinoService)).To(typeof(InquilinoService));
            Bind(typeof(IProprietarioService)).To(typeof(ProprietarioService));
            Bind(typeof(IRecebimentoService)).To(typeof(RecebimentoService));
            Bind(typeof(ITipoContactoService)).To(typeof(TipoContactoService));
            Bind(typeof(ITipoDespesaService)).To(typeof(TipoDespesaService));
            Bind(typeof(ITipoPropriedadeService)).To(typeof(TipoPropriedadeService));
            Bind(typeof(ITipoRecebimentoService)).To(typeof(TipoRecebimentoService));

            Bind(typeof(IFormService)).To(typeof(FormService));
        }
    }

Nos Forms, penso que tb apanhei a ideia:

               if (mutex.WaitOne(0, false))
                {
                    CompositionRoot.Wire(new ApplicationModule());

                    string ExeName = System.Reflection.Assembly.GetEntryAssembly().Location;

                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);

                    try
                    {
                        Application.Run(CompositionRoot.Resolve<FormLogin>());
                    }
                    catch (Exception ex)
                    {
                        throw new ApplicationException(ex.Message);
                    }
                }
                else
                {
                    MessageBoxAdv.Show("Uma instância do programa já está em execução...\r\n\r\nVerifique, p.f.",
                        "HouseRentalSoft", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }

Nos Serviços:

   public class DespesaService : IDespesaService
    {
        private readonly IDespesaRepository _repoDespesas;
        private readonly IArrendamentoRepository _repoArrendamento;
        private readonly IFracaoRepository _repoFracao;

        public DespesaService(IDespesaRepository repoDespesas, 
            IArrendamentoRepository repoArrendamento, 
            IFracaoRepository repoFracao)
        {
            _repoDespesas = repoDespesas;
            _repoArrendamento = repoArrendamento;
            _repoFracao = repoFracao;
        }
...

Aqui há algo que ainda me confunde... só injeto dependências dos rRpositórios na camada de Serviços, não nos rRpositórios em si, uma vez que  estes não têm construtores(…); é necessário fazer o Bind aos Repositórios? (pergunta de 'maçarico',).

...

Li algures que o 'Bind' poderia ser simplificado, usando 'Convention.Extensions', em vez de fazer Bind a cada uma das classes (experimentei, mas não resultou…),

 var kernel = new StandardKernel();
    kernel.Bind(t => t.FromThisAssembly()
                      .SelectAllClasses()
                      .BindAllInterfaces());

mas julgo que a lentidão não será causada por isso.  Será o facto do Ikernel ser estático (Service Locator)? Poder estar a fazer Bind aos repositórios?

Falei nisto, porque, na aplicação que estou a migrar, não pode haver degradação na performance, os utilizadores são muito 'exigentes'... Daí que não sei se vou usar injeção de dependências, para já está com 'newing' nos objetos. Vou ler mais sobre o Ninject, pode ser que descubra algo que me faça mudar de ideias

  public class MediaService : IMediaService
    {
        private readonly MediaRepository repo;
        public MediaService() => repo = new FracaoRepository();

...

Já agora aproveito a oportunidade (e a tua paciência) para te fazer uma pergunta: tenho alguns forms que aceitam parâmetros no construtor.; há alguma forma fácil de implementar? Com o 'new()',  passo valores a propriedades que são lidas no form, e só depois chamo o 'Show();.

Mais uma vez, os meus agradecimentos.

Fausto.

Share this post


Link to post
Share on other sites
Fausto Luís

PS: 'gralha', onde se lê 'public MediaService() => repo = new FracaoRepository();, deveria estar new MediaRepository()

Share this post


Link to post
Share on other sites
Fausto Luís

Desculpa, faltou mostrar como faço a injeção nos Forrms:

:

public partial class FormLogin : Form
    {
        IUserAuthenticationService _userAuthenticationSvc;
        IUtilizadorService _userSvc;

        public FormLogin(IUserAuthenticationService userAuthentication, IUtilizadorService usersSvc)
        {
            _userAuthenticationSvc = userAuthentication;
            _userSvc = usersSvc;

            InitializeComponent();
        }
...

Todos os forms comunicam com os repositórios através da camada de Serviços, daí a minha pergunta sobre fazer Bind aos repositórios (…)

Share this post


Link to post
Share on other sites
Fausto Luís

Olá, de novo.

Claro que é preciso fazer  Bind aos repositórios… senão, como é que os serviços conheciam os repositórios injetados? : -)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.