在填充多个ListBox时创建响应式WPF UI的技巧是什么?

时间:2013-02-14 03:07:58

标签: c# wpf multithreading backgroundworker dispatcher

我正在开发一个在TabItem中显示多个TabControl的支持工具。每个TabItem代表一名员工,并且在这些员工Tab的每一个中,还有另一个TabControl,其中包含其他TabItem个。这些TabItem代表该员工的Outlook文件夹(如“正在工作”,“已完成”等)。这些文件夹TabItem中的每一个都包含一个ListBox,该ObservableCollection绑定到与该Outlook文件夹相关的MailItemListBox。这些不是巨大的收藏 - 每TabItem只有十几个项目。虽然总的来说,在所有Global.System.Timer中,可以想象有100个左右的项目。

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

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

尽管如此,在扫描/填充ObservableCollection过程中,应用程序非常笨拙。我不清楚如何更好地设计这个,我承认我对此有些新意。我意识到清除每个MailItem是低效的,但是Outlook文件夹更改事件并不是特别可靠,所以我需要每隔一段时间重新扫描一次以确保所有{{1}代表了。

以下是包含ListBox的WPF控件的代码。请注意,这些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);
            }
        }
    }

1 个答案:

答案 0 :(得分: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 Part

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