Pergunta

Eu tenho um aplicativo WPF onde PageItems são objetos de modelo.

A minha principal ViewModel tem uma ObservableCollection de PageItemViewModels , cada um construir-se do seu objeto correspondente modelo PageItem.

Cada PageItemViewModel herda da classe abstrata BaseViewModel , a fim de obter a funcionalidade INotifyPropertyChanged.

Cada PageItemViewModel também implementa a IPageItemViewModel , a fim de se certificar de que tem as propriedades necessárias.

Eu eventualmente terá cerca de 50 páginas por isso quero eliminar qualquer código desnecessário :

  • RESOLVIDO (veja abaixo) : se existe uma maneira que eu posso obter aulas PageItemViewModel para IDCODE herdar e Título para que eu não tenho para implementá-las em cada classe? Eu não posso colocá-los em BaseViewModel uma vez que outros ViewModels herdá-la, que não precisa dessas propriedades, e eu não posso colocá-los em IPageItemViewModel, pois é apenas uma interface. Eu entendo que eu preciso herança múltipla para isso que C # não suporta
  • RESOLVIDO (veja abaixo) : se existe uma maneira que eu possa se livrar do interruptor declaração, por exemplo, de alguma forma usar reflexão em vez disso?

Abaixo está uma aplicação stand-alone Console que demonstra o código que eu tenho na minha WPF aplicativo:

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {

        }
    }

    public interface IPageItemViewModel
    {
        //these are the properties which every PageItemViewModel needs
        string IdCode { get; set; }
        string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

reformulado: Interface alterado para classe abstrata
using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelPageItemBase> pageItemViewModels = new List<ViewModelPageItemBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }

            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.IdCode, pageItemViewModel.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

Resposta à eliminação declaração Switch:

Graças Jab:

string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
string viewModelName = assemblyName + ".ViewModels.PageItem" + StringHelpers.ForcePascalNotation(pageItem.IdCode) + "ViewModel";
var type = Type.GetType(viewModelName);
var viewModel = Activator.CreateInstance(type, pageItem) as ViewModelBase;
AllPageViewModels.Add(viewModel);
Foi útil?

Solução

Uma solução que não é muito bonita, mas obras, seria a convenção de utilização para se livrar da instrução switch. Isso pressupõe que você pode alterar as IdCodes ou pelo menos modificar o caso para coincidir com o ViewModel.

    var type = Type.GetType("PageItem" + pageItem.IdCode + "ViewModel");
    var viewModel = Activator.CreateInstance(type) as ViewModelBase;
    pageItemViewModels.Add(viewModel);

Note que você deve adicionar verificação de erro aqui, há um par de pontos de falha aqui. É, no entanto, melhor do que ter de manter uma instrução switch sempre crescente.

Outras dicas

Você pode criar uma classe que herda de BaseViewModel que irá implementar essas duas propriedades -. Suas classes PageItemViewModel que precisam este poderia então herdar de que

Como sugerido por Paddy, Acabei de criar uma classe abstrata adicional, PageViewModelBase, com aquelas auto-props definido:

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : PageViewModelBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : PageViewModelBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {


        }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public abstract class PageViewModelBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

Por que você não pode colocar um método virtual GetViewModel() em sua classe PageItem base que retorna o modelo de vista apropriada?

   foreach (PageItem pageItem in pageItems)
   {
       pageItemViewModels.Add(pageItem.GetViewModel());
   }

A única coisa que imediatamente se parece com um cheiro de código é o uso de propriedades "id" - isso geralmente pode ser substituído por polimorfismo. Então você iria substituir a declaração switch com o código acima.

Editar:

Se sua classe PageItem não sabe nada sobre o seu modelo de vista, então não pode ser implementado desta forma. Basicamente, você precisa de uma fábrica, que você já tem (de forma).

Normalmente, eu tenho uma lista de relações (PageItem para ViewModel), o que no seu caso ser um Dictionary<String, Type>. Em seguida, você pode preencher esta lista durante a inicialização e ter o modelo de visão adequada instanciado mais tarde.

Para usar a reflexão para construir esta lista, você pelo menos necessidade de saber programaticamente qual item de página é suportado por um modelo de vista. Para esse efeito, você pode usar um atributo personalizado para decorar você classe, por exemplo:.

public class SupportsPageItemAttribute : Attribute
{
    private readonly string _id;
    public string ID
    {
        get { return _id;}
    }

    public SupportsPageItemAttribute(string id)
    {
        _id = id;
    }
}

E, em seguida, usar esse atributo para definir quais PageItem seu modelo pode aceitar:

[SupportsPageItemAttribute("manageCustomers")
public class PageItemManageCustomersViewModel
{
   // ...
}

E, em seguida, você usar o reflexo para obter todas as classes que implementam IPageItemViewModel e verificar seus atributos para obter a seqüência PageItem id.

Por exemplo (sem muita verificação de erros):

Dictionary<String, Type> modelsById = new Dictionary<String, Type>();
String viewModelInterface = typeof(IPageItemViewModel).FullName;

// get the assembly
Assembly assembly = Assembly.GetAssembly(typeof(IPageItemViewModel));

// iterate through all types
foreach (Type viewModel in assembly.GetTypes())
{
    // get classes which implement IPageItemViewModel
    if (viewModel.GetInterface(viewModelInterface) != null)
    {
        // get the attribute we're interested in
        foreach (Attribute att in Attribute.GetCustomAttributes(viewModel))
        {
            if (att is SupportsPageItemAttribute)
            {
                // get the page item id
                String id = (att as SupportsPageItemAttribute).ID;

                // add to dictionary
                modelsById.Add(id, viewModel);
            }
        }
    }
}

Por outro lado, existem vários quadros Inversão de controle que você pode considerar em vez de fazer o trabalho de reflexão desagradável mesmo.

Uma possível solução é inverter a relação entre PageItem e PageItemViewModel em seu código. Agora, você está gerando um PageItemViewModel baseado em um PageItem, mas o que se criou os PageItemViewModels primeiro e depois no construtor de cada PageItemViewModel, você criou o PageItem apropriado? Isso elimina a necessidade para o switch e torna as coisas mais limpo, porque agora a sua visão de modelo é responsável pela modelo, em vez do modelo sendo responsável pela visão de modelo.

Um exemplo com base no seu código atual:

using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<ViewModelPageItemBase> pageItemViewModels = PageItemViewModels.GetAll();

            // No switch needed anymore. Each PageItem's view-model contains its PageItem
            // which is exposed as property of the view-model.
            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.PageItem.IdCode, pageItemViewModel.PageItem.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel()
        {
            PageItem = new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers" };
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel()
        {
            PageItem = new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees" };
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //The PageItem associated with this view-model
        public PageItem PageItem { get; protected set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    // Replaces PageItems class
    public class PageItemViewModels
    {
        // Return a list of PageItemViewModel's instead of PageItem's.
        // Each PageItemViewModel knows how to build it's corresponding PageItem object.
        public static List<PageItemViewModelBase> GetAll()
        {
            List<PageItemViewModelBase> pageItemViewModels = new List<PageItemViewModelBase>();
            pageItemViewModels.Add(new PageItemManageCustomersViewModel());
            pageItemViewModels.Add(new PageItemManageEmployeesViewModel());
            return pageItemViewModels;
        }
    }
} 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top