.NETpad 2025: More Data Binding, GitHub, File Operations, More (Premium)

.NETpad 2025: More Data Binding, GitHub, File Operations, More

If only there were a term that meant doing the same thing over and over and expecting different results. Because that’s what it feels like I’m doing, solving the same problems again and again.

This isn’t what I wanted. But as my .NETpad modernization efforts hit a brick wall, I had to confront two inconvenient truths. My inability to successfully and consistently implement data binding is a serious problem in an app that supports multiple documents. And the Windows Presentation Foundation (WPF) is not up to the task regardless. These aren’t just setbacks, they’re work stoppages. Fixing each requires a lot of time that I don’t have and effort that is more frustrating than rewarding.

I have at least made progress in both areas.

In the previous article in this series, .NETpad 2025: Data Binding First Steps (Premium), I described how I finally got a basic bit of data binding to work and then went through the crucial process of actually duplicating that success in a new version of the app. Like so much in WPF (and the Windows App SDK/WinUI 3, for that matter), this simplest of implementations requires a mountain of code, in both XAML and C#, and this explosion of code was already an issue I was wrestling with. Now it’s worse.

In that article, I also described my first steps recreating a basic version of .NETpad in the Windows App SDK/WinUI 3 (which I’ll now just refer to as WinUI 3). WinUI 3 has many of the same problems as WPF, but it also has some key improvements that make it increasingly obvious that this is my only option if I’m serious about modernizing this app. There are obvious things like full support for modern controls that are core to this app (TabView and TabViewItem). And less obvious things like events that fire before a change is made, something I tried in vain to find in WPF and then workaround when I discovered this isn’t a feature of that framework, despite the obvious need. But whatever. WinUI 3 is the more modern framework, flaws and all.

When I wrote that previous article, I had reimplemented the FileName property in my Documents class as a dependency property with an OnPropertyChanged() event so that it could be used with data binding. And then I bound the Header property of the first (and, for now, only) tab (TabViewItem) to that property, using C# code. This meant that I could open a new document (and, later, save a new file or save an existing document with a new name) and have that tab header automatically update with the correct file name. I also wrote a value converter so that it would display correctly, meaning, in this case, without a file extension (.txt). This worked, still does, but it required a mountain of code and all it does is one little thing.

Here’s what I’ve done since then.

☁️ It’s on GitHub

I posted this new WinUI-based version of .NETpad, imaginatively named WinUIpad, to GitHub over the weekend and have updated it a few times since then. You can find the WinUIpad repository there now if you want to follow along with the progress (or lack thereof).

? More data binding

From a coding perspective, I took the next obvious steps with the Document class by converting its Contents, TextHasChanged, and DocumentIsSaved properties to dependency properties as I had done previously with FileName. And then I looked at how I might use them to automatically update other parts of the app, keeping the UI in sync with the document state. I was particularly keen to do as much data binding as possible in XAML, which can be cleaner to my eyes, but this has been hit or miss. Which is problematic, because data binding in WPF doesn’t throw an exception or give any sign that something is wrong when it doesn’t work. It just fails silently. (Which is amazing, given that a single errant character in XAML code is enough to bring a WPF app to its knees. But for some reason, data binding failures are silent and resilient.)

In previous versions of the app that included data binding functionality, I had implemented little colored icons to the right of the menu bar, over by the Settings (gear) icon, that would toggle on/off based on the state of TextHasChanged and DocumentIsSaved. I implemented both using InfoBadge controls in WinUI 3, and while this UI is not what I want for the final app, they work fine and are nice to have for quick, visual affirmation as I code and experiment.

I decided to switch that up and use the “status bar” at the bottom of the app window (really a Grid control with TextBlock controls inside) to display this status information textually instead. And so now it displays the value of TextHasChanged, DocumentIsSaved, and Filename (the latter with its full path when possible).

Each is implemented in XAML using data binding.

So that works. But the Big Kahuna here, so to speak, is the document itself, the text you see and edit in the text box, open from disk, and save to disk. For that, I bound the Contents property of the Documents class (which represents that document) to TextBox1, also in XAML, but this time in a two-way binding because changes made in the text box should reflect in the Contents property. The other data bindings are one-way because they just display the value of each property.

This, too, seems to work fine. And that’s kind of the core of my data binding needs, so I guess that’s a success of some kind. And this should work identically in WPF if/when I decide to implement data binding in that version of the app.

? File operations

The problem is that I have to test these bindings and other aspects of the app in different scenarios. And since this is a document-based app, one that will someday support multiple documents if I can ever get my act together, that means I need to implement core app features like New, Open, Save, and Save As. This is one of the few things that is straightforward in WPF. And it’s one of the things that’s proven surprisingly error-prone and problematic in WinUI 3, possibly tied in part to my app’s modularization. That is, I created a standalone File Operations class to handle this work.

