6

TabItemに複数のを表示するサポートツールに取り組んでいますTabControl。それぞれTabItemが従業員を表し、これらの各従業員の中に、追加のを含むTab別の従業員がいます。これらは、その従業員のOutlookフォルダを表します(「作業中」、「完了」など)。これらの各フォルダには、そのOutlookフォルダに関連するのにバインドされたが含まれています。これらは膨大なコレクションではありません。1つあたりのアイテムはわずか12個程度です。合計すると、すべてのアイテムで100個程度になる可能性があります。TabControlTabItemTabItemTabItemListBoxObservableCollectionMailItemListBoxTabItem

私が現在アプリケーションを構築している方法は、アプリが起動し、適切な従業員タブとサブタブが画面に表示されることです。このプロセスはかなり迅速で、私は満足しています。Global.System.TimerすべてのフォルダーTabItemのコードビハインドが同期されるスタティックを作成しました。したがって、5分ごとに、アプリケーションはすべてをクリアしObserverableCollection、Outlookフォルダを再スキャンします。

問題は、スキャンプロセスによってアプリケーションが停止することです。を使用してBackgroundWorkerOutlookからメールをバックグラウンドプロセスとして収集し、List<MailItem>オブジェクトをRunWorkerCompletedメソッドに渡してthis.Dispatcher.BeginInvoke、それぞれをクリアするプロセスを実行しObservableCollectionてから、アイテムをList<MailItem>後ろからに追加しようとしましたObservableCollection。これDispatcherを低い優先度に設定しました。

ListBoxそれにもかかわらず、アプリケーションはスキャン/入力プロセス中に非常に不格好な感じです。私はこれをより良く設計する方法がはっきりしていません、そして私はこれにいくらか新しいことを認めます。それぞれのをクリアするObservableCollectionのは非効率的ですが、Outlookのフォルダー変更イベントは特に信頼できるものではないため、すべてのが表示されるように、ブルートフォース再スキャンを時々実行する必要がありますMailItem

以下は、を含むWPFコントロールのコードですListBoxListBoxこれらのコントロールは一度に約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 はモデル、ビュー、およびビュー モデルであり、モデルに変更がある場合、モデルはビュー モデルを更新し、ビュー モデルはビューを更新します。これは、「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 に答える