The UWP Notepad Project (Redux): More Settings (Premium)

In this installment of the UWP Notepad project, we’ll implement the non-font user settings and a workaround for a UWP weirdism that had me stumped for days.

FUBAR

UWP did it to me again. Despite the fact that I’d previously created four different versions of this app and thought I had figured out how to correctly read and save user settings, it wasn’t working correctly in this version. And after spending several hours over two days trying to figure out the problem, I finally figured out a workaround.

I can’t claim that actual tears were involved, but this incident is a great example of why UWP is so f&$%ing broken if you’re used to WPF, and why I spent so much of the past 48 hours alternating between rage and a funk state. I hate this. I hate the illogical nature of it.

The issue, in short, is that UWP quietly triggers certain event handlers unexpectedly at app launch, and that in doing so it short-circuited my attempts at accurately reading user settings and then configuring the app accordingly. Perhaps not coincidentally, this behavior seems to be tied to the weeks-long issues I had had figuring out the asynchronous programming techniques required for both file access and displaying pop-up windows in UWP. We’ll get to that in a future installment. For now, I’d like to just move past this horrific problem and make some progress.

And while I can’t explain this, and honestly couldn’t care less at this point why it even happens, the font (family) and font size settings we create, edit, and save in this app work just fine, but the other settings—which include the fold bold and font italic properties, plus Word Wrap, Status Bar, Display (Command Bar) Labels, and Auto Save—do/did not. So we need to make some changes to the code I provided in the previous installment. And then implement the workaround to what, to me, seems like bizarre behavior.

Ch-ch-changes

First, we’re going to change the MyFontBold and MyFontItalic user settings from string values to Boolean values. Before making that change, however, we should reset the app’s user settings, which can do by running the app and choosing “…” (See more) > Reset Settings. Good thing we added that.

Next, open MainPage.xaml.cs and locate the ReadSettings() method. Then, change the Font Bold and Font Italic if-then blocks to work with Boolean values like so:

// Font Bold
if (myFontBold == true)
    TextBox1.FontWeight = FontWeights.Bold;
else
    TextBox1.FontWeight = FontWeights.Normal;

// Font Italic
if (myFontItalic == true)
    TextBox1.FontStyle = FontStyle.Italic;
else
    TextBox1.FontStyle = FontStyle.Normal;

Note that the myFontBold and myFontItalic references will trigger errors. No worries: We’re going to add the appropriate variable declarations soon.

Next, find the SaveFontSettings() method. Here, we need to make similar changes so that this method saves FontBold and FontItalic as Boolean values, not strings. So replace the lines of code related to FontBold and FontItalic with the following:

if (TextBox1.FontWeight.Weight == 700)
    settings.Values["MyFontBold"] = true;
else
    settings.Values["MyFontBold"] = false;
if (TextBox1.FontStyle == FontStyle.Italic)
    settings.Values["MyFontItalic"] = true;
else
    settings.Values["MyFontItalic"] = false;

Next, locate SettingsPane_PaneOpening(). I apparently forgot to document how to configure the Settings pane’s BoldButton and ItalicButton last time, so let’s add that code now. This goes at the bottom of the event handler and under the two for loops that populate the FontsList and FontSizesList combo boxes:

// Select bold button if needed
if (TextBox1.FontWeight.Weight == 700)
    BoldButton.IsChecked = true;
else
    BoldButton.IsChecked = false;

// Select italic button if needed
if (TextBox1.FontStyle.ToString() == "Italic")
    ItalicButton.IsChecked = true;
else
    ItalicButton.IsChecked = false;

Now, it’s time to implement that workaround. The problem is that UWP is running various event handlers when the application starts—something I discovered laboriously by debugging—and that, in doing so, it is basically ignoring the settings we saved. My workaround involves short-circuiting that stupid behavior by creating global variables that read the MyFontBold and MyFontItalic settings before those event handlers all run. Then, we can configure the app correctly the global variables instead of reading the settings, which would have, by that point, changed.

In MainPage.xaml.cs, scroll up to the top and locate the MainPage class definition. Inside the opening curly braces, and above the MainPage constructor (public MainPage()), add the following lines of code:

// Global variables
// For font settings
readonly bool myFontBold = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyFontBold"]);
readonly bool myFontItalic = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyFontItalic"]);

That’s it. Now, if you run the application and change the Font Bold or Font Italic choice in the Settings pane, stop the application, and then restart it, the settings should carry forward as expected.

Seriously, I spent about 10 hours on this. Whew.

Other settings

With that finally and blessedly out of the way, we can move forward to what I originally intended to document days ago: Implementing the remaining settings that the user can configure here: Word Wrap, Status Bar, Display (Command Bar) Labels, and Auto Save. Each of these is enabled/disabled with a UWP control called the ToggleSwitch. And the event we need to handle for each is called Toggled.

