我有一个WPF应用程序,其中 PageItems 是模型对象。
我的主ViewModel有一个 PageItemViewModels 的ObservableCollection,每个都来自其匹配的PageItem模型对象。
每个 PageItemViewModel 都从抽象类 BaseViewModel 继承,以获得INotifyPropertyChanged功能。
每个 PageItemViewModel 还会实现 IPageItemViewModel ,以确保它具有所需的属性。
我最终会有大约50页,所以我想消除任何不必要的代码:
以下是独立控制台应用,它演示了我在 WPF 应用中的代码:
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;
}
}
}
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;
}
}
}
谢谢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);
答案 0 :(得分:2)
您是否可以创建一个继承BaseViewModel的类来实现这两个属性 - 您需要的PageItemViewModel类可以从中继承。
答案 1 :(得分:1)
根据Paddy的建议,我刚刚创建了一个额外的抽象类,即PageViewModelBase,并定义了这些自动道具:
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;
}
}
}
答案 2 :(得分:1)
为什么不能在基础PageItem类中放置一个GetViewModel()
虚拟方法来返回相应的视图模型?
foreach (PageItem pageItem in pageItems)
{
pageItemViewModels.Add(pageItem.GetViewModel());
}
立即看起来像代码味道的东西是“id”属性的用法 - 这通常可以用多态替换。所以你要用上面的代码替换switch
语句。
修改强>
如果您的PageItem类对您的视图模型一无所知,那么就无法以这种方式实现它。基本上,你需要一个你已经拥有的工厂(在某种程度上)。
我通常有一个关系列表(PageItem到ViewModel),在你的情况下是Dictionary<String, Type>
。然后,您可以在初始化期间填写此列表,并在以后实例化适当的视图模型。
要使用反射来构建此列表,您至少需要以编程方式了解视图模型支持哪个页面项。为此,您可以使用自定义属性来装饰您的类,例如:
public class SupportsPageItemAttribute : Attribute
{
private readonly string _id;
public string ID
{
get { return _id;}
}
public SupportsPageItemAttribute(string id)
{
_id = id;
}
}
然后使用该属性来定义模型可以接受的PageItem:
[SupportsPageItemAttribute("manageCustomers")
public class PageItemManageCustomersViewModel
{
// ...
}
然后,使用反射来获取所有实现IPageItemViewModel的类,并检查它们的属性以获取PageItem id字符串。
例如(没有太多的错误检查):
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);
}
}
}
}
另一方面,您可以考虑使用各种反转控制框架,而不是自己进行讨厌的反射工作。
答案 3 :(得分:1)
一个不太漂亮但有效的解决方案是使用约定来摆脱switch语句。这假设您可以更改IdCodes或至少修改案例以匹配ViewModel。
var type = Type.GetType("PageItem" + pageItem.IdCode + "ViewModel");
var viewModel = Activator.CreateInstance(type) as ViewModelBase;
pageItemViewModels.Add(viewModel);
请注意,您应该在此添加错误检查,这里有几点失败。然而,它比维持不断增长的转换声明更好。
答案 4 :(得分:1)
一种可能的解决方案是在代码中反转PageItem
和PageItemViewModel
之间的关系。现在,您正在基于PageItemViewModel
生成PageItem
,但是如果先创建PageItemViewModel
然后在每个PageItemViewModel
的构造函数中创建,那么您创建了适当的PageItem
?这消除了对switch
的需求并使事情更清晰,因为现在您的视图模型负责模型,而不是负责视图模型的模型。
基于您当前代码的示例:
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;
}
}
}