WPF / C#使用ObservableCollection更新ListBox(目录监视)

时间:2013-09-17 22:40:38

标签: c# wpf data-binding listbox observablecollection

我刚刚发现由于Novell,我无法使用FileSystemWatcher监视目录中的内容更改,因此我必须实现自己的版本。除了我的实现可能效率低下之外,我遇到的一个问题是我的UI中的ListBox没有更新文件列表。

这是我的文件夹类:

   public class FolderWatcher
   {
    // This collection will contain the files in the folder
    public static AsyncObservableCollection<FileItem> folder = new AsyncObservableCollection<FileItem>();

    // Timer
    System.Timers.Timer timer;

    // Path
    string path;

    // Extension type
    string extension;

    //The next line is important to allow system for us to monitor it.  It's a trust setting, and we need Full Trust.
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public void Run(string dirPath, string extensionFilter)
    {
        // Set path and extension
        path = dirPath;
        extension = extensionFilter;

        // Populate the folder list with the current files
        var dirInfo = new DirectoryInfo(path);
        foreach (var currentFile in dirInfo.GetFiles())
        {
            var aFile = new FileItem
            {
                Name = currentFile.Name,
                Path = currentFile.FullName
            };

            folder.Add(aFile);
        }

        // Start the timer
        timer = new System.Timers.Timer(5000);
        timer.AutoReset = true;
        timer.Enabled = true;

        // When timer elapses, raise an event
        timer.Elapsed += new System.Timers.ElapsedEventHandler(UpdateFiles);
    }

    // Update Files
    private void UpdateFiles(object sender, System.Timers.ElapsedEventArgs e)
    {
        // Get directory info
        var dirInfo = new DirectoryInfo(path);

        // Create a temporary list to hold new items
        AsyncObservableCollection<FileItem> temp = new AsyncObservableCollection<FileItem>();

        // Add the items into the temporary list
        foreach (var currentFile in dirInfo.GetFiles())
        {
            var aFile = new FileItem
            {
                Name = currentFile.Name,
                Path = currentFile.FullName
            };

            temp.Add(aFile);
        }

        // Check for new files first, checking if they've been renamed as well
        int count = temp.Count;
        for (int i = 0; i < count; i++)
        {
            // If main folder list doesn't contain a new file, add it
            if (!folder.Contains(temp[i]))
            {
                folder.Add(temp[i]);
            }
        }

        // Now check to see if any files have been removed
        count = folder.Count;
        for (int i = 0; i < count; i++)
        {
            // If temp folder doesn't contain these files, remove them
            if (!temp.Contains(folder[i]))
            {
                folder.RemoveAt(i);
            }
        }
    }
}

的FileItem:

public class FileItem : INotifyPropertyChanged
{
    private string _Name;

    public string Name
    {
        get 
        { 
            return _Name; 
        }
        set
        {
            if (_Name != value)
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }
    }
    private string _Path;

    public string Path 
    { 
        get { return _Path; }
        set
        {
            if (_Path != value)
            {
                _Path = value;
                OnPropertyChanged("Path");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public override string ToString()
    {
            return _Path;
    }
}

修改后的ObservableCollection:

public class AsyncObservableCollection<T> : ObservableCollection<T>
{
    private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

    public AsyncObservableCollection()
    {
    }

    public AsyncObservableCollection(IEnumerable<T> list)
        : base(list)
    {
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the CollectionChanged event on the current thread
            RaiseCollectionChanged(e);
        }
        else
        {
            // Post the CollectionChanged event on the creator thread
            _synchronizationContext.Post(RaiseCollectionChanged, e);
        }
    }

    private void RaiseCollectionChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the PropertyChanged event on the current thread
            RaisePropertyChanged(e);
        }
        else
        {
            // Post the PropertyChanged event on the creator thread
            _synchronizationContext.Post(RaisePropertyChanged, e);
        }
    }

    private void RaisePropertyChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnPropertyChanged((PropertyChangedEventArgs)param);
    }
}

更新:以上是更新后的代码。我正在努力让它正确更新,但其他一切似乎都运行得很好。我会在它完全正常运行时进行更新,以便其他人可以使用。

我最终重写了我的旧代码,它已经失控并难以管理。我决定简化它,现在它正在使用字符串,但可以像我的FileItem一样容易使用...这里是一个片段(它没有拆分成文件,因为这是一个测试应用程序):

    public MainWindow()
    {
        // Must be used (read MSDN documentation)
        InitializeComponent();

        // Our main collection to hold names of files
        this.dir = new ObservableCollection<string>();

        // Bind our collection to the listbox as the source of data
        TheListBox.ItemsSource = dir;

        // Target directory path
        dirInfo = new DirectoryInfo(@"C:\Temp");

        // Set timer (2 seconds is optimal for overhead and quick updates... increase or decrease based on system performance)
        timer = new System.Timers.Timer(2000);
        timer.AutoReset = true;
        timer.Enabled = true;

        // Add an event handler for timer elapsing
        timer.Elapsed += new System.Timers.ElapsedEventHandler(UpdateFiles);
    }

    // Updates Files in the directory collection
    private void UpdateFiles(object sender, System.Timers.ElapsedEventArgs e)
    {
        // Temporary collection that will help us compare files and avoid errors
        ObservableCollection<string> temp = new ObservableCollection<string>();

        // Since we're using a timer, we have to invoke the changes into the main thread, or we'll get an access exception on dir
        System.Windows.Application.Current.Dispatcher.Invoke(
            DispatcherPriority.Normal,
            (Action)delegate()
            {
                // Populate the temporary collection with files
                foreach (var file in dirInfo.GetFiles())
                {
                    if (!String.IsNullOrEmpty(file.ToString()))
                    {
                        try
                        {
                            temp.Add(file.ToString());
                        }
                        catch (Exception listE)
                        {
                            // log exception
                        }
                    }
                }

                // Check to see if there are any new files
                foreach (var item in temp)
                {
                    if (!dir.Contains(item.ToString()) && !String.IsNullOrEmpty(item.ToString()))
                    {
                        try
                        {
                            dir.Add(item.ToString());
                        }
                        catch (Exception listE)
                        {
                            // log exception
                        }
                    }
                }

                // Check to see if any files have been moved/renamed
                for (int i = 0; i < dir.Count; i++)
                {
                    if (!temp.Contains(dir[i].ToString()) && !String.IsNullOrEmpty(dir[i].ToString()))
                    {
                        try
                        {
                            dir.RemoveAt(i);
                        }
                        catch (Exception listE)
                        {
                            // log exception
                        }
                    }
                }
            });
    }

它有效并且效果很好。没有错误,没有。我确信有一种方法可以让它更短,但那将会在你身上。我以为我会和遇到类似问题的人分享这个。

1 个答案:

答案 0 :(得分:1)

FileWatcher需要实现INotifyPropertyChanged界面并将fileList更改为引发该事件的属性:

    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<string> _fileList;
    public ObservableCollection<string> fileList
    {
        get
        {
            return _fileList;
        }
        set
        {
            _fileList= value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("fileList"));
        }
    }

通过提升事件,通知UI基础数据已更改并需要更新。