{"id":2893,"date":"2009-04-13T23:21:00","date_gmt":"2009-04-13T23:21:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2009\/04\/13\/keep-the-customer-satisfied-matt-gertz\/"},"modified":"2024-07-05T13:30:46","modified_gmt":"2024-07-05T20:30:46","slug":"keep-the-customer-satisfied-matt-gertz","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/keep-the-customer-satisfied-matt-gertz\/","title":{"rendered":"Keep the Customer Satisfied (Matt Gertz)"},"content":{"rendered":"<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">As I&rsquo;ve written elsewhere, the toughest critics for any work you do can always be found in your own family.<span>&nbsp; <\/span>Pleased at the immense work that I&rsquo;d done in scanning in and tagging all of my photos and media (as noted <\/font><a href=\"http:\/\/blogs.msdn.com\/vbteam\/archive\/2009\/01\/23\/an-updated-screensaver-example-matt-gertz.aspx\"><font color=\"#800080\" size=\"3\" face=\"Calibri\">in this post<\/font><\/a>)<font size=\"3\" face=\"Calibri\">, and with my ego sufficiently boosted by writing a screensaver in VB that would display not only the pictures but the tags associated with them, it was a deflating experience to have my middle child, Aidan, pepper me with comments like &ldquo;That&rsquo;s not me; that&rsquo;s Brandon&rdquo; or &ldquo;You didn&rsquo;t include Grandpa in the tags, but I can see his ear in this photo.&rdquo;<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In order to preserve the peace (and find the inevitable set of photos with bad tagging or poor rotation), I needed to open up the code to give the kids a way to let me know which photos needed to be re-examined.<span>&nbsp; <\/span>But how to do this?<span>&nbsp; <\/span>I&rsquo;ve currently got the photos set to display for 30 seconds, which is a limited amount of time for the kids to get over to my desk, pull out the keyboard tray, and somehow record a flaw without turning off the screensaver.<\/font><\/p>\n<h2><font color=\"#4f81bd\" size=\"4\" face=\"Cambria\">&ldquo;X&rdquo; marks the spot<\/font><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">So, I considered my customers.<span>&nbsp; <\/span>My kids would likely be coming over from the couch, which is to the left of my desk.<span>&nbsp; <\/span>Pulling out the keyboard tray could jiggle the mouse and end the screensaver, so I wanted a key that they could reach without moving anything.<span>&nbsp; <\/span>Furthermore, it needed to be a memorable key, and so I settled on &ldquo;X&rdquo; (as in &ldquo;X&rdquo; marks the spot), which is of course located on the left side of the keyboard.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Now, this sounds a lot like overthinking, doesn&rsquo;t it?<span>&nbsp; <\/span>But really, all design decisions need to take in account customer abilities and cognitive associations.<span>&nbsp; <\/span>I could just as easily have chosen &ldquo;F5&rdquo; as the keystroke, but besides creating an added potential for moving the mouse disruptively (an accessibility issue), the fact is that the key &ldquo;F5&rdquo; would have no association for my kids whatsoever.<span>&nbsp; <\/span>&ldquo;X,&rdquo; however, is an &ldquo;interesting&rdquo; letter and likely to stick in their minds.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">So, anyhow, here&rsquo;s how I went about it code-wise:<span>&nbsp; <\/span>I opened up the project that&rsquo;s presented in the <\/font><a href=\"http:\/\/blogs.msdn.com\/vbteam\/archive\/2009\/01\/23\/an-updated-screensaver-example-matt-gertz.aspx\"><font color=\"#800080\" size=\"3\" face=\"Calibri\">aforementioned post<\/font><\/a><font size=\"3\" face=\"Calibri\"> and navigated to the code for the frmScreenSaver object.<span>&nbsp; <\/span>Inside that code, I added an event handler for &ldquo;X&rdquo; thusly:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Sub<\/span> frmScreenSaver_KeyPress(<span>ByVal<\/span> sender <span>As<\/span> <span>Object<\/span>, _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>ByVal<\/span><span> e <span>As<\/span> System.Windows.Forms.KeyPressEventArgs) <span>Handles<\/span> <span>Me<\/span>.KeyPress<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">First thing to do is check to see if &ldquo;X&rdquo; was pressed:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> e.KeyChar = <span>&#8220;x&#8221;<\/span> <span>OrElse<\/span> e.KeyChar = <span>&#8220;X&#8221;<\/span> <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><font size=\"3\"><font face=\"Calibri\">OK, &ldquo;X&rdquo; was pressed.<span>&nbsp; <\/span>Now, do I actually have a file that I&rsquo;m currently showing?<span>&nbsp; <\/span>If my screensaver couldn&rsquo;t find any files, then I wouldn&rsquo;t have one.<span>&nbsp; <\/span>I can leverage the m_currentFile variable to check:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> <span>Not<\/span> <span>String<\/span>.IsNullOrEmpty(m_currentFile) <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">OK, I&rsquo;ve got a file.<span>&nbsp; <\/span>So, I&rsquo;m going to log this filename to a text file, appending it to whatever is already in that file (or creating it if none have been logged yet).<span>&nbsp; <\/span>(Every Saturday, I check this file for the accumulated complaints, examine the photos, make the required fixes, and delete the log file.)<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>My<\/span>.Computer.FileSystem.WriteAllText( _ <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>My<\/span><span>.Computer.FileSystem.SpecialDirectories.MyDocuments _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>&amp; <span>&#8220;FilesToFix.txt&#8221;<\/span>, m_currentFile &amp; vbCrLf, <span>True<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">Pretty simple!<span>&nbsp; <\/span>But I need to provide feedback to my kids that their keystroke was accepted, and so I add a simple &ldquo;beep:&rdquo;<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>My<\/span>.Computer.Audio.PlaySystemSound(Media.SystemSounds.Exclamation)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Sub<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">And that&rsquo;s it.<span>&nbsp; <\/span>I build the project in retail, rename it to be a *.scr file, and copy it up the the system directory.<span>&nbsp; <\/span><\/p>\n<p><\/font><\/font><\/span><\/p>\n<h2><span><font size=\"4\"><font color=\"#4f81bd\"><font face=\"Cambria\">&ldquo;Is that <i>really<\/i> necessary to look at, dear?&rdquo;<\/p>\n<p><\/font><\/font><\/font><\/span><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">The second complaint I received about the screensaver came not from my kids but from my wife, the crux of the problem being that the screensaver didn&rsquo;t discriminate between good pictures and bad pictures.<span>&nbsp; <\/span>This could be somewhat embarrassing when friends were visiting.<span>&nbsp; <\/span>For example, I&rsquo;ve got a set of photos from when I was in Dubai on a recruiting trip a few years back. <span>&nbsp;<\/span>The photos weren&rsquo;t just taken by me, but by my co-workers as well.<span>&nbsp; <\/span>Evidently, one of them was highly impressed with the bidet in their hotel bathroom and decided to commemorate it with a photo &#8212; not something that my wife was crazy about seeing on the screen.<span>&nbsp; <\/span>And, while I am often impressed by the artistic abilities of my kids, the fact of the matter is that their photos are often punctuated by close-ups of\n the dog&rsquo;s butt, their best friend&rsquo;s eyeball, or some incredibly blurry thing that can&rsquo;t be made out at all.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">So, to address this problem, I decided to add a &ldquo;nuke&rdquo; feature to the screensaver.<span>&nbsp; <\/span>When the &ldquo;N&rdquo; key (another easy-to-reach key with a good mnemonic &#8212; &#8220;N&#8221; stands for &#8220;nuke&#8221;) is pressed for a given photo in the collection, I want to eliminate it and move to the next picture.<span>&nbsp; <\/span>Sounds simple, no?<span>&nbsp; <\/span>It&rsquo;s just a modification of the &ldquo;X&rdquo; code from above.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">But here&rsquo;s the problem:<span>&nbsp; <\/span>my kids are wonderfully smart, but I don&rsquo;t trust them to be good judges of whether or not a photo should be <b>permanently<\/b> deleted (and, depending on the timing, they might use the key just as the photo changed, thus deleting the wrong photo).<span>&nbsp; <\/span>I therefore needed to construct a way to send the photo to a &ldquo;penalty box&rdquo; until I had the time to review the photo.<span>&nbsp; <\/span>Furthermore, this &ldquo;penalty box&rdquo; needed to be able to persist between sessions. <\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Here&rsquo;s how I set about it:<span>&nbsp; <\/span>first, in the same event I created above, I check for the &ldquo;N&rdquo; key and for the existence of the file:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>ElseIf<\/span> e.KeyChar = <span>&#8220;n&#8221;<\/span> <span>OrElse<\/span> e.KeyChar = <span>&#8220;N&#8221;<\/span> <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> <span>Not<\/span> <span>String<\/span>.IsNullOrEmpty(m_currentFile) <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">I log the picture just as I did before, although to a different file this time:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>My<\/span>.Computer.FileSystem.WriteAllText( _ <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>My<\/span><span>.Computer.FileSystem.SpecialDirectories.MyDocuments _ <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><\/span><span><span>&nbsp;<\/span>&amp; <span>&#8220;FilesToNuke.txt&#8221;<\/span>, m_currentFile &amp; vbCrLf, <span>True<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>My<\/span>.Computer.Audio.PlaySystemSound(Media.SystemSounds.Exclamation)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">I then remove the photo from the list of files by finding its index and calling &ldquo;RemoveAt()&rdquo;:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> index <span>As<\/span> <span>Integer<\/span> = m_files.IndexOf(m_currentFile)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> index &gt;= 0 <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m_files.RemoveAt(index)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m_fileCount -= 1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">And then I call &ldquo;DrawPicture(),&rdquo; which forces a move to the next picture without waiting for the timer:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>DrawPicture()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\" face=\"Calibri\">Unfortunately, if the screensaver is interrupted before I get a chance to review the &ldquo;photos to nuke&rdquo; on Saturday, it will re-create the file list with the bad photo(s) still in it the next time that it restarts.<span>&nbsp; <\/span>Therefore, I need to adjust how I create the file list in the frmScreenSaver_Load() event handler &ndash; it needs to read from the &ldquo;FilesToNuke.txt&rdquo; file (if it exists) and remove such photos from the photo collection that it generates.<span>&nbsp; <\/span><\/font><\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\" face=\"Calibri\"><span><\/span><\/font>&nbsp;<\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\" face=\"Calibri\"><span><\/span>Here&rsquo;s how I do that &ndash; first, I change m_file&rsquo;s type to be a normal collection rather than a read-only collection, so that I can alter it:<\/font><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> m_files <span>As<\/span> <span>New<\/span> Collection(<span>Of<\/span> <span>String<\/span>) <span>&#8216; List of files<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\" face=\"Calibri\">Then, in the Load event handler, I make a few minor modifications (in bold):<\/font><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> files <span>As<\/span> ReadOnlyCollection(<span>Of<\/span> <span>String<\/span>) <span>&#8216; List of files<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&nbsp;&nbsp;&nbsp;<\/span><span>If<\/span> m_Options.UseSubdirectories = <span>True<\/span> <span>Then<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>files = <span>My<\/span>.Computer.FileSystem.GetFiles(m_Options.Directory, _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>FileIO.SearchOption.SearchAllSubDirectories, <span>&#8220;*.jpg&#8221;<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Else<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>files = <span>My<\/span>.Computer.FileSystem.GetFiles(m_Options.Directory, _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>FileIO.SearchOption.SearchTopLevelOnly, <span>&#8220;*.jpg&#8221;<\/span>)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>If<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\"><font face=\"Calibri\">That code is the same as before, except that I read in the collection for the new variable &ldquo;files&rdquo; rather than m_files.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\"><font face=\"Calibri\">Now, we encounter a pet peeve of mine &ndash; the method GetFiles returns a read-only collection rather than just a normal collection.<span>&nbsp; <\/span>This means that we can&rsquo;t remove anything from the collection we get back from GetFiles(), and as noted above, I <b>need<\/b> to remove the questionable files from the collection until such time as I get around to reviewing and deleting the files permanently.<span>&nbsp; <\/span>(One day, I&rsquo;m going to write a blog which discussed the <b>correct<\/b> reasons for using read-only objects &ndash; I&#8217;m all for security, but it&#8217;s too easy go overboard with it sometimes, in my opinion.)<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNoSpacing\"><font size=\"3\" face=\"Calibri\">I have two choices &ndash; either write my own (thorough!) routine for enumerating files, or else bite the bullet and copy the read-only collection to a modifiable collection.<span>&nbsp; <\/span>(Of course, I could alter the Timer handler so that I check the random result against &ldquo;bad&rdquo; filenames <b>every time<\/b> the timer fires, but that would be taking on a perf hit on every event, not just the initialization.)<span>&nbsp; <\/span>Being inherently lazy, I&rsquo;ll just copy the collection (ooh, I feel so dirty!) into m_files, which is the version that I ultimately cache &amp; use internally:<\/font><\/p>\n<p class=\"MsoNoSpacing\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Copy to a writeable collection (why is the other collection read-only?)<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> <span>Each<\/span> f <span>As<\/span> <span>String<\/span> <span>In<\/span> files<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>m_files.Add(f)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><font size=\"3\"><font face=\"Calibri\">The worst (ethically) is over; I will now load in the set of questionable files from the &#8220;FilesToNuke.txt&#8221; file and remove them from the collection.<span>&nbsp; <\/span>Fortunately, the files being separated in the file by a carriage return, it&rsquo;s easy for me to read them in and separate them into a &ldquo;nuke&rdquo; list, which I can then iterate to remove the corresponding items from the main collection.<span>&nbsp; <\/span>(I could also have read this list <i>a priori<\/i> and prevented the items from being copied into the modifiable collection above, but that&rsquo;s actually less performant, as I would have far more misses than hits, even if I removed items from the &ldquo;nuke&rdquo; list after the corresponding entry was found.)<\/p>\n<p><\/font><\/font><\/span><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As I&rsquo;ve written elsewhere, the toughest critics for any work you do can always be found in your own family.&nbsp; Pleased at the immense work that I&rsquo;d done in scanning in and tagging all of my photos and media (as noted in this post), and with my ego sufficiently boosted by writing a screensaver in [&hellip;]<\/p>\n","protected":false},"author":258,"featured_media":8818,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[21,22,195],"tags":[101,165,166],"class_list":["post-2893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-im-a-vb","category-matt-gertz","category-visual-basic","tag-matt-gertz","tag-vb2005","tag-vb2008"],"acf":[],"blog_post_summary":"<p>As I&rsquo;ve written elsewhere, the toughest critics for any work you do can always be found in your own family.&nbsp; Pleased at the immense work that I&rsquo;d done in scanning in and tagging all of my photos and media (as noted in this post), and with my ego sufficiently boosted by writing a screensaver in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/2893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/users\/258"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/comments?post=2893"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/2893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media\/8818"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media?parent=2893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=2893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=2893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}