我正在使用
WPF中的一个项目,每秒更新视图30次.据我所知,我正在使用MVVM模式,并且对目前的结果非常满意.但是,我想知道是否有更有效的方法来更新我的主机容器中的VisualCollection内的DrawingVisuals.在我的viewmodel中的每个属性更改中,我发现,删除然后为该viewmodel重新添加一个新的DrawingVisual.随着不断移动的对象,我觉得应该有更好的方法,比如将DrawingVisuals本身直接绑定到viewmodel的属性,但是看起来会是什么样的?随着模拟中模型数量的增加,我需要确保我有一个简化的更新工作流程.我开始关注这个例子:
http://msdn.microsoft.com/en-us/library/ms742254.aspx
我故意避免DependencyProperties并将UserControls绑定到每个viewmodel,因为我需要一个非常有效的绘图画布(因此下面的QuickCanvas).因此除了设计主UI和连接按钮和命令之外,我几乎不需要XAML.请问是否有些事情似乎不清楚或我遗漏了一些重要的事情.谢谢!
可视主机容器(视图):
public partial class QuickCanvas : FrameworkElement { private readonly VisualCollection _visuals; private readonly Dictionary<Guid,DrawingVisual> _visualDictionary; public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof(ObservableNotifiableCollection<IVisualModel>),typeof(QuickCanvas),new PropertyMetadata(OnItemsSourceChanged)); public QuickCanvas() { InitializeComponent(); _visuals = new VisualCollection(this); _visualDictionary = new Dictionary<Guid,DrawingVisual>(); } public ObservableNotifiableCollection<IVisualModel> ItemsSource { set { SetValue(ItemsSourceProperty,value); } get { return (ObservableNotifiableCollection<IVisualModel>)GetValue(ItemsSourceProperty); } } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property.Name == "Width" || e.Property.Name == "Height" || e.Property.Name == "Center") { UpdateVisualChildren(); } } private void UpdateVisualChildren() { if (ItemsSource == null || _visuals.Count == 0) return; foreach (var model in ItemsSource) { var visual = FindVisualForModel(model); if (visual != null) { UpdateVisualFromModel(visual,model); } } } private void UpdateVisualPairFromModel(DrawingVisual visual,IVisualModel model) { visual.Transform = ApplyVisualTransform(visual.Transform,model); } private static void OnItemsSourceChanged(DependencyObject obj,DependencyPropertyChangedEventArgs args) { (obj as QuickCanvas).OnItemsSourceChanged(args); } private void OnItemsSourceChanged(DependencyPropertyChangedEventArgs args) { _visuals.Clear(); if (args.OldValue != null) { var models = args.OldValue as ObservableNotifiableCollection<IVisualModel>; models.CollectionCleared -= OnCollectionCleared; models.CollectionChanged -= OnCollectionChanged; models.ItemPropertyChanged -= OnItemPropertyChanged; } if (args.NewValue != null) { var models = args.NewValue as ObservableNotifiableCollection<IVisualModel>; models.CollectionCleared += OnCollectionCleared; models.CollectionChanged += OnCollectionChanged; models.ItemPropertyChanged += OnItemPropertyChanged; CreateVisualChildren(models); } } private void OnCollectionCleared(object sender,EventArgs args) { _visuals.Clear(); _visualDictionary.Clear(); } private void OnCollectionChanged(object sender,NotifyCollectionChangedEventArgs args) { if (args.OldItems != null) RemoveVisualChildren(args.OldItems); if (args.NewItems != null) CreateVisualChildren(args.NewItems); } private void OnItemPropertyChanged(object sender,ItemPropertyChangedEventArgs args) { var model = args.Item as IVisualModel; if (model == null) throw new ArgumentException("args.Item was expected to be of type IVisualModel but was not."); //TODO is there a better way to update without having to add/remove visuals? var visual = FindVisualForModel(model); _visuals.Remove(visual); visual = CreateVisualFromModel(model); _visuals.Add(visual); _visualDictionary[model.Id] = visual;** } private DrawingVisual FindVisualForModel(IVisualModel model) { return _visualDictionary[model.Id]; } private void CreateVisualChildren(IEnumerable models) { foreach (IVisualModel model in models) { var visual = CreateVisualFromModel(model); _visuals.Add(visual); _visuals.Add(visual); _visualDictionary.Add(model.Id,visual); } } private DrawingVisual CreateVisualFromModel(IVisualModel model) { var visual = model.GetVisual(); UpdateVisualFromModel(visual,model); return visual; } private void RemoveVisualChildren(IEnumerable models) { foreach (IVisualModel model in models) { var visual = FindVisualForModel(model); if (visual != null) { _visuals.Remove(visual); _visualDictionary.Remove(model.Id); } } } protected override int VisualChildrenCount { get { return _visuals.Count; } } protected override Visual GetVisualChild(int index) { if (index < 0 || index >= _visuals.Count) throw new ArgumentOutOfRangeException("index"); return _visuals[index]; } }
IVisuaModel impl:
public class Vehicleviewmodel : IVisualModel { private readonly Vehicle _vehicle; private readonly IVisualFactory<Vehicleviewmodel> _visualFactory; private readonly IMessageBus _messageBus; public Vehicleviewmodel(Vehicle vehicle,IVisualFactory<Vehicleviewmodel> visualFactory,IMessageBus messageBus) { _vehicle = vehicle; _visualFactory = visualFactory; _messageBus = messageBus; _messageBus.Subscribe<VehicleMovedMessage>(VehicleMoveHandler,Dispatcher.CurrentDispatcher); Id = Guid.NewGuid(); } public void Dispose() { _messageBus.Unsubscribe<VehicleMovedMessage>(VehicleMoveHandler); } private void VehicleMoveHandler(VehicleMovedMessage message) { if (message.Vehicle.Equals(_vehicle)) OnPropertyChanged(""); } public Guid Id { get; private set; } public Point Anchor { get { return _vehicle.Position; } } public double Rotation { get { return _vehicle.Orientation; } } public DrawingVisual GetVisual() { return _visualFactory.Create(this); } public double Width { get { return _vehicle.VehicleType.Width; } } public double Length { get { return _vehicle.VehicleType.Length; } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) handler(this,new PropertyChangedEventArgs(propertyName)); } }
IVisualFactory impl:
public class VehicleVisualFactory : IVisualFactory<Vehicleviewmodel> { private readonly IDictionary<string,Pen> _pens; private readonly IDictionary<string,Brush> _brushes; public VehicleVisualFactory(IDictionary<string,Pen> pens,IDictionary<string,Brush> brushes) { _pens = pens; _brushes = brushes; } public DrawingVisual Create(Vehicleviewmodel viewmodel) { var result = new DrawingVisual(); using (var context = result.RenderOpen()) { context.DrawRectangle(_brushes["VehicleGreen"],_pens["VehicleDarkGreen"],new Rect(-viewmodel.Width / 2,-viewmodel.Length / 2,viewmodel.Width,viewmodel.Length)); } return result; } }
解决方法
我发现你在阅读你的帖子时非常有趣且巧妙地做了.我已经用wpf和“实时”问题进行了一些实验,这里有一些我可以根据自己的经验带来的东西:
>我不建议您使用视图模型的完整绑定视图,特别是如果车辆数量不同,您的车辆的属性.实际上,只有在初始化之后,绑定机制才会非常快.初始化确实很慢.因此,如果您倾向于使用此类机制,我建议您使用池来尽可能避免绑定分配.关于它的外观问题的问题……我猜所有与装订有关的事情都会在c#(http://msdn.microsoft.com/en-us/library/ms742863.aspx)的车辆工厂进行?
>我的第二点是半个问题的半个问题:您是否考虑过使用另一种架构模式而不是MVVM?我可以在你的代码中看到你已经实现了很多与view / viewmodel同步相关的东西. MVVM是一种结构,通过viewmodel原理,可以使用数据层轻松连接和分离高度交互的视图.游戏体系结构通常不会像表单应用程序那样分离相同的问题,主要是因为性能,可伸缩性和模块化问题不是同样的挑战.这就是为什么我想知道代理模式,经典的getInputs / think / draw vehicule对象是不是一个好的方法,如果你的目标是获得最大的性能 – >意思是一层应用程序和代理中的多个层.
希望这有点帮助.如果有一些你不理解的点或者你只是不同意,请不要犹豫.请告知我们,我对您的选择非常感兴趣!