I also needed a save confirmation dialog. If you’ve been following along with this self-torture, you may remember that WPF does not support the modern ContentDialog, so I had to create my own custom dialogs. (And that I later updated them to be more efficient.) WinUI 3 does support ContentDialog, of course, so that bit is easier, and will continue to be easier as I build out the rest of the app.

Anyway, I went through this in some logical order, building out a basic version of the File menu with New, Open, Save, and a few other commands. In the click event handlers for each–which will later be implemented as literal commands, as they are in the WPF version–I tried to keep the code as minimal as possible. Basically, if the user clicks New, Open, or Save (or types the associated keyboard shortcut), the app checks to see whether the current document needs to be saved. If it does, the user is prompted. And unless the user cancels the operation at any point, it then runs a helper method in the FileOperations class.

This is problematic. Because this is a separate class with unique visibility and accessibility permissions, I have to pass certain values into those methods and then return certain values to get anything done. (In my stupid brain, I always think about this as “crossing the blood/brain barrier” for some reason.) That’s true in any C# environment, but WinUI 3 throws in added complexity: File operations are done asynchronously, and that requires special attention. That’s not the case in WPF, one example of that framework being a bit easier to work with.

I figured this would be straightforward. But I kept running into issues that were difficult to debug, including an instance in which the app would simply hang after display a Save dialog. There’s a certain complexity to all this, and unless you’ve wrestled with asynchronous programming, it’s unlikely that you understand the cascading effect that this has one code. I struggle to describe this well, so I will just punt on this topic for now in the interest of brevity and see whether I can make sense of this later. But here’s the short version, using Save/SaveAs as the example:

  • I implement a click event handler for File > Save (Ctrl + S)
  • That click event handler creates a new instance of the FileOperations class, checks whether the document is saved already, and then runs SaveDocument() or SaveNewDocument, which are FileOperations member methods, as needed.
  • SaveDocument saves the previously-saved document to disk using the old System.IO.File object, which is not asynchronous, so it just works with nothing extra required. (After running into all kinds of issues with async, I tried to use the old System.IO.File object everywhere but couldn’t make it work well with SaveNewDocument, below.)
  • SaveNewDocument displays a FilePicker (Save As dialog), which requires a handle to the main window, which is an old-school Win32 thing and is thus ridiculous. Then it uses its PickSaveFileAsync() method to save the file to disk.
  • PickSaveFileAsync() is an asynchronous method, so it needs the await keyword in its return type.
  • Because PickSaveFileAsync() is asynchronous, the method that calls it–SaveNewDocument()–must be made asynchronous, too. This means I need the async keyword in its return type too.
  • SaveNewDocument() returns a bool and asynchronous methods cannot return a bool. So this has to change to Task.
  • Because the click event handler from MainWindow.xaml.cs calls SaveNewDocument() (in FileOperations.cs), it, too, must run asynchronously. So I have to add the async keyboard to its return type and use await with the SaveNewDocument() call.
  • This won’t be obvious, but using a different class also requires me to think through what parameters to pass to the FileOperations methods. For example, SaveDocument() needs that stupid handle (hwnd), so I have pass a references to the MainWindow. And it changes properties for the current document, so I have to pass it a Document as well. Fun stuff.

All these requirements keep cascading back to calling methods, forcing me to change them and, in some cases, other methods that call those calling methods too. It’s crazy.

It’s also really buggy. I had an issue where all file operations worked except for one: If I started with a new document and typed Ctrl + S (or chose File > Save) to save it, the Save As dialog would appear normally, I could save the file, and then the app would just hang. The file was always saved, I could open it, but it was always empty. I’ve been meaning to document how I debug in Visual Studio, but this one eluded me. I reimplemented it several times before I finally found a way that worked. Here, again, I may expand on this eventually. But I think it works now.

I also know that I wasted the better part of a week on this. It was very frustrating.

? One small user interface change

Back in early June, I wrote about a vague desire to make .NETpad look and feel more like Microsoft Edge, with its nice WinUI 3 fonts and iconography. As I’ve been working through the WinUI 3 version of the app, it occurred that I wasn’t using one of the most iconic parts of the Edge UI, though. And so I made minor changes to the TextBox style so that it has rounded corners and is inset by 10 dips (device independent pixels) on all sides. This makes it look like the main area of Edge, the part that displays a web document. I have to say, it’s subtle, but I really like it.

There’s so much more to do. So much more. But struggling to reimplement functionality I’d successfully finished in the past has been somewhat deflating. Hopefully, I can make some real progress in this coming week.

More soon. Hopefully.

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