What is the trick to creating a responsive WPF interface when populating multiple ListBox lists?

I am working on a support tool that displays several TabItemin TabControl. Each TabItemrepresents an employee, and inside each of these employees Tabthere is another TabControlthat contains additional TabItems. These TabItemrepresent the Outlook folders for this employee (for example, Job, Completed, etc.). Each of these folders TabItemcontains ListBox, which is linked to ObservableCollectionfrom MailItem, related to this Outlook folder. These are not huge collections - only about a dozen items per ListBox. Although, in general, in all TabItem, perhaps there may be 100 elements or so.

The way I was currently creating the application is that the application launches and fills the screen with the appropriate tabs and sub-tabs of the employee. This process is pretty fast and I'm happy. I created Static Global.System.Timer, with which all code TabItem-behind code is synchronized . Therefore, every 5 minutes, the application cleans everything ObserverableCollectionand re-scans the Outlook folders.

The problem is that the scanning process is stopping the application. I tried BackgroundWorkerto collect mail from Outlook as a background process, and then pass the object to a List<MailItem>method RunWorkerCompleted, which then starts the process this.Dispatcher.BeginInvoke, which cleans up the corresponding one ObservableCollection, then adds elements from List<MailItem>to ObservableCollection. I even set this Dispatcherto a lower priority.

Despite this, the application is very awkward during the scanning / filling process ListBox. I do not understand how best to do this, and I admit that I am a little new to this. I understand that cleaning each of them is ObservableCollectioninefficient, but the Outlook folder change events are not particularly reliable, so I need to reprogram the re-scan again to ensure that everyone MailItemis represented.

Below is my code for the WPF control that contains ListBox. Keep in mind that about 10 of these controls ListBoxare active immediately.

// 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);
            }
        }
    }
+5
source share
1 answer

, 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

+1

All Articles