Repository: microsoft/PowerToys
Pull request #37626: [RegPreview] Init with header and add NEW button
Author: login: htcfreek
Labels: name: Product-Registry Preview
description: Refers to the Registry Preview PowerToy
name: Good to Merge
Created: 2025-02-25T12:26:16Z | Merged: 2025-04-21T08:11:07Z

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

This PR improves Registry preview to allow pasting registry keys and values without writing the file header manually.

This is done by adding the following changes:
1. Init the editor with only the registry file header if no file is opened.
2. Adding a new button that resets the app to the "init without file" state.

![image](https://github.com/user-attachments/assets/86cbf75f-e074-4f82-bf48-78cebc70c2cf)



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #36872
- [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
   - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries
   - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder
   - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects
   - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Run local build and validate the behavior.


Diff:
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 46432105d052..58691db7b16d 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -1005,6 +1005,7 @@ netsh
 newcolor
 NEWDIALOGSTYLE
 NEWFILE
+NEWFILEHEADER
 newitem
 newpath
 newplus
diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs
index 0df3a5c0f5ed..20009533c1d5 100644
--- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs
+++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs
@@ -192,12 +192,70 @@ private async void RefreshButton_Click(object sender, RoutedEventArgs e)
             // reload the current Registry file and update the toolbar accordingly.
             UpdateToolBarAndUI(await OpenRegistryFile(_appFileName), true, true);
 
+            // disable the Save button as it's a new file
             saveButton.IsEnabled = false;
 
             // restore the TextChanged handler
             MonacoEditor.TextChanged += MonacoEditor_TextChanged;
         }
 
+        /// <summary>
+        /// Resets the editor content
+        /// </summary>
+        private async void NewButton_Click(object sender, RoutedEventArgs e)
+        {
+            // Check to see if the current file has been saved
+            if (saveButton.IsEnabled)
+            {
+                ContentDialog contentDialog = new ContentDialog()
+                {
+                    Title = resourceLoader.GetString("YesNoCancelDialogTitle"),
+                    Content = resourceLoader.GetString("YesNoCancelDialogContent"),
+                    PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"),
+                    SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"),
+                    CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"),
+                    DefaultButton = ContentDialogButton.Primary,
+                };
+
+                // Use this code to associate the dialog to the appropriate AppWindow by setting
+                // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
+                if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
+                {
+                    contentDialog.XamlRoot = this.Content.XamlRoot;
+                }
+
+                ContentDialogResult contentDialogResult = await contentDialog.ShowAsync();
+                switch (contentDialogResult)
+                {
+                    case ContentDialogResult.Primary:
+                        // Save, then continue the file open
+                        SaveFile();
+                        break;
+                    case ContentDialogResult.Secondary:
+                        // Don't save and continue the file open!
+                        saveButton.IsEnabled = false;
+                        break;
+                    default:
+                        // Don't open the new file!
+                        return;
+                }
+            }
+
+            // mute the TextChanged handler to make for clean UI
+            MonacoEditor.TextChanged -= MonacoEditor_TextChanged;
+
+            // reset editor, file info and ui.
+            _appFileName = string.Empty;
+            ResetEditorAndFile();
+
+            // restore the TextChanged handler
+            MonacoEditor.TextChanged += MonacoEditor_TextChanged;
+
+            // disable buttons that do not make sense
+            saveButton.IsEnabled = false;
+            refreshButton.IsEnabled = false;
+        }
+
         /// <summary>
         /// Opens the Registry Editor; UAC is handled by the request to open
         /// </summary>
diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs
index 819fee949a84..3ec275abf844 100644
--- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs
+++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs
@@ -22,6 +22,8 @@ namespace RegistryPreviewUILib
 {
     public sealed partial class RegistryPreviewMainPage : Page
     {
+        private const string NEWFILEHEADER = "Windows Registry Editor Version 5.00\r\n\r\n";
+
         private static SemaphoreSlim _dialogSemaphore = new(1);
         private string lastKeyPath;
 
@@ -77,6 +79,9 @@ private async Task<bool> OpenRegistryFile(string filename)
             }
             catch
             {
+                // Set default value for empty opening
+                await MonacoEditor.SetTextAsync(NEWFILEHEADER);
+
                 // restore TextChanged handler to make for clean UI
                 MonacoEditor.TextChanged += MonacoEditor_TextChanged;
 
@@ -167,6 +172,25 @@ private void RefreshRegistryFile()
             ChangeCursor(gridPreview, false);
         }
 
+        private async void ResetEditorAndFile()
+        {
+            // Disable parts of the UI that can cause trouble when loading
+            ChangeCursor(gridPreview, true);
+
+            // clear the treeView and dataGrid no matter what
+            treeView.RootNodes.Clear();
+            ClearTable();
+
+            // update the current window's title with the current filename
+            _updateWindowTitleFunction(string.Empty);
+
+            // Set default value for empty opening
+            await MonacoEditor.SetTextAsync(NEWFILEHEADER);
+
+            // Reset the cursor but leave editor disabled as no content got loaded
+            ChangeCursor(gridPreview, false);
+        }
+
         /// <summary>
         /// Parses the text that is passed in, which should be the same text that's in editor
         /// </summary>
diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml
index a48222e9aa8f..18225902e33a 100644
--- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml
+++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml
@@ -55,6 +55,15 @@
                 HorizontalAlignment="Left"
                 DefaultLabelPosition="Right">
 
+                <AppBarButton
+                    x:Name="newButton"
+                    x:Uid="NewButton"
+                    Click="NewButton_Click"
+                    Icon="{ui:FontIcon Glyph=&#xe7c3;}">
+                    <AppBarButton.KeyboardAccelerators>
+                        <KeyboardAccelerator Key="N" Modifiers="Control" />
+                    </AppBarButton.KeyboardAccelerators>
+                </AppBarButton>
                 <AppBarButton
                     x:Name="openButton"
                     x:Uid="OpenButton"
diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw
index 73469a3b509b..93242072241d 100644
--- a/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw
+++ b/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw
@@ -297,4 +297,7 @@
     <value>Copy value with key path</value>
     <comment>Like "Copy item"</comment>
   </data>
+  <data name="NewButton.Label" xml:space="preserve">
+    <value>New</value>
+  </data>
 </root>
\ No newline at end of file


Reviews:
user: login: htcfreek
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-28T09:27:49Z
user: login: jamrobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T22:47:23Z
user: login: htcfreek
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-06T14:40:09Z
user: login: copilot-pull-request-reviewer[bot]
body: 

Copilot reviewed 2 out of 5 changed files in this pull request and generated no comments.

<details>
<summary>Files not reviewed (3)</summary>

* **.github/actions/spell-check/expect.txt**: Language not supported
* **src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml**: Language not supported
* **src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs:25**
* [nitpick] Consider renaming the constant NEWFILEHEADER to NewFileHeader to adhere to .NET naming guidelines.
```
private const string NEWFILEHEADER = "Windows Registry Editor Version 5.00\r\n\r\n";
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-06T15:18:54Z
user: login: zhaopy536
body: Looks good to me! Approved
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T07:42:16Z

Review comments:
diff_hunk: @@ -73,6 +73,15 @@
                         <KeyboardAccelerator Key="F5" />
                     </AppBarButton.KeyboardAccelerators>
                 </AppBarButton>
+                <AppBarButton
+                    x:Name="newButton"
+                    x:Uid="NewButton"
+                    Click="NewButton_Click"
+                    Icon="{ui:FontIcon Glyph=&#xe8a5;}">
path: src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml
user: login: htcfreek
body: ```suggestion
                    Icon="{ui:FontIcon Glyph=&#xe7c3;}">
```

page icon instead
created_at: 2025-02-28T09:27:42Z
author_association: COLLABORATOR
diff_hunk: @@ -167,6 +170,25 @@ private void RefreshRegistryFile()
             ChangeCursor(gridPreview, false);
         }
 
+        private async void ResetEditorAndFile()
+        {
+            // Disable parts of the UI that can cause trouble when loading
+            ChangeCursor(gridPreview, true);
+
+            // clear the treeView and dataGrid no matter what
+            treeView.RootNodes.Clear();
+            ClearTable();
+
+            // update the current window's title with the current filename
+            _updateWindowTitleFunction(string.Empty);
+
+            // Set default value for empty opening
+            await MonacoEditor.SetTextAsync("Windows Registry Editor Version 5.00\r\n\r\n");
path: src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs
user: login: jamrobot
body: would be great if move this to constant value.
created_at: 2025-04-04T22:47:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -167,6 +170,25 @@ private void RefreshRegistryFile()
             ChangeCursor(gridPreview, false);
         }
 
+        private async void ResetEditorAndFile()
+        {
+            // Disable parts of the UI that can cause trouble when loading
+            ChangeCursor(gridPreview, true);
+
+            // clear the treeView and dataGrid no matter what
+            treeView.RootNodes.Clear();
+            ClearTable();
+
+            // update the current window's title with the current filename
+            _updateWindowTitleFunction(string.Empty);
+
+            // Set default value for empty opening
+            await MonacoEditor.SetTextAsync("Windows Registry Editor Version 5.00\r\n\r\n");
path: src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs
user: login: htcfreek
body: nice catch
created_at: 2025-04-06T14:40:09Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-02-25T12:29:26Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/microsoft/PowerToys/pull/37626/files/) view, the [:scroll:action log](https://github.com/microsoft/PowerToys/actions/runs/13521178350/job/37780639223#step:4:1),  or [:memo: job summary](https://github.com/microsoft/PowerToys/actions/runs/13521178350/attempts/1#summary-37780639223) for details.

### Unrecognized words (1)

initilize

<details><summary>These words are not needed and should be removed
</summary>ahk AMPROPERTY AMPROPSETID Breadcrumb CDEF comdef ddf devenum DEVMON DEVSOURCE DGR DIIRFLAG dshow DVH DVHD DVSD DVSL EData ERole fdw FILEINFOSIG Filtergraph Filterx HCERTSTORE IKs iljxck IYUV KSPROPERTY lcb ldx lld LONGLONG LTRB majortype makecab MEDIASUBTYPE mediatype mfplat mic mjpg Msimg msiquery ORAW outpin overlaywindow PAUDIO PINDIR Pnp ppmt previouscamera PROPBAG propvarutil reencode reencoded REFGUID REGFILTER REGFILTERPINS REGPINTYPES regsvr shmem sizeread stl strsafe strutil subquery SYNCMFT TMPVAR vcdl vdi vid VIDCAP VIDEOINFOHEADER vih webcam wistd WVC 
</details><p></p>

<details><summary>To accept these unrecognized words as correct and remove the previously acknowledged and now absent words,
you could run the following commands</summary>

... in a clone of the [git@github.com:htcfreek/PowerToys.git](https://github.com/htcfreek/PowerToys.git) repository
on the `PT_RegPrevHeader` branch ([:information_source: how do I use this?](
https://docs.check-spelling.dev/Accepting-Suggestions)):

``` sh
curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.24/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/13521178350/attempts/1'

```
</details>

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](
https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: randyrants
created_at: 2025-03-10T01:19:09Z
body: Quick comment, going back to the days of Office unifying their menu/toolbars: does it make sense to have New after Reload or should it be first in the toolbar, as many apps have "New, Open, Save, Save As" under File and the toolbar for this app is very much a File menu.  Just horizontally. 
author_association: COLLABORATOR
user: login: htcfreek
created_at: 2025-03-10T07:07:21Z
body: Good question. My idea was that open is the more often use case. But we can switch.
author_association: COLLABORATOR
user: login: randyrants
created_at: 2025-03-10T19:49:19Z
body: Not a firm opinion - just an opinion.  Wondering what others think too.
author_association: COLLABORATOR
user: login: htcfreek
created_at: 2025-04-01T08:24:34Z
body: Can someone please review this that we get the other PR ready. 

cc: @zhaopy536
author_association: COLLABORATOR
user: login: zhaopy536
created_at: 2025-04-02T05:18:18Z
body: > Can someone please review this that we get the other PR ready.
> 
> cc: @zhaopy536

good job! reviewing
author_association: CONTRIBUTOR
user: login: htcfreek
created_at: 2025-04-02T07:03:39Z
body: > > Can someone please review this that we get the other PR ready.
> > 
> > cc: @zhaopy536
> 
> good job! reviewing

@zhaopy536 
Thank you. Can you approve and merge?
author_association: COLLABORATOR
user: login: zhaopy536
created_at: 2025-04-02T15:19:56Z
body: > > > Can someone please review this that we get the other PR ready.
> > > cc: @zhaopy536
> > 
> > 
> > good job! reviewing
> 
> @zhaopy536 Thank you. Can you approve and merge?

Hi, team is doing a 0.90 fix release these days, and cannot merge PR for the time being.

author_association: CONTRIBUTOR
user: login: jamrobot
created_at: 2025-04-04T22:50:42Z
body: > Good question. My idea was that open is the more often use case. But we can switch.

I vote for 'New, Open, Reload' order align w/ other apps. there is no copy/paste support before, so Open is the only option. 
author_association: CONTRIBUTOR
user: login: jamrobot
created_at: 2025-04-04T22:52:40Z
body: overall looks good to me, please let me know if comments make sense to you. thanks for adding 'New' function to RegPreview.
author_association: CONTRIBUTOR
user: login: htcfreek
created_at: 2025-04-06T14:53:15Z
body: @jamrobot
Requested changes are done. Please review.
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-04-06T14:55:57Z
body: 
# @check-spelling-bot Report
## :red_circle: Please review
### See the [:open_file_folder: files](https://github.com/microsoft/PowerToys/pull/37626/files/) view, the [:scroll:action log](https://github.com/microsoft/PowerToys/actions/runs/14293682212/job/40058134221#step:4:1),  or [:memo: job summary](https://github.com/microsoft/PowerToys/actions/runs/14293682212/attempts/1#summary-40058134221) for details.

### Unrecognized words (1)

NEWFILEHEADER

<details><summary>These words are not needed and should be removed
</summary>APPICONREFERENCE APPPUBLISHER cdn CREATETHREAD CREATEWINDOW DDEAPPLICATION DDECOMMAND DDEIFEXEC DDETOPIC DEFAULTTOFOLDER DEFAULTTOSTAR DELEGATEEXECUTE DROPTARGET flaticon fpvm gifv HSSH IGNOREBASECLASS INVALIDCALL INVALIDINDEX libraryincludes mdpvm NOFIXUPS NOOPEN NOREMAPCLSID NOTRUNCATE NOUSERSETTINGS QUICKTIP REGISTERCLASSEX REMAPRUNDLL searchtext SHELLEXTENSION SHELLNEWVALUE SHNAMEMAPPING sourced TILEINFO TMPVAR videourl WANTMAPPINGHANDLE xdoc youtube 
</details><p></p>

<details><summary>To accept these unrecognized words as correct and remove the previously acknowledged and now absent words,
you could run the following commands</summary>

... in a clone of the [git@github.com:htcfreek/PowerToys.git](https://github.com/htcfreek/PowerToys.git) repository
on the `PT_RegPrevHeader` branch ([:information_source: how do I use this?](
https://docs.check-spelling.dev/Accepting-Suggestions)):

``` sh
curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.24/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/14293682212/attempts/1'

```
</details>

<details><summary>Errors (2)</summary>

#### See the [:open_file_folder: files](https://github.com/microsoft/PowerToys/pull/37626/files/) view, the [:scroll:action log](https://github.com/microsoft/PowerToys/actions/runs/14293682212/job/40058134221#step:4:1),  or [:memo: job summary](https://github.com/microsoft/PowerToys/actions/runs/14293682212/attempts/1#summary-40058134221) for details.

[:x: Errors](https://docs.check-spelling.dev/Event-descriptions) | Count
-|-
[:x: ignored-expect-variant](https://docs.check-spelling.dev/Event-descriptions#ignored-expect-variant) | 3
[:warning: no-newline-at-eof](https://docs.check-spelling.dev/Event-descriptions#no-newline-at-eof) | 1

See [:x: Event descriptions](https://docs.check-spelling.dev/Event-descriptions) for more information.

</details>

<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details><summary>If the flagged items are :exploding_head: false positives</summary>

If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

  Please add a file path to the `excludes.txt` file matching the containing file.

  File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.

  `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).

* well-formed pattern.

  If you can write a [pattern](
https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
) that would match it,
  try adding it to the `patterns.txt` file.

  Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.

  Note that patterns can't match multiline strings.

</details>

author_association: NONE
user: login: htcfreek
created_at: 2025-04-10T19:27:56Z
body: ping @jamrobot for review 
author_association: COLLABORATOR
user: login: htcfreek
created_at: 2025-04-16T17:35:31Z
body: @crutkas
Can you please take a look? Seems that @jamrobot doesn't have the time to review.
author_association: COLLABORATOR
user: login: crutkas
created_at: 2025-04-18T13:35:39Z
body: @htcfreek if @jamrobot cant take a look before I do, I’ll do it on Monday. I tried but the day got away from me and I won’t be in a position where I can today. 
author_association: MEMBER
user: login: zhaopy536
created_at: 2025-04-21T06:04:56Z
body: /azp run
author_association: CONTRIBUTOR
user: login: azure-pipelines[bot]
created_at: 2025-04-21T06:05:05Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE