.NETpad 2025: Rewrite More File Operations (Premium)

.NETpad 2025: Rewrite More File Operations

Before I can finish my rewrite of .NETpad’s file operations class and fully implement Close, I had to write some other code first. The Close operation is difficult, and there are multiple checks to make whenever the user tries to close a tab or the entire app, thanks to the multiple documents and tabs the app now supports. And in working through this major task, I decided I needed a few new things:

  • A rewritten/updated SaveConfirmationDialog dialog class
  • A NeedsToBeSaved() method to test an individual tab/document to see whether it, well, needs to be saved

Naturally, I did this out of order. But that’s because I knew I could fall back on the system MessageBox before working on SaveConfirmationDialog. This WPF dialog, among other things, supports up to three buttons. I need three–for “Save,” “Don’t save,” and “Cancel”–and I wanted to rewrite my custom Confirmation dialog to work the same way. And more elegantly than was currently the case.

But first things first.

? NeedsToBeSaved()

My FileOperations class has a method called SaveOrSaveAs() that couldn’t be simpler: It determines whether to call Save() or SaveAs() based on the state of the current document and then returns a Boolean value indicating whether the operation completed successfully.

What it doesn’t do is determine whether the current document needs to be saved in the first place. That can be OK: Because it returns a value, I can (and do) use it in a conditional check, like so:

if (fo.SaveOrSaveAs(dts[TabIndex], TextBox1.Text))
{
UpdateTab(dts[TabIndex]);
}

But as I worked on improving the .NETpad 4.0 rewrite to include a confirmation dialog–the first implementation simply skipped that check so I could focus on the core tab/document management functionality–I realized I needed something more. And so I started working on a superset replacement that does everything. It’s called NeedsToBeSaved(). And if I do this correctly, I can just replace SaveOrSaveAs() with this completely.

Tied to this, I wanted to rewrite my the custom dialog code that I use for my current Save confirmation dialog. Thinking ahead for a change, I decided to implement NeedsToBeSaved() using the system MessageBox instead of rewriting my Save confirmation dialog too. Once this worked properly, I could move on to the new dialog code, which would emulate how MessageBox works in a three button configure (Yes/No/Cancel, though in this case, it will be Save/Don’t save/Cancel).

NeedsToBeSaved() is pretty simple too. I pass it the same parameters as SaveOrSaveAs()–a DocumentTab and text representing the current document–and it, too, returns a Boolean value indicating success. At a high level, it looks like so.

The trick, of course, is that switch statement.

Working backward through the choices…

Cancel. If the user cancels the operation, NeedsToBeSaved() returns a false value, indicating failure. (This way, the calling method can cancel whatever it’s doing. For example, if the user is going to close the app and then is prompted with this but chooses Cancel, the app remains open and nothing changes.)

No/Don’t save. If the user chooses “No,” nothing happens–the document is not saved–and the method returns a true value, indicating success. The calling method can continue with whatever it’s doing.

Yes/Save. The “Yes” option (which will be “Save” in my custom version of this dialog) is more problematic. This choice presents a matrix of choices and potential outcomes. If the document was previously saved, the Save() method is called and the most likely outcome there is it just works: It’s pretty straightforward to save a previously saved file. If the document is new and unsaved, then SaveAs() is called; this returns a Boolean value because the user could choose “Save” and then cancel the operation. If they do that, then the false value cascades back and the entire operation is canceled.

This bit of code is a good example of how I try to use AI to improve code quality. My original version was easy to read but inelegant, but the goal was just to get it to work. But I knew my sad embedded if-else clauses could be made more efficient. So I asked GitHub Copilot to tackle the first of two in there. It came up with the top (“if”) part below, making it easy for me to adapt the bottom half similarly.

But what about the outer if-else? GitHub Copilot took that code and turned it into this. Just a single line. I can barely understand the code, to be honest. I would never have written it. It’s pretty incredible, though.

