如何在ViewModel不可见时正确处理DispatcherTimer / Queue

时间:2018-04-10 07:56:53

标签: c# wpf

在具有MVVM模式的WPF应用程序中,我有一个DockManager,它根据客户的配置显示不同的窗格。

其中一些View / ViewModel使用DispatcherTimer来处理来自Queue的数据,经过一段时间后才通过并处理队列(选择此选项是因为数据以非常高的频率处理并直接绑定到DataItems会降低性能

进行一些测试我发现(并且这是正确的)即使未显示View / ViewModel也会调用DispacterTimer,这会占用主线程上的资源,这样可以更好地执行其他工作,然后更新无用的ViewModel(因为数据经常更新,因此当用户将窗格推到顶部时,99%的更新数据都是旧的

我想知道处理这些数据的最佳方法是什么。首先,我想跳过更新并将其委托给以后的进程(当视图被推到顶部时,但这样我在显示最新数据之前还有很多工作要做。

这是调度员的嘀嗒事件

    private void DispatcherTimer_Tick(object sender, EventArgs e)
    {
        if (ScrollViewer != null)
        {
            HorizontalScrollViewOffset = ScrollViewer.HorizontalOffset;
            VerticalScrollViewOffset = ScrollViewer.VerticalOffset;
        }

        lock (_queueLock)
        {
            while (queue.Count > 0)
            {
                var deal = queue.Dequeue();

                Serilog.Log.Verbose($"Deal base - Dequeued {deal.Deal.Id} from deal queue");
                if (deal.Status == DealStatus.New && !dealmapping.ContainsKey(deal.Deal.Id))
                {
                    lock (DealLock)
                    {
                        Deals.Add(deal.Deal);
                        dealmapping = Deals.ToDictionary(x => x.Id, y => y);
                    }

                    Serilog.Log.Verbose($"Deal base - Added {deal.Deal.Id} to deals");
                }
                else
                {
                    lock (DealLock)
                    {
                        if (dealmapping.ContainsKey(deal.Deal.Id))
                        {
                            var oldItem = dealmapping[deal.Deal.Id];

                            var index = Deals.FindIndex(x => x.Id == deal.Deal.Id);
                            //var index = Deals.IndexOf(oldItem);
                            if (index > -1)
                            {
                                if (deal.Status == DealStatus.Updated)
                                {
                                    //Deals[index] = deal.Deal;

                                    Deals[index].PopulateWith(deal.Deal);

                                    Deals[index].IsChanged = true;

                                    Serilog.Log.Verbose($"Deal base - Updated {deal.Deal.Id} inside deals");
                                }
                                else
                                {
                                    Deals.Remove(Deals.First(x => x.Id == deal.Deal.Id));
                                }
                            }

                            lock (DealLock)
                            {
                                dealmapping = Deals.ToDictionary(x => x.Id, y => y);
                            }
                        }
                    }
                }

                Serilog.Log.Verbose($"Deal base - Updated lookup table with deal {deal.Deal.Id}");
            }

        }
    }    

我的第一个想法是添加if(!IsActive) return,但这不会处理队列,有什么建议吗?

2 个答案:

答案 0 :(得分:0)

我认为你可能是最好的" Processing"他们进来的时候。 部分原因是这个过程变得更加复杂,但无论如何都会将声音解耦为一个好的计划。

我认为得到数据的事情会更正确地#34;被认为是模型而不是视图模型。 我会在后台线程上运行它,将数据抓取到列表中。如果有很多话,可能是一份循环清单。在索引0处插入新条目,并删除索引为100或1000或其他任何内容的任何条目。 然后每10或20消息它获得新数据。 我可能使用mvvmlight messenger,但如果你的项目有prism,你可以使用eventaggregator。甚至只是一个静态桥接类作为中介。 从视图绑定附加属性以告知视图模型视图是否可见。 视图模型得到了#34;我得到了新闻"信息。 如果它的视图是可见的,那么它将从模型中重新读取数据。 如果它从isvisible false变为true,则重新从模型中读取数据。 如果需要显示大量列的大量记录,则需要保持较小的集合大小或保留多少数据。 请记住,未呈现的记录可以虚拟化,呈现数据集合的主要成本在视图中。

如果您确实需要担心该列表的大小,则可能需要采用更复杂的方法。 将新条目添加到队列(以及)。 实例化视图模型时,它会从模型中获取所有数据,如上所述。 否则,它会收到有新条目的消息。 它从fifo队列中读取新的,并将每个作为项目0插入到视图边界的observablecollection中。 该模型可以暴露该队列,或者,它可以在一个静态桥接对象中。

顺便说一下。 凭借高数据吞吐量,每次记录都可能是一个巨大的开销。

答案 1 :(得分:0)

这就是y管理的方式......您必须选择处理数据的位置并在ViewModel中对其进行转换。唯一的办法就是在调度程序线程上填充observable ....

Mainviewmodel

public ViewModel()
{
    try
    {
        Messenger.Default.Register<NotificationViewModelRefresh>(this, HandleRefreshAction);
    }
    catch (Exception error)
    {
        BusinessLogger.Manage(error);
    }
}

private void HandleRefreshAction(NotificationViewModelRefresh msg)
{
    if (!this.IsVisible)
        return;

子视图模型

public View()
{
    InitializeComponent();

    if (!DesignerProperties.GetIsInDesignMode(this))
    {
        this.IsVisibleChanged += (o, e) =>
        {
            if (this.IsVisible)
                (this.DataContext as ViewModel).Refresh();
        };
    }
}

子视图

Task tk = Task.Factory.StartNew(() =>
    {
        if (System.Threading.Monitor.TryEnter(_RefreshLocker))
        {
            try
            {
                VisualHelper.InvokeBackground(() =>
                {

在计时器的刷新或更一般的流程任务中......

private static void BeginInvoke(DispatcherPriority prio, Action action)
    {
        try
        {
            if (Application.Current != null)
            {
                if (Application.Current.Dispatcher.CheckAccess())
                    action();
                else
                    Application.Current.Dispatcher.BeginInvoke(prio, (ThreadStart)(() => action()));
            }
        }
        catch (Exception err)
        {
            BusinessLogger.Manage(err);
        }
    }

视觉助手提取物

import React, { Component } from 'react';
import { Container, Header, Content, Button, Left, Icon, Body, Title, Right } from 'native-base';
import { View, StyleSheet, TextInput, ScrollView, ListView, Text } from 'react-native';

export default class App extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {text: '', dataSource: ds.cloneWithRows(['hi', 'My Name is adam']),};
  }

  render() { 
    return (
      <Container style={styles.container}>
        <ListView
          dataSource={this.state.dataSource}
          style={styles.list}
          renderRow={(rowData) => 
              <Text style={styles.senderMessageText}>
                {rowData}
              </Text>
            }
        />
      </Container>
    );
  }
}


const styles = StyleSheet.create({
  container:{
    backgroundColor: 'green'
  },
  list:{
    backgroundColor:'blue'
  },
  senderMessageText: {
    backgroundColor:'red',
    padding: 8,
    marginTop: 10
  }
});