2013-02-14 69 views
5

我正在开发一个支持工具,该工具在TabControl中显示多个TabItem。每个TabItem代表一名雇员,并且在这些雇员Tab中的每一个中都有另一个TabControl,其中包含额外的TabItem。这些TabItem代表该员工的Outlook文件夹(如“正在工作”,“已完成”等)。这些文件夹中的每一个都包含ListBox,该ListBox绑定到与该Outlook文件夹相关的ObservableCollectionMailItem。这些并不是庞大的收藏品 - 每ListBox只有十几件。尽管在所有的TabItem中总共可以有100个项目左右。在填充多个ListBox时创建响应式WPF UI的诀窍是什么?

我目前构建应用程序的方式是,应用程序启动并使用适当的员工选项卡和子选项卡填充屏幕。这个过程相当快,我很高兴。我创建了一个静态Global.System.Timer,即所有文件夹TabItem的代码隐藏都与之同步。因此,每5分钟应用程序将清除所有ObserverableCollection s并重新扫描Outlook文件夹。

问题是扫描过程会导致应用程序停止。我已经使用BackgroundWorker从Outlook邮件收集作为后台进程,然后通过一个List<MailItem>对象到RunWorkerCompleted方法然后运行this.Dispatcher.BeginInvoke过程,清除各自ObservableCollection然后加上从List<MailItem>ObservableCollection的项目尝试。我甚至将Dispatcher设置为较低的优先级。

尽管如此,在扫描/填充ListBox过程中,应用程序仍然非常笨重。我不清楚如何更好地设计这一点,我承认我对此有点新鲜。我意识到清除ObservableCollection中的每一个都是低效的,但Outlook文件夹更改事件不是特别可靠,所以我需要每隔一段时间进行一次蛮力重新扫描,以确保所有MailItem都被表示。

下面是我的WPF控件的代码,其中包含ListBox。请记住,这些ListBox控件中大约有10个同时处于活动状态。

// This entire UserControl is essentially a ListBox control 
public partial class TicketListView : UserControl 
    { 
     private TicketList _ticketList; //this is the ObservableCollection object 
     private Folder _folder;   // Outlook Folder 

     public TicketListView(Folder folder) 
     { 
      InitializeComponent(); 

      _ticketList = this.FindResource("TicketList") as TicketList; 
      _folder = folder; 

      GlobalStatics.Timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed); 
     } 

     private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
     { 
      Refresh(); 
     } 

     private void Refresh() 
     { 
      BackgroundWorker worker = new BackgroundWorker(); 

      worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
      worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
      worker.RunWorkerAsync(); 
     } 

     private void worker_DoWork(object sender, DoWorkEventArgs e) 
     { 
      List<MailItem> tickets = new List<MailItem>(); 
      string filter = TicketMonitorStatics.TicketFilter(14); 
      Items items = _folder.Items.Restrict(filter); 

      try 
      { 
       foreach (MailItem mi in items.OfType<MailItem>()) 
       { 
        tickets.Add(mi); 
       } 
      } 
      catch (System.Exception) { } 

      e.Result = tickets; 
     } 

     private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      List<MailItem> tickets = e.Result as List<MailItem>; 

      this.Dispatcher.BeginInvoke(new System.Action(delegate 
       { 
        _ticketList.Clear(); 
        PopulateList(tickets); 
       })); 

      BackgroundWorker worker = sender as BackgroundWorker; 
      worker.Dispose(); 
     } 

     private void PopulateList(List<MailItem> ticketList) 
     { 
      foreach (MailItem mi in ticketList) 
      { 
       this.Dispatcher.BeginInvoke(new System.Action(delegate 
        { 
         _ticketList.Add(mi); 
        }), System.Windows.Threading.DispatcherPriority.SystemIdle); 
      } 
     } 
    } 
+0

似乎有点奇怪您有这样的小数据这么大的性能问题,我有一个类似的应用程序,选项卡式前景邮件阅读器,其中包括与高达5000多个项目的文件夹,这不挂UI什么样的'Timer'是'GlobalStatics.Timer',我使用'Threading.Timer'来消除'BackgroundWorker'的需要,是否还有与这些邮件项目相关的图像? – 2013-02-14 03:25:32

+0

RunWorkerCompleted处理程序是否在创建BackgroundWorker的线程上运行?如果是这种情况,您应该不需要使用分派器在UI线程上运行代码,因为您已经在UI线程中。 – Andy 2013-03-13 12:44:25

回答

1

您的需求,特别是在WPF中,您不应该使用计时器或后台工作人员来保持视图响应。相反,您应该使用MVVM模式设计您的应用程序。 MVVM是模型,视图和视图模型,如果模型发生变化,模型会更新视图模型,视图模型会更新视图。这是通过继承“INotifyPropertyChanged”接口完成的。

下面是一个简单的例子

的XAML部分:

<Window x:Class="SimpleMVVM.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="259" Width="445"> 
    <Grid Margin="0,0,2,-3"> 
     <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="10,33,0,0" VerticalAlignment="Top" Width="75"/> 
     <Label x:Name="label" Content="{Binding Name}" HorizontalAlignment="Left" Margin="103,23,0,0" VerticalAlignment="Top" Width="220" BorderBrush="Black" BorderThickness="1" Height="32" Padding="0"/> 

    </Grid> 
</Window> 

和部分的.cs

using System.ComponentModel; 
using System.Windows; 

namespace SimpleMVVM 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     private AnimalViewModel _animal= new AnimalViewModel(); 

     public MainWindow() 
     { 
      InitializeComponent(); 

      DataContext = _animal; 
      button.Click += (sender, e) => _animal.Name = "Taylor" ; 
     } 
    } 

    public class AnimalViewModel : AnimalModel 
    { 
     public AnimalViewModel() 
     { 
     }   
    } 

    public class AnimalModel : INotifyPropertyChanged 
    { 
     private string _name; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       if (_name != value) 
       { 
        _name = value; 
        OnPropertyChanged("Name"); 
       } 
      } 
     } 

     private void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName); 
       PropertyChanged(this, args); 
      } 
     } 
    } 

} 

没有想象按钮点击是调度触发更新,模型首先更新触发属性更改事件以更新视图。

使用此模式,您的代码将非常可靠。

我希望这会有所帮助。

问候 Jegan

+0

呃。你的视角模型在哪里?我只看到视图和模型。 – 2013-03-13 10:35:08

+0

按照您指出的方式编辑代码! – Jegan 2013-03-13 10:58:10

+0

你的代码是好的,但我不确定你是否可以称之为MVMC。我从来没有见过虚拟机继承自M – 2013-03-13 11:51:20