6

我正在开发一种支持工具,该工具TabItemTabControl. 每个TabItem代表一个员工,在每个员工Tabs 中都有另一个TabControl包含额外TabItem的 s。这些TabItems 代表该员工的 Outlook 文件夹(如“正在工作”、“已完成”等)。这些文件夹TabItem中的每一个都包含一个ListBox绑定到ObservableCollection属于MailItem该 Outlook 文件夹的一个。这些不是庞大的收藏品——每个ListBox. 尽管总的来说,在所有TabItems 中可能有 100 个左右的项目。

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

问题是扫描过程会使应用程序停止。我曾尝试使用 aBackgroundWorker从 Outlook 收集邮件作为后台进程,然后将一个List<MailItem>对象传递给一个RunWorkerCompleted方法,该方法然后运行一个this.Dispatcher.BeginInvoke清除相应的进程的进程,ObservableCollection然后将项目从List<MailItem>后面添加到ObservableCollection. 我什至将其设置Dispatcher为较低的优先级。

尽管如此,该应用程序在扫描/填充ListBox过程中感觉非常笨重。我不清楚如何更好地设计它,我承认我对此有些陌生。我意识到清除每个ObservableCollections 效率低下,但 Outlook 文件夹更改事件并不是特别可靠,所以我需要每隔一段时间进行一次强力重新扫描以确保所有MailItems 都被表示。

下面是我的 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);
            }
        }
    }
4

1 回答 1

1

根据您的要求,特别是在 WPF 中,您不应该使用计时器或后台工作人员来保持视图响应。相反,您应该使用 MVVM 模式设计您的应用程序。MVVM是Model、View和View Model,这里如果model有变化,model更新View Model,view model更新view。这是通过继承“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);
            }
        }
    }

}

无法想象按钮单击是由调度程序触发的更新,您首先更新模型,触发属性更改事件来更新视图。

使用这种模式,您的代码将更加可靠。

我希望这有帮助。

问候杰根

于 2013-03-13T10:21:05.497 回答