在填充多个列表框时创建响应式 WPF UI 的诀窍是什么?

本文关键字:UI WPF 诀窍 是什么 响应 创建 填充 列表 | 更新日期: 2023-09-27 17:58:57

我正在开发一种支持工具,该工具可以在TabControl中显示多个TabItem。 每个TabItem代表一个员工,在每个员工Tab中,还有另一个包含附加TabItemTabControl。 这些TabItem表示该员工的 Outlook 文件夹(如"正在工作"、"已完成"等(。 其中每个文件夹TabItem都包含一个ListBox,该绑定到与该 Outlook 文件夹相关的MailItemObservableCollection。 这些不是巨大的收藏 - 每ListBox只有十几件物品。 虽然,总的来说,在所有TabItem中,可以想象有 100 个左右的项目。

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

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

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

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

在填充多个列表框时创建响应式 WPF UI 的诀窍是什么?

对于您的要求,特别是在 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);
            }
        }
    }
}

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

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

我希望这有所帮助。

问候杰根