So open MainPage.xaml and locate, in turn, WordWrapSwitch, StatusBarSwitch, LabelsSwitch, and AutoSaveSwitch (in the StackPanel that represents our Settings pane). Then, create a blank Toggled event handler for each. When that’s done, you’ll see each stacked up on top of each other in MainPage.xaml.cs. And not surprisingly, the code we need in each is quite similar.

Add the following code to WordWrapSwitch_Toggled:

if (WordWrapSwitch.IsOn == true)
    TextBox1.TextWrapping = TextWrapping.Wrap;
else
    TextBox1.TextWrapping = TextWrapping.NoWrap;
SaveOtherSettings();

Then, add the following code to StatusBarSwitch_Toggled:

if (StatusBarSwitch.IsOn == true)
    StatusBar.Visibility = Visibility.Visible;
else
    StatusBar.Visibility = Visibility.Collapsed;
SaveOtherSettings();

Then, add the following code to LabelsSwitch_Toggled:

if (LabelsSwitch.IsOn == true)
    MainCommandBar.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
else
    MainCommandBar.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
SaveOtherSettings();

Finally, add the following code to AutoSaveSwitch_Toggled:

if (AutoSaveSwitch.IsOn == true)
    AutoSaveButton.Content = "Auto Save: On";
else
    AutoSaveButton.Content = "Auto Save: Off";
SaveOtherSettings();

In case it’s not obvious, we’re deferring the actual implementation of Auto Save until later, but this will at least save the setting and change the text in the status bar accordingly until then.

Next, we need to make sure that the Settings pane actually sets each setting correctly when it opens. We already configured SettingsPane_PaneOpening for the font (FontFamily), font size, bold, and italic. So let’s add the following code to the end of that event handler to configure the other settings too.

// Toggle Word Wrap as needed
if (TextBox1.TextWrapping == TextWrapping.Wrap)
    WordWrapSwitch.IsOn = true;
else
    WordWrapSwitch.IsOn = false;

// Toggle Status Bar as needed
if (StatusBar.Visibility == Visibility.Visible)
    StatusBarSwitch.IsOn = true;
else
    StatusBarSwitch.IsOn = false;

// Toggle Label Position as needed
if (MainCommandBar.DefaultLabelPosition == CommandBarDefaultLabelPosition.Right)
    LabelsSwitch.IsOn = true;
else
    LabelsSwitch.IsOn = false;

// Toggle Auto Save as needed
if (AutoSaveButton.Content.ToString() == "Auto Save: On")
    AutoSaveSwitch.IsOn = true;
else
    AutoSaveSwitch.IsOn = false;

Next, we need to implement the same workaround for these settings as we did for Font Bold and Italic. So scroll up to the top of MainPage.xaml.cs and locate those global variables we added. Then, add the following code right below that:

// For other (non-font) settings
readonly bool myWordWrap = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyWordWrap"]);
readonly bool myStatusBar = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyStatusBar"]);
readonly bool myLabelPosition = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyLabelPosition"]);
readonly bool myAutoSave = Convert.ToBoolean(ApplicationData.Current.LocalSettings.Values["MyAutoSave"]);

Now, we have to make the corresponding changes in ReadSettings(). Find the line the currently reads:

if (Convert.ToBoolean(settings.Values["MyWordWrap"]) == true)

And change that to:

if (myWordWrap == true)

Then, make similar changes, in turn, to the other checks. The Status Bar if clause will change to:

if (myStatusBar == true)

The Label Position if clause will change to:

if (myLabelPosition == true)

And the Auto Save if clause (and the line above it in which we created a Boolean variable) will change to:

if (myAutoSave == true)

Finally, locate SaveOtherSettings(). This change isn’t strictly necessary, but just to get you all on the same page as my own app, delete everything inside there and replace it with this:

// Save non-font options to settings

ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;

if (WordWrapSwitch.IsOn)
    settings.Values["MyWordWrap"] = true;
else
    settings.Values["MyWordWrap"] = false;

if (StatusBarSwitch.IsOn)
    settings.Values["MyStatusBar"] = true;
else
    settings.Values["MyStatusBar"] = false;

if (MainCommandBar.DefaultLabelPosition == CommandBarDefaultLabelPosition.Right)
    settings.Values["MyLabelPosition"] = true;
else
    settings.Values["MyLabelPosition"] = false;

string autoSaveText = AutoSaveButton.Content.ToString();
if (autoSaveText == "Auto Save: On")
    settings.Values["MyAutoSave"] = true;
else
    settings.Values["MyAutoSave"] = false;

Fun, right?

Catch your breath and run the application. Be sure to test all the settings, but they should all be configured correctly when changed (except Auto Save, which only sets the status bar text for now) and should survive app restarts too.

Originally, I was going to move on to other topics from here, but I’m wasted from all the stress of figuring this bit out (again). I’ll provide another update—most likely related to file operations—soon.

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