2013-02-10 14 views
6

私はWPFで1秒間に最大30回ビューを更新するプロジェクトに取り組んでいます。私はMVVMパターンを私の知る限り活用しており、今までの結果にはかなり満足しています。しかし、私はホストコンテナのVisualCollection内で私のDrawingVisualsを更新するための効率的な方法がないのだろうかと思っています。私のビューモデル内のすべてのプロパティ変更で、私はそのビューモデルのために新しいDrawingVisualを削除して削除します。常に動いているオブジェクトでは、DrawingVisuals自体をビューモデルのプロパティに直接バインドするなど、より良い方法があるはずですが、そのように見えますか?シミュレーションのモデルの数が増えるにつれ、私は更新のための合理化されたワークフローを確保する必要があります。 http://msdn.microsoft.com/en-us/library/ms742254.aspxWPFのVisualCollectionホストのDrawingVisualをMVVMパターンで更新するより良い方法はありますか?

非常に効率的な描画キャンバス(以下のQuickCanvas)が必要なので、DependencyPropertiesとUserControlsをすべてのビューモデルに慎重に使用しないようにしました。だから私はメインの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)); 
    } 
} 

A 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; 
    } 
} 

答えて

2

私はあなたが非常に興味深いアプローチと巧みましたあなたの投稿を読んでいる間に作られた。私はすでにWPFと「リアルタイム」で、いくつかのいくつかの実験法を行っている問題は、ここで私は私自身の経験から持って来ることができるいくつかのものがあります:

  • 私はあなたがViewModelにしてあなたが完全なバインドビューを使用しrecommandないだろう、特に車両の量が異なる場合、車両の特性。確かに、結合主義は非常に速いです...それが初期化されて初めてです。確かに初期化するのはかなり遅いです。だから、もしあなたがこのような主義を使う傾向があるならば、可能な限り多くのバインディング割り当てを避けるためにプールを使うことをお勧めします。それが持っている外観についてあなたの質問をお返しして...私はすべてのバインディング関連の事があなたの車工場でC#(http://msdn.microsoft.com/en-us/library/ms742863.aspx)で実行されるだろうと思いますか?

  • 私の2番目のポイントは、半分の質問です.MVVM以外のアーキテクチャパターンを使用したことはありますか?あなたのコードでは、view/viewModelの同期に関連することがたくさん実装されていることがわかります。 MVVMは、ViewModelの原則のおかげで、インタラクティブなビューとデータレイヤーを容易にインターフェースすることができます。ゲームのアーキテクチャでは、パフォーマンス、スケーラビリティ、モジュール性の問題は同じ課題ではないため、フォームアプリケーションと同じ懸案事項を分離する傾向がありません。だからこそ、エージェントパターン、古典的なgetInputs/think/drawi Vehicleuleオブジェクトは、あなたの目的が最も性能を発揮するためには良いアプローチではないと思っていた理由です。

これは少し助けました。 あなたが理解していない、あるいは単に好意的ではない点がある場合は、躊躇しないでください。 私たちに知らせてください、私はあなたが作る選択に非常に興味があります!

1

ちょうど最近、MSDNの例であるmentionに基づいて開始し、VisualHostという別名QuickCanvasの構造(DrawingVisualsのビジュアルコレクションを含むFrameworkElement)を使用しました。最初の描画時間は、UIElementsを使用した場合の約3倍高速です。

次に、私は同じことに気づきました:DrawingVisualsを再配置するために、それらを削除してから再描画する必要がありました。これには時間がかかります。観察は、たとえその費用を奪うのが最も大事でした!

部分的な解決策としては、より単純な問題(ジオメトリの重なりがない場合)の中間的な答えがあります。単なるDrawingVisualを削除して描画することができます。これにより、このようなケースでは5の因子でそのようなケースが表示されました。

しかし、VisualHostsを移動するような流暢な再描画が必要な場合(DrawingVisualをグループ化/リフレッシュできる場合は、QuickCanvasを複数配置することができますOnRender、MeasureOverrideなどをInvalidateVisual(ビジュアルホストコンテナ)で起動する方法もCanvas上またはRenderTransform(コードから)にも影響しない方法を見つけられませんでした。

私の必要性は、マウスごとに(例えば、親指で)VisualHostの伸ばしや移動を流暢に更新することです。
あなたが見つけたように、それはOnPropertyChangedだけのようです。

これまでのところ、DrawingVisualsを使用した最初の図面は非常に高速ですが、インタラクティブ機能は起動していません。すべてのハンドラは、インクリメンタルなイベントをローカルで発生させますが、私は、例えばDrawingVisualまたはVisualHostコンテナのコー​​ドの位置を、

   Canvas.SetLeft(element, left + xD); 
       Canvas.SetTop(element, top + yD); 

または

  transform.X += currentPoint.X - anchorPoint.X; 
      transform.Y += currentPoint.Y - anchorPoint.Y; 
      this.RenderTransform = transform; 

(物事を計算するが、ないレンダリングが行われません)。 InvalidateVisual、Update、その他のメカニズムのいずれもVisualHostコンテナを呼び出すQuickCanvasを起動していないようです。

+0

さらに、OFCはOnRenderなどをトリガーしません。これが、UIElementのトリガーメカニズムを回避して時間を節約するために、DrawingVisualsに行った理由です。代わりに、必要なときに自分自身が再描画を行います。 – Ulthien

関連する問題