And yeah, that’s how I spend my time these days. But … it seems to work.

I call NeedsToBeSaved() from the CloseTabEventHandler() method (in Tabs.cs, part of MainWindow) and in OpenDocument(), in the FileOperations class. And so this check will occur if the user tries to open or close a document. (I need to look at the New operation again just to be sure, but for now it’s not being used there.)

The next step? Replacing MessageBox with a new/updated Save confirmation dialog.

? Save confirmation dialog

I ended up sort of rewriting my existing custom dialog as a new window (with corresponding .xaml and .cs files) called SaveConfirmationDialog. I wrote that as “sort of” there because I copied and pasted the XAML code from my old ConfirmationDialog but then rethought how to implement the event handlers in SaveConfirmation.xaml.cs. And it didn’t take long. In fact, the solution presented itself so quickly I doubted it was right.

But it seems to work just fine, and I was able to replace the code that uses a system MessageBox with my new Save confirmation dialog easily.

Clearly, the work I’ve done with other classes in this version of the app helped. Because the first thing I added to my new SaveConfirmationDialog (in SaveConfirmation.xaml.cs) was a single property called Choice that’s of type string.

The constructor is the same as with its predecessor: It just grabs the name of the current document and displays it, but this time without the path (which was ugly, even though Notepad does it) or the file extension.

The Window_Loaded() event handler is likewise identical to before.

After that, I just created short event handlers for each of the three buttons (“Save,” “Save as,” and “Cancel”). Each just assigns the right string to the Choice property and then closes the dialog.

Then, I just replaced the system MessageBox code with code to create and use my Save confirmation dialog instead. Basically, there’s still a switch statement, but instead of see whether the result is Yes, No, or Cancel, it checks whether the result is “Save,” “Don’t save,” or “Cancel”

Again, this works nicely.

? But wait, there is one more thing

Well, it works nicely for one tab/document.

But what happens when the user just tries to close the app? They could click the “Close window” button. They could type Alt + F4. Or they could choose File > Exit from the app’s menu. However it happens, I have to deal with some number of open tabs/documents, some number of which have edited text. And some number of those were previously saved as files, while others were not. There’s a lot of checking to do.

I went through various permutation of this. And I can’t claim that what I came up with is ideal or bulletproof. But ultimately, I figured that I already had most of what I needed.

And that is:

  • A CloseTab() method in Tabs.cs that handles the closing of a single tab; if that tab is the last tab, then the app closes
  • A CloseTabEventHandler() method, also in Tabs.cs, that is called by CloseCommandBinding_Executed() in MainWindow.xaml.cs. It determines whether the underlying document needs to be saved, and it also closes the app if there are no more tabs left.

What I hadn’t written was code for the CloseButton_Click() event handler. This is what runs if the user clicks the “Close window” button. And I went through a lot of code changes there. But eventually, this felt simple. Maybe as simple as:

In other words, simple cycle through each tab and check whether each underlying document needs to be saved using CloseTabEventHandler(). If you reach the final tab and the user hasn’t canceled out somewhere, the app will close.

Could it be this simple?

Honestly, I’m probably missing some checks here and there. I could probably save/check document/tab state somewhere that I’m not. But yeah, for the most part, it does seem to work OK. I need to test this more, and I’m not 100 percent happy with any of it. But it feels like I’m getting close to “completing” the basics of file operations here.

April is hurtling to a close, and we’ll be heading home from Mexico soon. I really want to put this behind me before then. But I have more testing to do. So I will keep working on this to try and figure out what, if anything, I’ve missed. I mean, I have missed something. But we’ll see. It’s getting close.

Gain unlimited access to Premium articles.

With technology shaping our everyday lives, how could we not dig deeper?

Thurrott Premium delivers an honest and thorough perspective about the technologies we use and rely on everyday. Discover deeper content as a Premium member.

Tagged with

Share post

Thurrott