{"id":5783,"date":"2007-07-10T16:13:00","date_gmt":"2007-07-10T16:13:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2007\/07\/10\/loading-and-saving-files-in-visual-basic-matt-gertz\/"},"modified":"2024-07-05T14:43:36","modified_gmt":"2024-07-05T21:43:36","slug":"loading-and-saving-files-in-visual-basic-matt-gertz","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/loading-and-saving-files-in-visual-basic-matt-gertz\/","title":{"rendered":"Loading and Saving Files in Visual Basic (Matt Gertz)"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">(This is part 3 of the Paint-by-Numbers series)<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">There are four issues I want to deal with in this post:<span>&nbsp; <\/span>New, Open, Save (As), and Exit.<span>&nbsp; <\/span>I&rsquo;ll be doing these in order, but all of them depend on knowing whether or not the application is dirty, so I&rsquo;ll deal with that problem first.<span>&nbsp; Opening and saving files isn&rsquo;t a particularly hard problem once you know how to deal with streams, but knowing when to notify users about potential data loss when opening files (or creating new ones, or closing an application) involves some logic, and that&#8217;s where understanding &#8220;dirtiness&#8221; comes into play.<span>&nbsp; <\/span><\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">First, however, I&rsquo;m going to deal with a problem that Bill McC brought up with my last post (see the comments attached to my last post for details).<span>&nbsp; <\/span>I was essentially abusing List Of() to be a stack, when Stack Of() already exists.<span>&nbsp; <\/span>This illustrates why it&rsquo;s really important to (a) have a solid plan that you either stick to or re-examine thoroughly if you decide to change course and (b) make sure that you get someone to review your code.<span>&nbsp; <\/span>None of the code that I write for these blogs gets reviewed by anyone else &#8212; it&rsquo;s just stuff that I find time to do in the cracks of my schedule &ndash; but of course for production code in Visual Studio, every line gets scrutinized deeply by a senior developer (or two, when we get closer to shipping) for just this reason.<span>&nbsp; <\/span>In my case, my original plan was to append the undo units to a list, which would be fine, but I changed my mind halfway through the coding of undo\/redo to inserting them at the front instead without careful consideration of the performance hit that would create.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Changing this to use stacks instead of lists was very trivial:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I replaced all &ldquo;List Of(&hellip;)&ldquo; with &ldquo;Stack Of(&hellip;)&rdquo;<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I replaced all &ldquo;Insert(0,&hellip;)&rdquo; with &ldquo;Push(&hellip;)&rdquo;<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I replaced all &ldquo;Remove(0,&hellip;)&rdquo; with &ldquo;Pop(&hellip;)&rdquo;<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I replaced all &ldquo;Item(0)&rdquo; with &ldquo;Peek()&rdquo;<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><font face=\"Calibri\" size=\"3\">Functionality-wise, that&rsquo;s all that needs to be done and then everything will work the way it did before, albeit in a more performant manner, but I also made the following changes:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I right-clicked on the UndoList and RedoList variables and used the &ldquo;rename&rdquo; command to change them to &ldquo;UndoStack&rdquo; and &ldquo;RedoStack.&rdquo;<span>&nbsp; <\/span>This is not a functional change, but I like my variable names to match my types.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font face=\"Calibri\" size=\"3\">&#8211;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">I eliminated an unnecessary temporary variable in the Undo and Redo commands by nesting the calls to the stack, so that now Undo is:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> CanUndo() <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>DoActions(UndoStack.Peek(), <span>True<\/span>) <span>&#8216; Do all the actions inverted (erase becomes draw and vice-versa)<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>RedoStack.Push(UndoStack.Pop()) <span>&#8216; Now it&#8217;s a redo <\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&#8216; (etc&hellip;)<\/p>\n<p><\/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\">and similarly for Redo:<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> CanRedo() <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>DoActions(RedoStack.Peek(), <span>False<\/span>) <span>&#8216; Do all the actions properly (erase is erase, draw is draw)<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>UndoStack.Push(RedoStack.Pop()) <span>&#8216; Now it&#8217;s an undo <\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&#8216; (etc&hellip;)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">(Keen eyes will note that I now do the Actions before moving the action item&hellip; that&rsquo;s not strictly necessary, but is more aesthetically pleasing to me.) <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Those changes took about three minutes total and result in a much more performant and elegant application. <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Now, on to the main topic for today &ndash; loading and saving.<\/font><\/p>\n<h2><font face=\"Cambria\" color=\"#4f81bd\" size=\"4\">The Dirty Bit<\/font><\/h2>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Most applications have something called a &ldquo;dirty bit&rdquo; which gets set when an action happens in the document, and if you try to close the application while it&rsquo;s dirty, you get prompted to save.<span>&nbsp; <\/span>There are expedient ways to implement this, and smart ways which take a little more work:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font face=\"Calibri\" size=\"3\">(1)<\/font><span>&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">Do nothing.<span>&nbsp; <\/span>That is, whenever the user saves, you always overwrite the file regardless of whether or not anything has changed, and you always prompt the user to save when closing the application regardless of the applications state.<span>&nbsp; <\/span>I don&rsquo;t know about you, but applications like this always annoy the heck out of me.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font face=\"Calibri\" size=\"3\">(2)<\/font><span>&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">Create a Boolean value which gets set whenever any action happens, and gets cleared once a save is committed &ndash; check this value to see whether or not a save is needed.<span>&nbsp; <\/span>This is better, and a lot of applications do this.<span>&nbsp; <\/span>However, if you support undo\/redo, then the simple act of doing an undo followed by a redo after a save will result in a &ldquo;dirty&rdquo; app, even though the document is identi\ncal to the copy on disk.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font face=\"Calibri\" size=\"3\">(3)<\/font><span>&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\" size=\"3\">Compare the top of the undo stack with a known &ldquo;last action,&rdquo; and use that as the &ldquo;dirty&rdquo; flag.<span>&nbsp; Thus, an undo followed by a redo gets you back to the same state, with no unnecessary prompt to save.&nbsp; <\/span>It involves slightly more logic, but it gives the truest indication as to whether or not anything has really changed.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">I&rsquo;m going to implement option (3).<span>&nbsp; <\/span>To do this, first I need to cache the last pen stroke before the save:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>Dim<\/span><span> LastSavedAction <span>As<\/span> Stack(<span>Of<\/span> GridAction) = <span>Nothing<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">and then check the value of that against the top of the Undo stack (or against Nothing if the UndoStack is empty):<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Function<\/span> IsDirty() <span>As<\/span> <span>Boolean<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> <span>Me<\/span>.UndoStack.Count = 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; <\/span><span>Return<\/span> LastSavedAction <span>IsNot<\/span> <span>Nothing<\/p>\n<p><\/span><\/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><span>Return<\/span> LastSavedAction <span>IsNot<\/span> <span>Me<\/span>.UndoStack.Peek()<\/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=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">If the document is not dirty, then the top of the undo stack will have to be whatever we cached it to be at the last save; otherwise, it&rsquo;s dirty.<span>&nbsp; <\/span>(When starting up, the Undo stack will be empty and the LastSavedAction will be Nothing, so that combination should indicate that we&rsquo;re not dirty as well.)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">We&rsquo;ll add a helper to disable the Save menu item:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Sub<\/span> UpdateSaveMenuItem()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Me<\/span>.SaveToolStripMenuItem.Enabled = IsDirty()<\/p>\n<p><\/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=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">which we can call anytime something interesting happens &ndash; at the end of the handlers for FormLoad, MouseUp, Undo, Redo, and of course New, Open, Save and Save As.<span>&nbsp;&nbsp; <\/span>I&rsquo;ll go ahead &amp; call the UpdteSaveMenuItem helper function from the first four of those &ndash; I&rsquo;ll do the others later when I write those routines.<\/font><\/p>\n<h2><font face=\"Cambria\" color=\"#4f81bd\" size=\"4\">New<\/font><\/h2>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Opening up the form editor, I&rsquo;ll drop-down the &ldquo;File&rdquo; menu and double-click on &ldquo;New&rdquo; &ndash; this will create the appropriate handler for me.<span>&nbsp; <\/span>Now, all New needs to really do is just reset everything to the way it was when the application was started, but first it needs to check to see if the user really wants to discard any changes since the last save.<span>&nbsp; <\/span>In my resource editor (right-click &ldquo;Project,&rdquo; choose &ldquo;Properties,&rdquo; and then navigate to the Resources tab), I&rsquo;ll add a string called MSG_SaveFirst which says &ldquo;Do you want to save first?&rdquo; and I&rsquo;ll add a helper called ContinueOperation to prompt the user with this string (Cancel will cancel the operation, Yes will save the puzzle before proceeding, and No will just proceed with the operation).<span>&nbsp; <\/span>Then, the methods look like:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Function<\/span> ContinueOperation() <span>As<\/span> <span>Boolean<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> IsDirty() <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>Dim<\/span> result <span>As<\/span> MsgBoxResult = MsgBox(<span>My<\/span>.Resources.MSG_SaveFirst, MsgBoxStyle.YesNoCancel)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> result = MsgBoxResult.Cancel <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; <\/span><span>Return<\/span> <span>False<\/span> <span>&#8216; User decided not to create a new document after all<\/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>ElseIf<\/span> result = MsgBoxResult.Yes <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; <\/span><span>If<\/span> <span>String<\/span>.IsNullOrEmpty(saveFileName) <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><span>Return<\/span> DoSaveAs()<\/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>Else<\/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><span>Return<\/span> DoSave(saveFileName)<\/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><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><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> <span>True<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Function<\/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><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Sub<\/span> NewToolStripMenuItem_Click(<span>ByVal<\/span> sender <span>As<\/span> System.Object, <span>ByVal<\/span> e <span>As<\/span> System.EventArgs) _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Handles<\/span><span> NewToolStripMenuItem.Click<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> <span>Not<\/span> ContinueOperation() <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>Return<\/span> <span>&#8216;User elected to Cancel<\/p>\n<p><\/span>\n<\/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=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Now, reset everything to its pristine state:<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> i <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> Rows &#8211; 1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> j <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> Columns &#8211; 1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&nbsp;&nbsp;&nbsp;<\/span>Grid(i, j) = 0<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/p>\n<p><\/span><\/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><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>UndoStack.Clear()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>RedoStack.Clear()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>(This is part 3 of the Paint-by-Numbers series) There are four issues I want to deal with in this post:&nbsp; New, Open, Save (As), and Exit.&nbsp; I&rsquo;ll be doing these in order, but all of them depend on knowing whether or not the application is dirty, so I&rsquo;ll deal with that problem first.&nbsp; Opening and [&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":[22,195],"tags":[101,165],"class_list":["post-5783","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-matt-gertz","category-visual-basic","tag-matt-gertz","tag-vb2005"],"acf":[],"blog_post_summary":"<p>(This is part 3 of the Paint-by-Numbers series) There are four issues I want to deal with in this post:&nbsp; New, Open, Save (As), and Exit.&nbsp; I&rsquo;ll be doing these in order, but all of them depend on knowing whether or not the application is dirty, so I&rsquo;ll deal with that problem first.&nbsp; Opening and [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/5783","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=5783"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/5783\/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=5783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=5783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=5783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}