[英]What is the trick to creating a responsive WPF UI when populating multiple ListBoxes?
我正在開發一個支持工具,它在TabControl
中顯示多個TabItem
。 每個TabItem
代表一個員工,並且在每個員工Tab
都有另一個TabControl
,其中包含其他TabItem
。 這些TabItem
表示該員工的Outlook文件夾(如“正在工作”,“已完成”等)。 這些文件夾TabItem
的每一個都包含一個ListBox
,該ListBox
綁定到與該Outlook文件夾相關的MailItem
的ObservableCollection
。 這些不是巨大的集合 - 每個ListBox
只有十幾個項目。 雖然總的來說,在所有TabItem
,可以想象有100個左右的項目。
我目前構建應用程序的方式是應用程序啟動並使用相應的員工選項卡和子選項卡填充屏幕。 這個過程相當快,我很高興。 我創建了一個靜態Global.System.Timer
,所有文件夾TabItem
的代碼隱藏都與之同步。 因此,應用程序每隔5分鍾清除所有ObserverableCollection
並重新掃描Outlook文件夾。
問題是掃描過程使應用程序停止。 我嘗試使用BackgroundWorker
從Outlook收集郵件作為后台進程,然后將List<MailItem>
對象傳遞給RunWorkerCompleted
方法,然后運行this.Dispatcher.BeginInvoke
進程清除相應的ObservableCollection
然后添加List<MailItem>
的項目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中,您不應該使用計時器或后台工作程序來保持視圖響應。 相反,你應該用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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.