我目前正在使用WAF (WPF Application Framework)进行WPF编程。 我真的很喜欢为我的应用程序中的每个小视图单元创建一个自己的ViewModel,我随后以这种方式实现了它。
在我的项目中,我得到了一个复杂的列表,其中每个list-element也包含一个列表。由于复杂性,每个list和list-list元素都是一个自己的ViewModel。 “最坏情况”场景总共包含60-90个视图模型,仅用于列表视图。
(这是一个问题列表,其中每个问题都有一个带有评级和其他ui元素的答案列表。)
此实现效果很好,但性能非常糟糕。在分析后,我发现当我在一组问题之间切换时,错误导致创建我的ViewModel(因为必须再次生成整个列表)。
当我在问题集之间切换时,我不能重复使用我的观点1:1,因为问题的数量不同。
但是,我认为我可以重用给定的视图模型,并在必要时添加(如果新集合需要更多视图)更多视图模型。
因此我写了以下工厂:
[Export]
public class ViewModelPerformanceFactory<T> where T : IPerformanceFactoryViewModel
{
private List<T> _collection;
private int _index;
private readonly ExportFactory<T> _exportFactory;
[ImportingConstructor]
public ViewModelPerformanceFactory(ExportFactory<T> exportFactory)
{
_exportFactory = exportFactory;
_index = 0;
}
public void Reset()
{
_index = 0;
if (_collection == null)
{
return;
}
foreach (var elem in _collection)
{
elem.Reset();
}
}
public T Get()
{
if (_collection == null)
{
_collection = new List<T>();
}
if (_collection.Count <= _index)
{
_collection.Add(_exportFactory.CreateExport().Value);
}
return _collection[_index++];
}
}
其中IPerformanceViewModel
只提供Reset
- 清除ViewModel和View的方法。
所以每次加载一个新的问题集时,我都会调用我的ViewModelPerformanceFactory的reset-function来清除所有模型并将索引设置回0(所以如果有人需要一个新的viewmodel实例,它将获得第一个一个创建)。
从理论上讲,这很有效。
现在我的问题/问题:我在问题集之间切换的频率越高,我的应用程序就越慢......这不是viewmodel-objects的加载 - 这很好。我的列表看起来非常非常慢 - 有时甚至会停留几秒钟然后继续积累...
我认为这是一个WAF问题,因为每个ViewModel都会实现一个视图:
protected ViewModel(TView view) : base(view)
{
this.view = view;
}
}
似乎我不能像在WAF中的ViewModels一样轻松重用Views。
有没有人对我提出建议或者可能采用其他方法来加速我的申请?或者有人认为我的整个方法是愚蠢的,我关闭停止编程? ;)
编辑:有时会出现内存/性能泄漏,但每次都无法重现...... :(
答案 0 :(得分:2)
我不确定您的方法或存在性能问题的原因,但这是我解决类似问题的方法。
可以找到完整的解决方案https://github.com/steinborge/ProxyTypeHelper/wiki
我想要达到的目标是能够创建一个通用的&#39;查看模型,然后可以将其分配给datatemplate。数据模板只是一个用户控件。在您拥有大量简单数据维护屏幕的情况下,可以节省大量重复代码。
但有几个问题。数据模板在XAML中不能与泛型一起使用,如果你有很多数据模板,那么你创建了大量的XAML - 特别是如果你想在不同的视图中使用它。在你的情况下,你提到了多达90个视图 - 这将是很多XAML。
解决方案是将模板存储在查找中,并使用内容控件和DataTemplateSelector填充,具体取决于DataContext。所以首先需要注册数据模板/视图:
manager.RegisterDataTemplate(typeof(GenericViewModel<CarType, WPFEventInter.ViewModel.CarTypeViewModel>), typeof(WPFEventInter.UserControls.CarTypesView));
manager.RegisterDataTemplate(typeof(GenericViewModel<Colour, WPFEventInter.ViewModel.ColourViewModel>), typeof(WPFEventInter.UserControls.ColourView));
RegisterDataTemplate只是将datatemplate添加到字典中:
public void RegisterDataTemplate(Type viewModelType, Type dataTemplateType, string Tag="")
{
var template = BuildDataTemplate(viewModelType, dataTemplateType) ;
templates.Add(viewModelType.ToString() + Tag, template);
}
private DataTemplate BuildDataTemplate(Type viewModelType, Type viewType)
{
var template = new DataTemplate()
{
DataType = viewModelType,
VisualTree = new FrameworkElementFactory(viewType)
};
return template;
}
现在使用ContentPresenter控件创建一个视图。这将根据视图的Datacontext显示视图。
DataTemplateSelector如下所示。这将根据datacontext返回相应的视图:
public class ContentControlGenericTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
DataTemplate retVal = null;
try
{
retVal = Core.WPF.Infrastructure.DataTemplateManager.templates[item.GetType().ToString()];
}
catch //empty catch to prevent design time errors..
{
}
return retVal;
}
答案 1 :(得分:1)
如果没有看到整个代码,就很难说出你的问题是什么。但是从您提供的描述中猜测:
绝对切换到DataTemplates和HierarchicalDataTemplates。如果您每次更改数据时都在创建新控件,那么您将永远不会看到出色的性能。这样您也可以使用虚拟化。
您的应用程序变慢的事实确实表明存在内存泄漏。最常见的原因是没有取消订阅的事件。
实例化ViewModels不应占用任何重要时间,因为它们少于100个。如果是这种情况,你应该找出他们为什么要这么久的原因。您不应该重复使用ViewModels包装模型对象。如果你这样做,你需要大量的簿记来重置&#39;他们,或者他们必须是无国籍的,这首先打败了他们的目的。
ViewModel引用View的事实是关于MVVM的主要禁忌。在我的大多数解决方案中,我甚至没有视图类,我将DataTemplates用于除DateTimeBoxes和自定义ComboBox之类的自定义控件之外的所有内容。
答案 2 :(得分:-1)
很难提供解决方案无需查看整个代码以及您的问题:
您可以将代码替换为您的问题。
public class MyICommand<T>: ICommand {
Action _TargetExecuteMethod;
Func<bool> _TargetCanExecuteMethod;
public MyICommand(Action executeMethod) {
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func<bool> canExecuteMethod){
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged() {
CanExecuteChanged(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter) {
if (_TargetCanExecuteMethod != null) {
return _TargetCanExecuteMethod();
}
if (_TargetExecuteMethod != null) {
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime
is longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation public event
EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter) {
if (_TargetExecuteMethod != null) {
_TargetExecuteMethod();
}
}
}