c# - FileWatcher typicall implementation? -
i building app, watching files. pretty fresh in programming (maybe) have little issue, because dont have real practice. program working, dont know how usally implemented in "real" software. please overview , comment. because have possible in datefiles class , less possible in mainviewmodel. yes app in build on mvvm base.
my current state:
in mainviewmodel
public static string configurationfilessourcepath2;
private void initializefiles() { // new instance of datefiles df = new datafiles(); // path datefiles df.configurationfilessourcepath = configurationfilessourcepath; // run initialization method establish "filewatching" df.initializefiles(); // refresh listview in view refreshfilelist(); // assign handler propertychanged event df.propertychanged += df_propertychanged; } // if change inside datefiles private void df_propertychanged(object sender, propertychangedeventargs e) { refreshfilelist(); } // refresh file list public void refreshfilelist() { fileslist = new observablecollection<files>(); foreach (var item in df.fileslist) { fileslist.add(item); } notifypropertychanged("fileslist"); }
datafiles class:
public class datafiles : viewmodelbase { public filesystemwatcher filewatcher; public string configurationfilessourcepath; public observablecollection<files> fileslist { get; set; } = new observablecollection<files>(); public void initializefiles() { // create new filesystemwatcher filewatcher = new filesystemwatcher(); // set filter catch xal files filewatcher.filter = "*.txt"; // set path filewatcher.path = configurationfilessourcepath; // subscribe created event filewatcher.created += new filesystemeventhandler(fileonchanged); filewatcher.changed += new filesystemeventhandler(fileonchanged); filewatcher.deleted += new filesystemeventhandler(fileonchanged); filewatcher.renamed += new renamedeventhandler(fileonrenamed); // enable filesystemwatcher events filewatcher.enableraisingevents = true; refreshfileslist(); } private void fileonchanged(object sender, filesystemeventargs e) { refreshfileslist(); } private void fileonrenamed(object sender, renamedeventargs e) { refreshfileslist(); } public void refreshfileslist() { fileslist.clear(); directoryinfo dir = new directoryinfo(configurationfilessourcepath); string[] extensions = new[] { ".txt" }; int nof = 0; foreach (fileinfo file in dir.getfiles().where(f => extensions.contains(f.extension.tolower())).toarray()) { nof++; fileslist.add(new files() { fileid = nof, filename = file.name, filechanged = file.lastwritetime.tostring(), filecreated = file.creationtime.tostring(), onlynamewithoutextension = path.getfilenamewithoutextension(file.name) }); notifypropertychanged("fileslist"); } }
there several issues code. among important ones - rebuild whole file list on every change (while filesystemeventargs arguments provide info has changed , where) , don't update observablecollection on ui thread. @ following code (but keep in mind sample spot problems):
public class datafiles : idisposable { public filesystemwatcher filewatcher; private readonly object filelistlock = new object(); // don't need public setter on public observablecollection<files> fileslist { get; } = new observablecollection<files>(); // pass path here, no need use property public void initializefiles(string path) { // dispose existing watcher, if disposewatcher(); // create new filesystemwatcher filewatcher = new filesystemwatcher(); // set filter catch xal files filewatcher.filter = "*.txt"; // set path filewatcher.path = path; // subscribe created event filewatcher.created += new filesystemeventhandler(fileonchanged); filewatcher.changed += new filesystemeventhandler(fileonchanged); filewatcher.deleted += new filesystemeventhandler(fileonchanged); filewatcher.renamed += new renamedeventhandler(fileonrenamed); // don't refreshfileslist on ui thread, might take time , block ui task.run(() => refreshfileslist()); // enable filesystemwatcher events filewatcher.enableraisingevents = true; } private void fileonchanged(object sender, filesystemeventargs e) { // lock here avoid race conditions refreshfileslist lock (filelistlock) { // better use dictionary avoid looping on files // looping still better rebuilding whole list var file = fileslist.firstordefault(c => string.equals(c.fullpath, e.fullpath, stringcomparison.ordinalignorecase)); if (file != null) { if (e.changetype == watcherchangetypes.deleted) ; // delete else ; // update file properties } else { // add new, unless event delete } } } private void fileonrenamed(object sender, renamedeventargs e) { lock (filelistlock) { // better use dictionary avoid looping on files var file = fileslist.firstordefault(c => string.equals(c.fullpath, e.oldfullpath, stringcomparison.ordinalignorecase)); if (file != null) { file.fullpath = e.fullpath; } else { // add new } } } public void refreshfileslist() { // need lock here, because there race condition between method , fileonrenamed \ fileonchanged, // , might lose updates or duplicates. lock (filelistlock) { // update observablecollection on ui thread onuithreaddo(() => { fileslist.clear(); }); directoryinfo dir = new directoryinfo(filewatcher.path); int nof = 0; var files = new list<files>(); // use enumeratefiles foreach (fileinfo file in dir.enumeratefiles("*.txt")) { nof++; int tmp = nof; // if working ui (that case if use observablecollection) - // need update collection ui thread if have bound controls files.add(new files() { fileid = tmp, fullpath = file.fullname, filechanged = file.lastwritetime, filecreated = file.creationtime, }); // don't // notifypropertychanged("fileslist"); } // publish them collection on ui thread onuithreaddo(() => { foreach (var file in files) fileslist.add(file); }); } } private void onuithreaddo(action a) { if (application.current.checkaccess()) a(); else application.current.dispatcher.begininvoke(a); } public void dispose() { disposewatcher(); } private void disposewatcher() { if (filewatcher != null) { filewatcher.enableraisingevents = false; filewatcher.created -= fileonchanged; filewatcher.deleted -= fileonchanged; filewatcher.changed -= fileonchanged; filewatcher.renamed -= fileonrenamed; filewatcher.dispose(); } } } public class files : inotifypropertychanged { // implement inotifypropertychanged, because need reflect property changes in ui public int fileid { get; set; } public string fullpath { get; set; } public string filename => path.getfilename(fullpath); public datetime filechanged { get; set; } public datetime filecreated { get; set; } public string onlynamewithoutextension => path.getfilenamewithoutextension(fullpath); }
Comments
Post a Comment